4.3.4 LSP / IDE Integration

recommended

Starlark IDE integration is not one tool. It is a small stack: an editor extension, a Starlark language server, Buildifier, project-managed tool paths, and Bazel commands for the cases where only Bazel can answer. The point is to make .bzl and BUILD-file editing feel less blind while keeping Bazel as the authority for loading, analysis, and execution.

Which tool can answer your Starlark question?
The editor gives fast feedback. Bazel remains the authority for evaluated build behavior.
FORMAT OR LINT?
Keep source consistent
Buildifier Formats BUILD and .bzl files. It also reports style and simple lint issues.
fast · text-level answer
SYMBOL OR DIAGNOSTIC?
Navigate while editing
editor extension + Starpls Completion, definitions, references, hover text, and static diagnostics.
fast · language-level answer
WHAT DID BAZEL EVALUATE?
Prove graph behavior
bazel query / cquery / build Macro expansion, configured targets, providers, and build results belong to Bazel.
query --output=build cquery build
WHICH TOOL VERSION?
Let the repository decide
project-pinned tool paths The editor, local shell, and CI use the same Buildifier and language server.
repository contract, not a global install
Use editor feedback for fast local guidance. Use Bazel commands when the answer depends on loading or analysis.

From Formatting To Language Support

Buildifier is still the baseline. The official BUILD style guide says BUILD file formatting must match Buildifier output, and the .bzl style guide recommends Buildifier as both formatter and linter.1,2 That gives every editor a reliable first layer: format the file, catch simple style and lint problems, and avoid review churn over whitespace.

That is not the same as rich code intelligence. Historically, Starlark editor support was a gap: Buildifier handled formatting and trivial errors, while developers wanted completion, jump-to-definition across BUILD and .bzl files, and real-time error checking.3 That historical contrast is useful because it separates two jobs that often get conflated. Buildifier normalizes and lint-checks source text. A language server tries to make the editor understand symbols, references, and diagnostics while the developer is typing.

Today the Starlark-specific piece to know is Starpls, a language server for Starlark. It pairs with the Bazel Build VS Code extension, which helps VS Code find the Starpls server.4 That makes the practical model simple: the extension handles editor integration, the language server handles Starlark language features, and Bazel remains the build-system truth underneath.

Make The Setup Project-Owned

An IDE setup should not depend on every developer hand-installing the same helper binaries in the same global location. The stronger pattern is the one already introduced in 3.5 Developer Experience & Local Tooling: let the project expose the tools it expects developers and editors to run.

Dev containers plus bazel_env.bzl make this concrete: a VS Code, Cursor, or compatible editor can start a dev container, set up Bazel Env, install IDE extensions, and expose Starpls as part of that environment.4 The same idea applies to formatting: adding direnv plus bazel_env.bzl puts buildifier on PATH, then VS Code points at a stable generated path with bazel.buildifierExecutable and enables bazel.buildifierFixOnFormat for format-on-save.5

That is the difference between "my editor works" and "the repository has an editor contract." If the repository pins the tool and the editor setting points at that pinned tool, CI, local command lines, and format-on-save converge on the same binary.

{
  "bazel.buildifierExecutable": "./bazel-out/bazel_env-opt/bin/tools/bazel_env/bin/buildifier",
  "bazel.buildifierFixOnFormat": true
}

Treat that snippet as a shape, not a universal path. The exact generated location belongs to the repository's bazel_env setup.

What The IDE Should And Should Not Own

Use the editor for the fast, local loop: opening definitions, reading hover text, seeing syntax and lint feedback, and formatting on save. Use Bazel when the question depends on Bazel evaluation. If a macro produces targets you cannot see in the BUILD file, inspect the expansion with 4.3.2 Macro Expansion Inspection. If you need a quick value during loading or analysis, print() is available, but the official API reference marks it as debug output and warns that production use is noisy for users.6 If the Starlark control flow itself is hard to follow, the next item, 4.3.5 Starlark Debugger, covers the debugger path.

