# Code Quality & Architecture

Code that passes tests can still erode your architecture and performance; these checks flag drift, low-quality AI code and slowdowns, for tech leads and architects.

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it.

Bumpkit compares the base and head of a release and reports three kinds of problems in the code the release adds:

| Analysis | Finding kind | Flag |
|----------|--------------|------|
| Architecture drift: new dependency cycles, layer violations, coupling and complexity growth | `architecture_drift` | `architecture_drift` |
| AI code quality: copied code, needless abstractions, dead code, oversized functions, comment noise | `code_quality` | `slop_tracker` |
| Performance diff: queries in loops, blocking IO on request paths, unbounded work, benchmark regressions | `performance` | `performance_diff` |

Each analysis runs only when its flag is on for your workspace. The analyses read all 15 languages Bumpkit supports; see [Supported Languages](/docs/supported-languages). The request-path checks of the performance diff find HTTP handlers in Go, Python, TypeScript and JavaScript. Generated files, such as `*_templ.go`, `*.pb.go`, `*.sql.go`, `*.min.js` and `*.d.ts`, are skipped.

## How it runs

The analysis runs on your machine or CI runner, inside the `bumpkit` CLI. It reads both versions from your local Git history and sends Bumpkit the findings and a summary of metrics.

| Command | What happens |
|---------|--------------|
| `bumpkit quality` | Prints the report for a range of commits. Nothing is gated. |
| `bumpkit gate` | Sends the findings with the release. `gate.quality` in `bumpkit.yml` decides whether they hold it. |
| `bumpkit check` | Lists the findings with the rest of the check. |

