# Supported Languages

Bumpkit reads 15 languages: Go, TypeScript and JavaScript, Python, Ruby, PHP, Java, Kotlin, Scala, C#, Dart, Elixir, Rust, C, C++ and Swift. TypeScript and JavaScript count as one language: they share one analyzer and get the same support. This page shows what each language gets, and where support is partial.

> **Beta.** Blast radius, release checks, behavior checks, test evolution, code quality and cross-repo impact are Beta features in early access. If your workspace isn't on the list yet, the dashboard shows an early-access page where you can sign up. See [Release Checks & Blast Radius](/docs/release-checks), [Code Quality & Architecture](/docs/quality) and [Cross-Repo Impact](/docs/cross-repo).

"Yes" means Bumpkit does it for that language. "Limited" means it works with a gap described in the notes below the table. "No" means it doesn't.

## Code graph features

These features read your code at `--head` as a graph of what calls what: `bumpkit blast-radius`, `bumpkit predict`, `bumpkit history`, `bumpkit rollback`, the `blast_radius` findings in `bumpkit check` and `bumpkit gate`, `bumpkit tests` and `bumpkit quality`.

| Language | Extensions | Blast radius and code graph | Test skeletons (`bumpkit tests`) | Code quality (`bumpkit quality`) | Cross-repo impact (service graph) |
|---|---|---|---|---|---|
| Go | `.go` | Yes | Yes | Yes | Yes |
| TypeScript and JavaScript | `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, `.cjs` | Yes | Yes | Yes | Yes |
| Python | `.py` | Yes | Yes | Yes | Yes |
| Ruby | `.rb`, `.rake`, `.ru` | Yes | Yes | Limited ³ | Yes |
| PHP | `.php` | Yes | Yes | Limited ³ | Yes |
| Java | `.java` | Yes | Yes | Limited ³ | Yes |
| Kotlin | `.kt`, `.kts` | Yes | Yes | Limited ³ | Yes |
| Scala | `.scala`, `.sc` | Yes | Yes | Limited ³ | Yes |
| C# | `.cs` | Yes | Yes | Limited ³ | Yes |
| Dart | `.dart` | Yes | Yes | Limited ³ | Yes |
| Elixir | `.ex`, `.exs` | Yes | Yes | Limited ³ | Yes |
| Rust | `.rs` | Yes | Yes | Limited ³ | Yes |
| C | `.c`, `.h` | Limited ¹ | Yes | Limited ³ | Yes |
| C++ | `.cc`, `.cpp`, `.cxx`, `.hpp`, `.hh`, `.hxx`, and `.h` files that contain C++ | Limited ¹ | Yes | Limited ³ | Yes |
| Swift | `.swift` | Limited ² | Yes | Limited ³ | Yes |

1. **C and C++ blast radius is limited.** In large, template-heavy C++ headers (single-header libraries and test frameworks, for example), some callers don't show up. Plain C files are much less affected. Release checks are unaffected: they read the diff lines, not the graph.
2. **Swift blast radius is limited.** A few Swift calls are missed, and very slow-to-read files are left out of the graph. Release checks are unaffected.
3. **Code quality is limited.** Architecture drift (module cycles, layer rules, fan-out) and the code-quality analyses (copied code, single-implementation interfaces, pass-through functions, unused symbols, function size, comment-heavy files) read every supported language. The performance analyses (queries inside loops, blocking IO and unbounded work on code reached from HTTP handlers) are tuned for Go, TypeScript, JavaScript and Python, and find little or nothing in other languages.

With `bumpkit tests --coverage`, the coverage profile must be a Go cover profile. In other languages, a function counts as tested when a test refers to it by name.

## Release checks

These checks run in `bumpkit check`, `bumpkit gate` and `POST /v1/checks`. They read the changed lines of the diff, so they work the same on every file of a language, however large. See [Checkers and finding kinds](/docs/release-checks#checkers-and-finding-kinds).

| Language | Public API (`exports`) | HTTP routes (`routes`) | Env vars (`config`) | Dependency manifests (`dependencies`) | Vulnerability lookup (`vulnerabilities`) | Behavior checks |
|---|---|---|---|---|---|---|
| Go | Yes | Yes | Yes | Yes: `go.mod` | Yes | Yes |
| TypeScript and JavaScript | Yes | Yes | Yes | Yes: `package.json` | Yes | Yes |
| Python | Yes | Yes | Yes | Limited ⁴: `requirements*.txt` | Yes | Yes |
| Ruby | Yes | Yes | Yes | Yes: `Gemfile`, `*.gemspec` | No | Yes |
| PHP | Yes | Yes | Yes | Yes: `composer.json` | No | Yes |
| Java | Yes | Yes | Yes | Limited ⁵: `build.gradle`, `build.gradle.kts`, `*.versions.toml`, `pom.xml` | No | Yes |
| Kotlin | Yes | Yes | Yes | Yes: same as Java | No | Yes |
| Scala | Yes | Yes | Yes | Yes: `*.sbt`, plus the Java manifests | No | Yes |
| C# | Yes | Yes | Yes | Yes: `*.csproj`, `*.props`, `packages.config` | No | Yes |
| Dart | Yes | Yes | Yes | Yes: `pubspec.yaml` | No | Yes |
| Elixir | Yes | Yes | Yes | Yes: `mix.exs` | No | Limited ⁶ |
| Rust | Yes | Yes | Yes | Yes: `Cargo.toml` | Yes | Yes |
| C | Yes | No | Yes | Yes: `CMakeLists.txt`, `conanfile.txt`, `conanfile.py`, `vcpkg.json` | No | Yes |
| C++ | Yes | No | Yes | Yes: same as C | No | Yes |
| Swift | Yes | Yes | Yes | Yes: `Package.swift` | No | Yes |

4. Python's release dependency check reads `requirements*.txt` files only. `pyproject.toml` dependencies aren't checked there, although cross-repo impact reads them.
5. In `pom.xml`, the release dependency check sees `<x.version>` properties only, because a `<dependency>` block spans several lines. Gradle files and version catalogs are read in full, and cross-repo impact parses full poms.
6. Elixir's `if ... do` guard checks are skipped by design. Removed `raise`, `rescue` handling and validation calls are still reported.

Vulnerability lookups use the OSV database for Go modules, npm, PyPI and crates.io packages. The other ecosystems aren't looked up yet.

Migration, secret, sensitive-area and untested-change checks don't depend on the language. Migration rules cover SQL and the Rails and Django migration APIs. Laravel `Schema::` calls, EF Core `migrationBuilder` calls and Ecto's `remove`, `rename` and `modify` are recognized as new migrations, but their drops and renames aren't scored yet.

## Large repositories

Bumpkit reads each file once. After the first run, it reads only the files that changed, so the next check on the same repository is fast even in a large monorepo.

Measured on an Apple M1 Pro (10 cores, 16 GB memory) with the cache on local disk. Times for public repositories are the median of five runs; times for the synthetic repository are the average of five.

| Repository | Files analyzed | First run | Same commit again | One file changed | Peak memory |
|---|---|---|---|---|---|
| Synthetic monorepo (Go, TypeScript, Python) | 50,003 | 1.6 s | 0.19 s | 0.69 s | 627 MiB |
| kubernetes/kubernetes at `3c690c1` | 9,200 | 1.5 s | 0.32 s | 1.2 s | 695 MiB |
| microsoft/vscode at `11ba54a` | 10,515 | 2.9 s | 0.28 s | 1.9 s | 1.4 GiB |

Kubernetes has 31,647 files at that commit. Besides the 9,200 analyzed, 8,917 are vendored, build output or test files, 13,482 aren't in a supported language, 2 are over 1 MB, 5 are symlinks, and 41 are `go.mod` files that Bumpkit reads to find module paths. "One file changed" includes rebuilding the call graph across the whole repository, which is most of that time in large repositories.

### Files Bumpkit doesn't analyze

Every blast radius report says which files it left out, with the same wording in `bumpkit blast-radius`, in the `notes` field of the `blast_radius` MCP tool and its `--json` output, and in the dashboard:

- **Files over 1 MB**, for example `4 files over 1 MB not analyzed`.
- **Test, vendored or build output files**: files under `vendor`, `node_modules`, `third_party`, `dist`, `build`, `testdata`, `test`, `tests`, `__tests__`, `coverage`, `.next`, `__pycache__`, `site-packages` and virtual environments, plus `.min.js` and `.d.ts` files and test files.
- **Other files**: anything not in a supported language, like images, Markdown, YAML or code in other languages, for example `12 other files not analyzed`.
- **Files that failed to parse**.
- **Symlinks and submodules**, for example `2 symlinks or submodules not analyzed`. A submodule is its own repository, so add it to Bumpkit separately.

When a change reaches more callers than a report can hold, the report also says `Some callers were left out to keep the report bounded; every area listed is still reached`.

## Known limits by language

### Go

- Public API is exported names outside `internal` and `cmd` directories.
- HTTP routes are `Handle` and `HandleFunc` calls and router methods such as `.Get("/x")`.
- `bumpkit verify` calls each changed exported Go function with the same generated inputs at both versions. That comparison is Go only.

### TypeScript and JavaScript

- Public API is `export` declarations. Type declarations (`.d.ts`) and minified files are skipped by the code graph.
- HTTP routes are Express-style router calls (`.get("/x")`, `.post`, `.all`) and Next.js `app/**/route` and `pages/api` files.
- Calls on values of unknown type are matched by method name and shown as "matched by name", which lowers a blast-radius finding's confidence to medium.

### Python

- Public API is top-level `def` and `class`.
- HTTP routes are decorators such as `@app.get("/x")` and `@bp.route("/x")`.
- Dependencies: see note 4.

### Ruby

- Methods after `private` or `protected` (or `private def`) aren't public API. Files under `bin`, `exe`, `script`, `config`, `db`, `vendor` and `lib/tasks` aren't public API.
- HTTP routes: Rails (`get`, `post`, `resources`, `resource`, `match`), Sinatra and Grape.
- Postfix guards (`return if x`) and `unless` aren't read as guards yet.
- Rails migrations under `db/migrate` are checked with the migration rules (`drop_table`, `remove_column`, `rename_column` and raw SQL).

### PHP

- `private` and `protected` members aren't public API, and neither are Blade templates or files under `bin`, `config`, `database`, `bootstrap`, `public`, `resources`, `routes` and `vendor`.
- Parameters are compared by name, because named arguments make a rename breaking. A type change alone isn't reported.
- HTTP routes: Laravel `Route::` calls, Slim and Lumen `$app->get(...)`, and Symfony `#[Route]` attributes.
- Laravel and Doctrine migrations are recognized, but `Schema::drop`, `dropColumn` and `renameColumn` aren't scored yet.

