3.5.1 IDE Support

A Bazel build is optimized for reproducibility — the same graph, the same flags, the same outputs. An IDE wants the opposite kind of loop: live feedback on every keystroke, even when the code is half-broken, without waiting for Bazel to invalidate and rebuild several layers of dependencies.1 Bridging those two worlds is what IDE integration for Bazel is about, and community surveys and tooling talks consistently call it out as one of Bazel's top pain points.1

The boundary is between a hermetic build environment and a responsive editor environment. Keep their inputs aligned where practical: pin the language server, formatter, compiler-facing metadata, and configuration so developers and CI agree on versions and semantics. 3.5.3 Developer Tool Management shows how to put those Bazel-managed tools at stable paths, while 4.6 Toolchains & Platform Resolution explains how a build selects tools for execution and target platforms. But sharing tools does not turn an editor session into a Bazel action. The editor must tolerate unsaved and temporarily invalid code, retain an in-memory snapshot, and often load only part of the repository. Its diagnostics are fast evidence about that partial model. The actual Bazel build or test remains authoritative for its requested configured target graph and execution environment.

There are two established paths, and they diverge by editor ecosystem. JetBrains IDEs run a dedicated Bazel plugin that builds a full project model. VS Code and other LSP-based editors compose a Starlark extension for BUILD files with per-language language servers that talk to Bazel through build adapters or the Build Server Protocol (BSP). Both paths lean on Bazel aspects underneath to extract project metadata. 4.8 Aspects explains that mechanism in depth, while this article stays with the maintainer decision: which integration to pick, what it gives you, and where the sharp edges are.

Two IDE routes, one Bazel metadata source
Editors keep a live model. They read Bazel metadata during sync, not after every edit.
JETBRAINS PLUGIN ROUTE
One plugin owns the integration
JetBrains IDE
IntelliJ IDEA, GoLand, PyCharm
JetBrains Bazel plugin
Imports the project view and maps targets to IDE modules
IDE project model
Sources, modules, classpaths, generated files
Live analysis stays in the IDE's AST and type model.
LSP EDITOR ROUTE
Language tools compose the integration
LSP editor
VS Code, Cursor, Eclipse
Language server
Understands code and keeps an in-memory snapshot
Build adapter or BSP bridge
Translates language requests into Bazel metadata
gopls + gopackagesdriver Java LSP + Bazel Java SDK SourceKit LSP + BSP clangd + compile_commands.json
Live analysis stays in the language server's snapshot.
On sync or model refresh — not on each keystroke
SHARED BAZEL MODEL EXTRACTION
Read the project model from Bazel
Both routes request Bazel metadata. Neither route needs a build after every edit.
Query
selects targets and discovers rulesets
Aspects
read sources, deps, and provider fields
BAZEL BUILD GRAPH
Targets and providers are the source of truth
//app:server → sources, deps, JavaInfo / CcInfo / GoArchive

Bootstrap one supported path

For JetBrains, install the official Bazel plugin, create or check in the project's .bazelproject view, import that view, and run the first sync.2 A checked-in project view is the team contract: it records which directories or targets are imported and which Bazel flags sync uses.

For VS Code, install the Bazel Build extension, open the workspace, and configure its language-server command when the executable is not already on PATH (the setting is bazel.lsp.command).3 Then add the language-specific adapter your source language requires. A Starlark extension alone improves BUILD and .bzl editing. It does not manufacture Java, Go, C++, or Swift classpaths.

The JetBrains Plugin Path

For a long time, JetBrains IDEs relied on a plugin maintained by Google under bazelbuild/intellij, covering IntelliJ IDEA, CLion, and Android Studio. That plugin took contributions, but priorities tracked Google's internal needs, so most large customers ended up on private forks — a pattern that neither scaled nor produced a stable shared platform.1

JetBrains has released its own official Bazel plugin as the successor to the Google-maintained plugin for the main JetBrains IDE path.1 At the time of the cited tooling discussion, the supported language set centered on JVM (Java, Kotlin, Scala), Go, and Python, while C++/CLion, JS/TS, and Rust support were still separate or less mature paths.1 Treat that product matrix as ecosystem state, not the core lesson: the stable maintainer choice is whether your team should use the dedicated JetBrains project-model plugin or compose a VS Code/LSP stack. The core of the plugin is open source and mirrored from the IntelliJ monorepo into the public Hirschgarten GitHub repo, which is also where external contributions land.1