This split matters most for rule and macro authors. An LSP can improve navigation around .bzl code, but Bazel still decides what a package loads, what a macro expands into, what configured targets exist, and which providers configured targets expose to rule implementations. For architecture-level work in this level, keep the editor in the inner loop but keep bazel build, bazel query, cquery, and focused debugging commands in the proof loop.

That boundary becomes concrete when external labels stop resolving. Starpls documents that external repositories must already be fetched, and that its Bzlmod repository mappings are loaded lazily with bazel mod dump_repo_mapping and cached.7 Current Starpls clears that cache when the LSP client reports a saved MODULE.bazel, *.MODULE.bazel, or WORKSPACE-family file, although its README still recommends a restart after dependency changes.7 This is Starpls-specific behavior, not a general LSP guarantee: fetch missing dependencies and save the boundary file. If the editor's view still disagrees with the evaluated graph—for example, because the client did not deliver the save notification—restart the server, then verify the graph with Bazel itself.

Debugger And REPL Are Adjacent Tools

Do not expect LSP integration to carry every Starlark debugging task. The Starlark debugger is a different interface: a long-running Bazel server, a debug client, and optional IDE integration through DAP, with VS Code able to set breakpoints, step through code, and inspect variables via a debug adapter.8 4.3.5 Starlark Debugger shows that debugging workflow. Day-to-day language-server setup stays focused on navigation and static feedback.

The same boundary applies to REPL-style experiments. The VS Code Starlark debugger and a Starlark REPL are the tools for cases where macro code itself fails.9 A language server helps while editing. The debugger and REPL help when static editor feedback is not enough to explain runtime Starlark behavior. 4.3.6 Starlark REPL / Playground is the place to treat those interactive environments directly.

key takeaway

For Starlark development, set up the editor as a composition: Buildifier for formatting and linting, Starpls or editor-native Starlark support for navigation, and Bazel commands for truth about the loaded and analyzed graph.

The mature setup is project-pinned. Pin and expose the tools through the repository, point editor settings at those paths, and let individual developers choose their editor without changing the semantics of the Starlark tooling.

Check your understanding · 3 questions

1.Why should Bazel commands stay in the loop even when Starlark LSP support is configured?

Select one answer

2.Which practices match a project-owned Starlark IDE setup?

Select all that apply

3.Match each tool or layer to its role in the Starlark IDE stack.

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
Starpls
Buildifier
Bazel Build VS Code extension
bazel query --output=build
0 of 3 answered

Footnotes

  1. BUILD Style Guide — Buildifier as the required standard formatter for BUILD files.

  2. .bzl style guide — Buildifier as formatter and linter for Starlark.

  3. BazelCon 2019 Day 2: Half-Day Bazel Bootcamp (Part 2) — "Editor Support for Starlark" and Q&A on IDE support gaps.

  4. Review of State of the Art Solutions for IDE Support and Developer Tooling in Monorepos — dev containers, Bazel Env, Starpls, and the Bazel Build VS Code extension. 1 2

  5. Starlark linter: Buildifierdirenv / bazel_env.bzl setup and VS Code Buildifier settings.

  6. All Bazel filesprint() debug output behavior and production-use warning.

  7. Starpls — Starlark language server — the upstream known-issues section documents external-repository fetching and lazy Bzlmod mapping resolution, while did_save_text_document clears cached mappings after relevant boundary files are saved. The current README is stale on the restart requirement and misspells Bazel's singular dump_repo_mapping command as dump_repo_mappings 1 2

  8. Starlark Debugger: Deep dive — DAP, debug clients, and VS Code debugger workflow.

  9. Sponsored Session: Enough Bazel to Be Dangerous: A Debugging Cookbook - Instructor: Alejandro Gomez — Starlark debugger and REPL as macro-debugging tools.