### Java, Kotlin and Scala

- Calls resolve across all three languages. Overloads share one name, so a call reaches every overload of that name.
- JVM sources all start with `src/main`, so set `gate.blast_radius.approve_areas` to package words (for example `[owner, billing]`) to get useful area names.
- HTTP routes: Spring mapping annotations, JAX-RS `@Path`, Micronaut, Ktor and Play's Scala and Java routing DSLs. Play's `conf/routes` file, Akka and Pekko HTTP, and http4s aren't read. Class-level prefixes aren't joined to method paths.
- A Kotlin or Scala parameter list that spans several lines is compared only on its first line, and a Scala `def x: Int` gaining a parameter list isn't reported as a signature change.
- Projects whose package path contains `samples` or `example` are skipped by the release checks, like other sample code.
- Liquibase changelogs and Flyway Java migrations aren't checked. Flyway `V1__name.sql` files are.

### C#

- Properties, fields, events, operators, indexers and anything `internal`, `private` or `protected` aren't public API. Generated files and EF Core migrations aren't either.
- A parameter list that spans several lines is compared only on its first line.
- HTTP routes: MVC `[HttpGet]`-style and `[Route]` attributes, and minimal APIs (`MapGet`, `MapPost`, `Map`, `MapMethods`). An `[HttpGet]` with no template isn't reported, and `[Route]` and `MapGroup` prefixes aren't joined to the action's template.
- With central package management, a version change shows up in `Directory.Packages.props`, where the version lives.