Sync runs aspects over the workspace to collect per-target metadata — source lists, dependencies, classpaths, module info — and uses dynamic rule detection (via bazel query, and bazel mod graph for Bzlmod projects) to generate aspect-loading code only for the rulesets your workspace actually has.4 That is what keeps sync from collapsing on an unfamiliar workspace that happens to use, say, rules_kotlin but not rules_scala. The aspect mechanism itself and what providers carry are 4.8 Aspects.

The VS Code and BSP Path

VS Code's story is fragmented by design — each language has its own extension and language server, and Bazel integration means wiring each of them to read the Bazel graph.

For Starlark files themselves (BUILD, .bzl, MODULE.bazel), the Bazel Build VS Code extension pairs with Starpls, a Starlark language server, to give you go-to-definition and completion inside BUILD files.5 This is the piece of VS Code integration that is purely about editing Bazel's own configuration — the deeper Starlark dev-tool story is 4.3.4 LSP / IDE Integration.

For code in your project, VS Code uses two broad strategies:

  • Build Server Protocol (BSP). BSP originated in the Scala ecosystem as a build-tool-agnostic way for any editor to ask any build tool to run an incremental build and push diagnostics back via LSP.1 bazel-bsp is one implementation. sourcekit-bazel-bsp is an active effort to wire Swift's SourceKit LSP to Bazel the same way.6 The JetBrains Bazel plugin itself started as a BSP prototype, then dropped the abstraction once it became clear that supporting Bazel deeply was more valuable than maintaining a portable layer.1 BSP is still the natural fit when the editor already expects it — notably Scala via Metals, and increasingly Swift.
  • Per-language extensions with a build adapter. The most mature example is Salesforce's Bazel Java SDK, which powers both Bazel Eclipse and a Bazel VS Code Java extension on top of Red Hat's Java language server.7 It supports partial project imports — essential in a large monorepo where no developer imports every package — and computes per-package classpaths by combining bazel query with aspects.7 On the Go side, gopackagesdriver from rules_go plugs into the standard gopls LSP server as a build adapter, translating gopls's package queries into bazel query + aspect-driven metadata extraction.8 Teams sometimes skip BSP entirely and just ship a settings.json that points gopls at a Bazel-aware GOPACKAGESDRIVER binary.9

Whichever path the language takes, the shape is the same: a language-aware driver sits between the editor's LSP server and Bazel, and aspects are how that driver reads the graph.

Repository scale makes the project-model boundary operational rather than theoretical. A small workspace may import nearly every target. A large monorepo needs project views, target- or folder-scoped compilation databases, or partial package imports so indexing remains responsive.5,7 That scope is an editor choice, not a claim that the rest of the graph is irrelevant. CI must still select and validate affected targets. 6.5.1 Affected-Target Service Contract explains how to determine that scope. Likewise, project views decide what a developer loads, while target 0.2.4 Visibility and the later API-boundary policy in H.5.2 Dependency Boundaries decide which dependencies are permitted.

Why the IDE Still Re-Implements So Much

A reasonable question is why an IDE cannot just call bazel build on every save and surface the errors. The answer is latency. One edit can invalidate three levels of dependencies. The incremental rebuild can run for minutes before Bazel comes back with diagnostics.1 IntelliJ IDEA, which predates LSP by roughly fifteen years, handles this by re-implementing the front half of each language's compiler: it builds its own AST, runs its own type checker, and only asks Bazel for the project model — source roots, classpaths, dependency edges.1 Rolling out Java 21 or Scala 3 support is therefore a compiler-sized project, not a plugin update, and the same cost reappears for every language the IDE promises to analyze live.

