Starlark linter: Buildifier
Formatting and linting Bazel and Aspect configuration files

Bazel uses its own configuration language called Starlark: https://starlark-lang.org. It’s a Python dialect that allows parallel evaluation to make builds faster.
Linting is the process of using a static code analysis tool, known as a "linter," to identify and flag potential programming errors, bugs, stylistic issues, and suspicious constructs in source code. It essentially examines code without executing it.
Of course every language needs linting and formatting. Starlark has one too! It was originally created because the Go team at Google wanted to machine-edit BUILD files, but didn’t want to get into code reviews with teams who liked their hand-formatting of files. Read https://laurent.le-brun.eu/blog/the-story-of-reformatting-100k-files-at-google-in-2011 for more on this back-story.
Buildifier is a tool for formatting Bazel BUILD and .bzl files with a standard convention. Buildifier works on the Aspect Extension Language too! Here’s how that looks in VSCode.

Setting it up
There are several ways to install Buildifier for developers. After working with over 50 companies on their Bazel config, we encoded our learnings into our Starter repos: https://github.com/bazel-starters. To save you a bunch of browsing, here’s a summary of what Aspect recommends:
Buildifier is written in Go, but most engineers don’t want to wait to compile it when it’s a cache miss. This is why the “official” instructions look HORRIBLE. We like https://github.com/keith/buildifier-prebuilt as an easy way to get pre-built binaries, along with a build rule to run it. Note that you could fetch binaries directly from the project releases as well.
Developers will want to run
buildifierfrom their PATH. We recommend https://direnv.net to hook the shell to update PATH as youcdinto the workspace folder, then https://github.com/buildbuddy-io/bazel_env.bzl to add tools to the PATH. Here’s the spot in the example that sets up Buildifier.bazel_env.bzl also provides a stable path for the tool that editors can reference. For example in VSCode, add this to your
.vscode/settings.json:
"bazel.buildifierExecutable": "./bazel-out/bazel_env-opt/bin/tools/bazel_env/bin/buildifier",
you probably also want to enable it on save:
"bazel.buildifierFixOnFormat": true,
Buildifier formatting
We want to make developers productive! What’s not productive? Discussion of whitespace, or waiting to re-run your CI job because of a formatter nit. We can make these basically disappear.
First, setup Aspect rules_lint, following https://github.com/aspect-build/rules_lint/blob/main/docs/formatting.md. You don’t need this for buildifier itself, but assuming you also want to run other formatters for other languages, it gives you a single setup that formats all files in the repo.
We setup the editor earlier to run buildifier on save, but engineers have a lot of editor choices. As a fallback, add a pre-commit hook so any unformatted files get fixed at git commit time. A couple options for this are documented in formatting.md.
Finally we do want to enforce that files in the repo are formatted. This isn’t because we hate reading code with different whitespace - it’s just to avoid the next engineer who touches the file ending up with spurious deltas in their pull request. You can run the format.check target from rules_lint on your CI. Give developers a nice error message when it fails, guiding them to setup their dev environment so they don’t need to hit a red CI job in the future.
When you first format the repo, it’s good practice to list your commit hash in the .git-blame-ignore-revs - see https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-filefile. This way you don’t pollute the blame layer.
Buildifier linting
Buildifier has about 100 checks that catch certain coding issues in Starlark files, though many of them are specific to Bazel’s standard library. The list is here: https://github.com/bazelbuild/buildtools/blob/main/WARNINGS.md
Using rules_lint only runs linters over the dependency graph, and you probably didn’t want to have to add your BUILD files to a filegroup in the BUILD file. Too self-referential! So we recommend running buildifier linting as a standalone step, such as this GitHub Actions workflow. Note that this is not incremental - it runs the linter across all the code in the repository, every time it’s run.
When you first setup the linter, it will point out a big pile of issues, and this may result in a massive PR that is hard to rebase and disruptive to engineers when it lands. We recommend enabling a single check at a time, and slowly rinse-and-repeat following the “Ratchet principle”.
It’s easier on our platform!
The Aspect Workflows developer productivity platform includes buildifier as a first-class task type. This lets you skip some of the setup steps above, and get our recommendations running automatically on your continuous integration (CI) system. Check it out at https://aspect.build/platform.
Or talk with us to learn more.