### Dart

- Public API is names without a leading `_`, in files under `lib`. Generated files (`.g.dart`, `.freezed.dart` and similar) aren't public API.
- Named parameters and signatures that `dart format` splits over several lines aren't compared.
- HTTP routes: shelf_router and `@Route` annotations. dart_frog file routes aren't read, and Flutter apps have no HTTP routes.
- SDK, path and git dependencies without a version aren't reported.

### Elixir

- Public API is `def`, `defmacro`, `defguard`, `defdelegate` and modules in files under `lib`. Signatures are compared by arity.
- Calls on variables and Erlang modules aren't linked in the code graph, and `use` macros aren't expanded.
- HTTP routes: Phoenix and Plug.Router. A `scope "/prefix"` isn't joined to the path, so moving a route into a new scope reads as one removed and one new route.
- Behavior checks: see note 6. Ecto `drop table(...)` is reported, but `remove`, `rename` and `modify ... null: false` aren't scored yet.

### Rust

- `pub(crate)`, `pub(super)` and `pub(in ...)` items aren't public API, and neither is code in `src/main.rs`, `src/bin`, `build.rs`, `examples`, `benches`, `tests`, `fuzz` or `xtask`.
- Tests inside `#[cfg(test)] mod tests` are left out of the code graph.
- With axum-style `.route("/x", get(h))`, the route's method is shown as `ANY`.
- A Cargo `0.x` minor bump (0.6 to 0.7) is reported as a version change, not as a major upgrade.

### C and C++

- Blast radius: see note 1.
- Public API is declarations in headers. `.c` files aren't public API, and neither are headers under `internal`, `detail`, `impl` or `private` directories or named `*_internal.h`, `*_impl.h`, `*-inl.h` or `*_p.h`. C++ declarations are keyed by name plus parameter types, so overloads are told apart.
- There's no HTTP route convention in C or C++, so no routes are reported.
- A `.h` file that contains C++ (namespaces, templates, classes) is read as C++.

### Swift

- Blast radius: see note 2.
- Public API is `public` and `open` declarations. `main.swift`, `Package.swift`, `Tests`, `Benchmarks`, `Examples` and `Plugins` aren't public API.
- HTTP routes: Vapor `app.get(...)` and `routes.post(..., use:)`.
- swift-testing `@Test` functions whose name doesn't start with `test`, with `@Test` on the line above, aren't counted as existing tests.
