待翻譯:Open-sourcing Metals v2: Databricks’ Java and Scala language server for multi‑million line codebases
AI 服務暫時不可用,以下為來源摘要,待恢復後補全翻譯:Most code at Databricks is now written by agents. For the moments engineers still...
AI 服務暫時不可用,以下為來源正文,待恢復後補全翻譯。
Open-sourcing Metals v2: Databricks’ Java and Scala language server for multi‑million line codebases | Databricks Blog Skip to main content Metals v2 rethinks code intelligence for large-scale monorepos that have outgrown conventional JVM language servers. Metals has been the official Scala language server for years. With full Java support in v2, companies with some of the largest Java codebases in the industry are now piloting Metals for Java alone. AI changes the relative value of Language Server Protocol (LSP) features. As engineers write less code by hand, low setup cost, fast feedback, and reliable codebase orientation matter more than broad autocomplete and refactoring coverage. Most code at Databricks is now written by agents. For the moments engineers still go hands-on, they reach for lightweight editors that spin up quickly and let them navigate the code with little setup. For Scala and Java, though, IntelliJ has set the standard for years. At the scale of our monorepo, it was effectively the only editor that could keep up. This post shares how we built an alternative solution by extending Metals, the widely-used Scala language server, to have first-class Java support and scale for the size of our monorepo. In collaboration with the upstream Metals team, we have now open-sourced Metals v2 so that anyone with a large Java and Scala codebase can pair their coding agent with a lightweight editor. Metals v2 is available today in Cursor, VS Code, and Neovim with installation instructions available on the Metals website. Building the IDE flywheel In May 2025, we began standardizing Databricks’ day-to-day editor workflow around Cursor. Cursor and VS Code were already widely used at Databricks for frontend and other non-JVM work, and both had strong SSH remote support for our cloud-based development environment. However, most of our services are written in Scala and Java, and navigating them at the scale of our monorepo was the holdout; the problem we set out to solve. Standardizing on one editor mattered beyond individual preferences. A unified IDE platform creates a flywheel: teams share one baseline for code exploration and development, while the platform team can concentrate investments in one place. Cursor became the primary editor for JVM work in our monorepo, and we consolidated enough that we did not renew the majority of our IntelliJ seats this year. Figure 1: Broader adoption increases the leverage of platform investments, which improves the shared IDE and reinforces adoption. Three signals show how far the shift to Cursor went: overall IDE usage, Scala and Java file open events where Metals sits on the critical path, and adoption outside Databricks. IDE usage The broadest signal is overall IDE usage. Cursor adoption grew on its own at Databricks but stalled by September 2025, where it held for months. Growth resumed with the Metals v2 rollout and by July 2026, 92% of weekly active IDE users open Cursor compared to 12% for IntelliJ. Among engineers who only use a single IDE, 2.4k are now on Cursor compared to 120 for IntelliJ. Figure 2: IDE usage, April 2025 – July 2026. The black line marks the first internal Metals v2 release, which moved Scala and Java navigation onto a Metals-provided sourcepath (deep dive below). Scala and Java file open events A closer signal than overall IDE usage is the ratio of files opened in each IDE for Scala and Java, the languages where Metals v2 sits on the critical path. Since the first major Metals v2 improvements landed in Cursor in October 2025, Cursor's share of Scala and Java file open events has risen from 40% to 78%. The climb is slower than aggregate IDE adoption because Scala and Java are where IntelliJ was most entrenched, but Cursor's share continues to trend upwards month-over-month. Figure 3: Scala and Java file open events by IDE, April 2025 – July 2026. Early external adoption Metals v2 started as a Databricks fork, but the goal was always to bring the work back to the open-source community. Together with the Cursor team and the core Metals maintainers at VirtusLab, we’re preparing a stable Metals v2 release to supersede the current stable v1 release. We're contributing to Metals v2 to make Cursor work well for multi-million line Java codebases with a focus on improving Bazel support, debugging, and testing.—Kevin Niparko, Cursor AI is changing how developers use IDEs, and Metals v2 moves in the right direction: fast startup, reliable codebase orientation, and an architecture built for large codebases. Databricks validated the approach at exceptional scale, and VirtusLab is excited to help bring this work to the broader Scala and JVM community.—Krzysztof Romanowski, Head of Development Productivity, VirtusLab Conversations with other large JVM codebases have reinforced the same pattern we saw at Databricks: demand for Cursor, VS Code, and Neovim with strong SSH remote support, platform pressure toward unified tooling, and no credible path through existing JVM language servers at monorepo scale. We began rolling out Metals V2 at Stripe less than a month ago, yet we're constantly impressed with how well it works in our Java codebase, the excitement from our engineers, and how delightful it is to work with and learn from the maintainers.—Mahib Hosain, Developer Platform, Stripe This is the model we want for Metals v2: shared infrastructure for large JVM codebases, maintained in the open, and shaped by the companies that need it to work at scale. Ongoing development is led by VirtusLab, reach out to them with questions, feedback, or contributions via the GitHub issue tracker or [email protected]. Deep dive: how Metals v2 scales Everything above is the case for Metals v2. The rest is for readers who want to better understand the engineering behind low-latency code intelligence across a 26M-line monorepo. Because agents write most of the code, fast codebase orientation mattered more than comprehensive Language Server Protocol (LSP) completion and refactoring coverage. That narrowed the problem, but it did not make the solution obvious: we still had to deliver easy-to-setup, low-latency navigation for a Scala and Java Bazel monorepo this large, and off-the-shelf LSPs were not designed to support that scale. We had to build repository-scale code intelligence from first principles, and treat startup usefulness as a key metric we could measure and improve. Defining time-to-initial-intelligence (TTII) Time-to-initial-intelligence (TTII) measures how quickly the editor becomes useful after opening the repository. We start the clock when the language server activates and stop when the most important features are available, assuming no user intervention. These critical features include fuzzy searching workspace symbols, jump-to-definition, and finding symbol usages across the entire repository. Three layers of Metals v2 Metals v2 is a language server for Scala and Java. We started from Metals v1, the official Scala language server, but reaching our TTII target took more than tuning it around the edges. We forked it and reworked three central layers: the repo index, the Scala and Java compiler pipelines, and the build integration boundary. Build‑free repo index: Metals v2 removes the Build Server Protocol (BSP) from the startup critical path by indexing workspace sources directly using its own mbt index described in the section below. The key departure from Metals v1 is that Metals now owns the initial project model instead of waiting for the build server to supply it. Compiler‑backed interactive pipelines for Scala and Java: Metals v2 moves symbol loading from a build-provided classpath into a Metals-provided sourcepath allowing diagnostics and navigation to more accurately reflect the actual code on disk rather than a stale snapshot from the last successful compilation. This required rethinking a long tail of core assumptions from the v1 codebase. Metadata-first build integration: Metals v2 still uses BSP, but with a narrower contract. Metals v1 used the build server on the editor hot path for diagnostics, while Metals v2 moves routine diagnostics out of the build server and uses BSP mostly to query build metadata: dependencies, generated sources, test discovery, and debug launchers. This shift lowers the barrier to implementing a BSP server for Metals v2, and our internal Bazel BSP server validates that the model scales to large Bazel monorepos. The sections below walk through each layer in turn. The mbt index: repo-wide intelligence before build sync mbt stands for Metals Build Tool, and the mbt index is the main enabler of TTII or the “useful immediately” contract. It is a content-addressed index of workspace sources: Metals uses the command git ls-files --stage to discover files and Git blob OIDs to decide which index entries can be reused. With repo-wide information available before build sync, Metals can answer “first mile” questions: diagnostics for cross-file references fuzzy symbol search jump-to-definition across the repo but not external dependencies or generated code broad find-references and find-implementations through creative use of per-document bloom filters The mbt index is effectively a hash map from source file to a file-local summary. An entry records the file’s package declarations, definitions with source locations, and compact bloom filters for identifiers referenced in the file. The definitions power workspace symbol search and jump-to-definition while the bloom filters let Metals quickly rule out files that cannot contain a reference before doing more precise checks. Because each entry is derived only from one file, incremental updates stay straightforward: when a file changes, Metals recomputes that file’s entry and replaces it. In our monorepo, the persisted mbt index weighs 936MB uncompressed and contains information about 2.9m symbols across over 142k Scala, Java, and Protobuf files. A clean benchmark build takes 22 seconds at full CPU utilization across 32 cores, while parsing a pre-built index from disk takes 5 seconds. In production, we measure TTII as the time to start the server, load a stale mbt index, update it against the latest git ls-files --stage state, and restart the Scala and Java presentation compilers: p50 8.7s, p90 36.7s. Fuzzy symbol search across 2.9m workspace symbols is p50 10ms, p90 95ms. There is room to reduce TTII further, but at these numbers it is not the bottleneck we need to address next. Scala pipeline: pushing 24M lines of code on a single compiler instance The Scala pipeline is built around the presentation compiler, a mode of the Scala type-checker that caches and reuses symbol table information across compilation runs. This reuse, combined with the compiler's lazy symbol resolution, lets a single instance keep the full 24M-line Scala codebase in scope while publishing diagnostics at p50 0.9s, p90 8.9s. That single Scala compiler instance runs in one of two modes, depending on how much information Metals has from the build server about the file being edited. Before a build sync, a fallback compiler takes a permissive view, treating every source file in the repository as an eligible dependency candidate making navigation useful immediately, even across code that does not yet compile in Bazel. After a build sync, a precise compiler restricts itself to the classpath and sourcepath boundaries the build server reports, which is what makes its diagnostics and dependency information build-accurate. Both the precise and fallback modes lean on the same two techniques to keep a sourcepath this large tractable: Outline mode for unopened sources. Sources that are not open in an editor are stripped of method bodies before type-checking. This preserves the type signatures the compiler needs while avoiding work in method bodies, where most type-checking time is spent. An in-memory source-layout [truncated for AI cost control]