The VS Code side hides some of this work inside the LSP servers (gopls, Red Hat Java LSP, rust-analyzer, etc.), but the core tension is identical: the language server maintains its own in-memory snapshot of the workspace and recomputes diagnostics on each change — the gopls snapshot is explicitly described as an in-memory analogue of Bazel's action cache.8 Bazel supplies the project model and the artifacts. The IDE keeps the editing loop responsive.

The friction shows up in concrete places. IntelliJ's Java project model requires sources to be attached to directories, and each directory can belong to exactly one IntelliJ module — but Bazel returns per-target source lists, which may cover only some files in a directory or spread one directory across several targets.1 The JetBrains plugin solved this only recently. It still recommends directory-level globs in BUILD files for best performance.1 Project-model mismatches like this one, not the build itself, are the dominant reason IDE syncs are slow on large Bazel repos.

Per-Language Bottlenecks

Current IDE repositories make the generic-versus-language split observable. The official Hirschgarten map routes JetBrains users through its feature matrix and phased sync guide: query builds a quick source model first, then an aspect supplies full dependencies and generated sources. The VS Code Bazel map shows a composition model instead: query-backed target navigation is built in, while bazel-lsp or starpls is configured as an external server. Neither plugin removes the need for a language-specific project model.

Most of the remaining friction is language-specific, and the fix lives in that language's ruleset and driver — not in a generic IDE feature. These are the ones worth knowing about at the maintainer level:

  • C++ — compile_commands.json. Clangd and most C++ IDEs want a JSON compilation database: every translation unit with its exact compiler flags.10 Bazel has no built-in support. The community has converged on a handful of approaches — build interception with bear, bazel aquery, or aspects — each with different trade-offs around tree artifacts and accuracy.10 The aquery-based hedronvision/bazel-compile-commands-extractor is the de facto standard for projects without tree artifacts.10 Its public refresh_compile_commands macro exposes target/flag selection and large-workspace exclusions, while the repository map records generated-file and tree-artifact failure routes.11 Upstream uses a custom source-available license rather than an OSI/FSF open-source license, so teams should review redistribution terms before standardizing on it. Depth is in L2.5.1 Compilation Database.
  • Go — gopackagesdriver. Ships with rules_go and is the bridge between gopls and Bazel. An aspect reads each target's GoArchive provider and emits per-target JSON metadata that gopls consumes through its standard go/packages loader API.8 Depth is in L5.6 IDE Integration (gopackagesdriver).
  • Swift / iOS — SourceKit LSP plus rules_xcodeproj. Xcode can be driven by generating a Bazel-backed .xcodeproj via rules_xcodeproj, or replaced altogether with VS Code + the SourceKit LSP server wired to Bazel through BSP (sourcekit-bazel-bsp).6 Mobile-specific setup is T2.1 iOS with Bazel.
  • Imported Xcode indexes are a separate layer. When compilation runs under Bazel but navigation remains in Xcode, index-import can rewrite checkout, Swift module, and object-file prefixes while copying Clang/Swift index-store data into Xcode. Its README.md example is an illustrative Run Script fragment rather than a complete drop-in integration: derive the actual Bazel index-store inputs and Xcode destination from your build, then use tests/swiftc/ or tests/clang/ to isolate language-specific remapping failures.12
  • JVM — IDE/Bazel drift on generated sources. Mixed-source and annotation-processor setups can pass IntelliJ's import resolution but fail Bazel, or vice versa. See L1.5 Mixed Java/Kotlin Source Sets for concrete patterns.
  • Python — venv-for-IDE. Most Python extensions are still written for a classic virtualenv, and the practical workaround is to have Bazel generate one from the resolved lockfile just for the IDE while keeping builds hermetic. Covered in L3.6 Virtual Environments & Bazel.

An IDE bottleneck in one language often drives team-wide tool choices — which editor you recommend, whether you adopt BSP, whether you fork a plugin — far more than any build performance discussion.

key takeaway

