> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Starlark linter: Buildifier

> How to set up the Bazel Buildifier tool by Aspect Build

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Starlark linter: Buildifier" date="2025-10-14" authors="Alex Eagle" tags="Linting, Gazelle, Bazel">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-3c12d34767.png?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=765f17cfe8de931b65b9b8d3ae38a684" alt="" className="blog-post-cover" width="1920" height="1073" data-path="images/blog/hashnode/hn-3c12d34767.png" />

  Bazel uses its own configuration language called Starlark: [https://starlark-lang.org](https://starlark-lang.org). It’s a Python dialect that allows parallel evaluation to make builds faster.

  [Linting](https://aspect.build/docs/cli/tasks/lint) 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](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.

  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-8192a4765a.png?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=14e2a7bf2a9252b1d67f0ed0a1d7005b" alt="" className="block mx-auto" width="661" height="333" data-path="images/blog/hashnode/hn-8192a4765a.png" />

  ## Setting it up

  There are several ways to install [Buildifier](https://aspect.build/docs/cli/tasks/buildifier) for developers. After working with over 50 companies on their Bazel config, we encoded our learnings into our Starter repos: [github.com/aspect-starters](https://github.com/aspect-starters). To save you a bunch of browsing, here’s a summary of what Aspect recommends:

  1. 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](https://github.com/bazelbuild/buildtools/tree/main/buildifier#setup-and-usage-via-bazel) look HORRIBLE. We like [https://github.com/keith/buildifier-prebuilt](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](https://github.com/bazelbuild/buildtools/releases) as well.

  2. Developers will want to run `buildifier` from their PATH. We recommend [https://direnv.net](https://direnv.net) to hook the shell to update PATH as you `cd` into the workspace folder, then [https://github.com/buildbuddy-io/bazel\_env.bzl](https://github.com/buildbuddy-io/bazel_env.bzl) to add tools to the PATH. [Here’s the spot in the example](https://github.com/buildbuddy-io/bazel_env.bzl/blob/85e57cfd869cbbcc412c79db050191d88eef554a/examples/BUILD.bazel#L38) that sets up Buildifier.

  3. 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](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](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](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](https://github.com/aspect-build/rules_jasmine/blob/fdfda3c75ff986c82dbb486d9ed93ee9caa9ff6f/.github/workflows/buildifier.yaml). 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](https://qntm.org/ratchet)”.

  ## 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](https://aspect.build/platform).

  Or [talk with us](https://calendly.com/aspect-build/intro) to learn more.
</BlogPost>