The CLI asks `GET /v1/features` which analyses are on for your workspace. To run it in CI, install the CLI in the job and set `BUMPKIT_API_KEY`; see [Install the CLI](/docs/#install-the-cli).

## bumpkit quality

```bash
bumpkit quality --base v1.4.0
```

```text
Code quality of v1.4.0..HEAD
  Modules 42 → 44, dependencies 118 → 125, cycles 0 → 1, instability 0.48 → 0.50
  Mean complexity 3.10 → 3.24 (max 21 → 27) over 612 functions
  New code: 840 lines, 6.2% duplicated, low-value code score 4.8 per 100 new lines
    Claude: 610 lines, 9 findings, low-value code score 6.1
    No agent signals: 230 lines, 2 findings, low-value code score 1.3

Architecture drift
  MEDIUM New dependency cycle: services/billing → services/users → services/billing services/users/plan.go:14
```

| Flag | Default | Description |
|------|---------|-------------|
| `--base` | latest tag | Version or ref the change starts from. |
| `--head` | `HEAD` | Commit being released. |
| `--policy` | `bumpkit.yml` at the base | Policy file to read limits and layers from. |
| `--bench-base` | none | `go test -bench` output, or JSON, measured at the base. |
| `--bench-head` | none | `go test -bench` output, or JSON, measured at the head. |
| `--json` | off | Print the full report as JSON. |

The command exits with an error if all three analyses are off for your workspace.

## Architecture drift

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `architecture_drift`.

Bumpkit builds the module dependency graph of each version. A module is a directory. Each finding points at the file and line of the new dependency.

| Checker | Reports | Severity |
|---------|---------|----------|
| `cycles` | A new dependency cycle between modules, with each hop and the dependency that closed it. | medium |
| `layers` | A new dependency that breaks `architecture.layers` or `architecture.forbidden`. | high |
| `layers` | Without declared layers: a new dependency that reverses the direction between two areas seen at the base. | medium |
| `coupling` | A module that picked up several new module dependencies, with its fan-out and instability before and after. | low |
| `complexity` | A changed function whose cyclomatic complexity rises above `gate.quality.max_complexity`. Medium at twice the limit. | low or medium |

### Declare layers

`architecture` is a top-level key in `bumpkit.yml`, next to `gate`. List layers from the top down. A layer may depend on layers listed after it, never on layers listed before it.

```yaml
architecture:
  layers:
    - name: cmd
      paths: [cmd]
    - name: api
      paths: [services/api, services/web]
    - name: domain
      paths: [services/orders, services/payments]
    - services/db
  forbidden:
    - from: services/web
      to: services/db
```

| Key | Description |
|-----|-------------|
| `layers[].name` | A unique name for the layer. |
| `layers[].paths` | Directory prefixes in the layer. A trailing `/**` is allowed. Defaults to the name. |
| `layers[]` as a string | Shorthand for a layer whose name is also its path. |
| `forbidden[].from`, `forbidden[].to` | A layer name or directory prefix. Any new dependency from `from` to `to` is reported. |

Paths are relative to the repository root. If you declare no layers, Bumpkit infers the intended direction from the base and reports dependencies that reverse it. See [architecture](/docs/bumpkit-yml#architecture) in the bumpkit.yml reference.

## AI code quality

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `slop_tracker`.

Bumpkit looks only at lines the release adds, outside tests.

| Checker | Reports |
|---------|---------|
| `duplication` | New lines that repeat existing code, names and literals aside. The minimum match is `gate.quality.clone_tokens` tokens. |
| `abstraction` | A new interface with a single implementation. |
| `wrappers` | A new function that only forwards its arguments to another. |
| `dead_code` | A new symbol nothing in the repository references, tests included. |
| `function_size` | A changed function longer than `gate.quality.max_function_lines`. |
| `comment_noise` | A file where much of the new code is comments. |

All `code_quality` findings are low severity.

### Low-value code score

The low-value code score rates `code_quality` findings per 100 new lines of code, from 0 to 100. Bumpkit splits new lines, findings and the score by author: each coding agent it detected, and "No agent signals" for the rest. This lets you compare what agents add with what people add.

## Performance diff

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `performance_diff`.

Bumpkit finds the code reached from HTTP handlers, up to `gate.quality.hot_path_depth` calls deep, and reviews new code on those paths.

| Checker | Reports | Severity |
|---------|---------|----------|
| `n_plus_one` | A query inside a loop. High when the loop is on a request path. | medium or high |
| `blocking_io` | A blocking network, file or process call on a request path. | medium |
| `hot_path_complexity` | A function on a request path whose complexity grew. Medium above `max_complexity`. | low or medium |
| `unbounded` | An allocation or loop sized by request input. | medium |
| `benchmarks` | A benchmark that got slower, compared between versions. | medium, or high at 4 times the threshold |

Request-path findings name the handler chain that reaches the code.

### Compare benchmarks

Run your Go benchmarks at least 4 times on each version, then pass both outputs:

```bash
git checkout v1.4.0 && go test -bench . -count 10 ./... > old.txt
git checkout main && go test -bench . -count 10 ./... > new.txt
bumpkit quality --base v1.4.0 --bench-base old.txt --bench-head new.txt
```

`bumpkit gate` takes the same `--bench-base` and `--bench-head` flags. Bumpkit compares each benchmark's runs between versions and reports a change when it's statistically clear and the median moved by at least `gate.quality.bench_threshold` percent. Improvements and benchmarks with too few runs are reported at info severity.

Instead of `go test` output, you can pass a JSON array:

```json
[
  {"name": "BenchmarkParse", "samples": [1210, 1198, 1225, 1203]},
  {"name": "BenchmarkRender", "ns_per_op": 5400}
]
```

## Gate policy

Under `gate.quality` in `bumpkit.yml`, set what each finding kind does in the [Release Gate](/docs/release-gate):

```yaml
gate:
  quality:
    architecture_drift: block
    code_quality:
      action: approve
      severity: low
      when: agent
    performance: approve
    max_function_lines: 80
    max_complexity: 15
```

Each rule is either an action, or an object with these keys:

| Key | Values | Default | Description |
|-----|--------|---------|-------------|
| `action` | `warn`, `approve`, `block` | `warn` | `warn` only reports. `approve` needs an approval. `block` fails the gate. |
| `severity` | `high`, `medium`, `low` | `medium` | The lowest severity that triggers the action. |
| `when` | `always`, `agent`, `agent_or_unknown` | `always` | `agent` and `agent_or_unknown` apply to any release not confirmed as written by a person. A `block` fails only on a declared or verified agent change, and needs an approval otherwise. |

All `code_quality` findings are low severity, and so are `coupling` findings. With the default `severity: medium`, a `code_quality` rule never triggers. Set `severity: low` to act on them.

Limits:

| Key | Default | Range | Used by |
|-----|---------|-------|---------|
| `max_function_lines` | 80 | 1 to 10000 | `function_size` |
| `max_complexity` | 15 | 1 to 1000 | `complexity`, `hot_path_complexity` |
| `clone_tokens` | 40 | 10 to 1000 | `duplication` |
| `hot_path_depth` | 4 | 1 to 10 | performance checkers |
| `bench_threshold` | 5 | 0 to 1000 (percent) | `benchmarks` |

Suppressed findings don't trigger a rule. When a rule triggers, the gate reason is named `quality.<kind>: <action>` and lists up to five findings. See [quality](/docs/bumpkit-yml#quality) in the bumpkit.yml reference.

## Quality dashboard

Every `bumpkit gate` run sends a snapshot of the metrics with the release. The **Quality** tab on the dashboard's [Insights page](https://dashboard.bumpkit.dev/insights) charts the last 30 releases of each repository:

- Low-value code score
- Duplicated new code (%)
- Mean cyclomatic complexity
- Dependency cycles
- Mean instability

## API

`bumpkit gate` sends the findings and the snapshot in the `POST /v1/gate` request. Stored trends are at `GET /v1/quality/history` (`bumpkit quality history`). The gate request fields:

| Field | Description |
|-------|-------------|
| `findings` | Findings with kind `architecture_drift`, `code_quality` or `performance`. Bumpkit keeps up to 50 per kind. |
| `quality` | The metrics snapshot: `base` and `head` commit SHAs, `before` and `after` module metrics, `release` totals for the new code, and `authors`. |

Findings for a kind whose flag is off are ignored. Agents use the `quality` and `quality_history` MCP tools. See the [API Reference](/docs/api-reference) for the full gate request.