For JetBrains IDEs, the current mainstream path is the official JetBrains Bazel plugin, successor to the Google-maintained plugin, with language coverage that should be checked against the plugin release you plan to support. For VS Code, expect a composition: a Starlark extension for BUILD files themselves, plus a per-language bridge (BSP for Scala/Swift, gopackagesdriver for Go, Salesforce's Java server for JVM) that walks the Bazel graph through aspects. In either path, the editor's partial, in-memory model provides fast feedback, while the actual requested Bazel build or test is authoritative. Pinning the tools and configuration used on both sides aligns their versions and semantics without pretending that an editor session is a hermetic build action. The metadata mechanism is 4.8 Aspects. The full LSP architecture is 4.8.5 Language Server Integration Architecture. The faster inner-loop iteration story that sits alongside IDE integration is 3.5.2 Inner-Loop Build Delegation, and distributing the hermetic tools the IDE needs on PATH is 3.5.3 Developer Tool Management.

Check your understanding · 3 questions

1.Why does the JetBrains IDE (IntelliJ) run its own type checker and AST instead of just calling 'bazel build' on each file change?

Select one answer

2.Match each language's IDE bridge to its mechanism:

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

Answers
Go (VS Code)
JVM (VS Code)
C++ (VS Code/CLion)
Swift (VS Code)

3.True or false about JetBrains-style Bazel IDE integration:

Choose True or False for each sentence

A dedicated JetBrains Bazel plugin builds an IDE project model from Bazel metadata rather than relying on a per-keystroke bazel build.
Maintainers should treat exact language coverage as product status to verify for the plugin release they plan to support.
The JetBrains plugin path and VS Code/LSP path both commonly rely on aspects to extract Bazel graph metadata.
0 of 3 answered

Footnotes

  1. Bazel plugins in JetBrains IDEs — IDE tooling as the #2 pain point in the 2025 Bazel user survey. History of the Google bazelbuild/intellij plugin and customer forks. GA of the new JetBrains plugin with IntelliJ 2025.2. Supported languages (JVM, Go, Python) and the CLion/C++ gap. BSP-prototype lineage. Hirschgarten open-source repo. Java directory-vs-source-list project-model friction. Why invoking Bazel per keystroke is too slow 1 2 3 4 5 6 7 8 9 10 11 12

  2. JetBrains Bazel project views — importing and sharing .bazelproject project views

  3. bazel-contrib VS Code Bazel extension — installation, language-server support, and the bazel.lsp.command setting

  4. Intellij IDEA & Bazel: what aspects can tell us about your project? — the plugin injects aspects into the user's workspace. Rule detection via bazel query / bazel mod graph. Dynamic extension loading that generates an extensions file based on detected rules to avoid provider-loading failures

  5. Review of State of the Art Solutions for IDE support and Developer Tooling in Monorepos — Starpls + Bazel Build VS Code extension for editing BUILD / .bzl files 1 2

  6. State of Swift and iOS in Bazelsourcekit-bazel-bsp wiring SourceKit LSP to Bazel via BSP. VS Code / Cursor as an Xcode alternative. rules_xcodeproj for Xcode project generation 1 2

  7. Eclipse and VS Code IDE Support for Java packages in Bazel — Salesforce's Bazel Java SDK shared between the Eclipse plugin and a VS Code extension built on Red Hat's Java LSP. Partial project imports. Per-package classpaths via bazel query + aspects 1 2 3

  8. Go Editor Support in Bazel Workspaces — full stack: editor → gopls (LSP) → go/packagesgopackagesdriverrules_go GoArchive provider via aspect. gopls in-memory snapshot vs Bazel action cache analogy 1 2 3

  9. Bazel: {Fast, Correct, Seamless}: Choose 3 — LinkedIn's bazel run setup rule that writes VS Code settings.json configuring GOPACKAGESDRIVER and Go environment variables for gopls

  10. The State of Compilation Database in Bazel — four approaches to generating compile_commands.json (build interception, extra actions, aquery, aspects). Tree-artifact limitations of aquery. hedronvision/bazel-compile-commands-extractor as de facto standard 1 2 3

  11. Hedron compile commands extractor repository map — supported setup, aquery-based refresh controls, clangd symptoms, remote-generated-file caveats, tree-artifact limits, and license scope.

  12. index-import repository map — documented path-remapping interface, focused Swift/Clang/multiple-store fixtures, and the boundary between the illustrative Bazel snippet and a workspace-specific integration.