# My AI writes 400-line diffs in 30 seconds. I built a 2-second gate that reviews them.

> Source: <https://dev.to/techdevfun/my-ai-writes-400-line-diffs-in-30-seconds-i-built-a-2-second-gate-that-reviews-them-dn6>
> Published: 2026-10-31 12:07:46+00:00

My AI pair programmer writes a 400-line diff in 30 seconds. I used to spend 20 minutes reviewing it: scroll, squint, guess whether that new function is "a bit complex" or "merge-blocking complex", wonder if the helper it added already exists somewhere else in the file. Tests pass. The diff looks plausible. Ship it.

A week later we find the duplicated retry logic in three places and a 14-branch function nobody remembers writing.

The bottleneck in AI-assisted coding is not writing code. It is reviewing diffs at the speed the model produces them. Eyeball review does not scale to model throughput, and "tests pass" says nothing about the shape of the code that was just added.

I built [diffgate](https://github.com/techdevfun/diffgate), a zero-config CLI that checks **changed code only** for complexity, duplication, and PR growth. It runs in CI in under a second and returns a verdict: pass or fail, with reasons.

Four gates, all scoped to the diff (v1):

| Gate | Default | What it checks | 
|---|---|---|
| `complexity` | max 10 | a touched function above the max fails | 
| `delta` | max +15 | a function gaining more than this at once fails | 
| `dupe` | 50 tokens | a normalized token block repeated in the diff fails | 
| `growth` | +500 lines | a single file growing past this fails ("split this PR") | 

The point of scoping to the diff: pre-existing tech debt is context, never a failure. You do not get punished for the legacy function you did not touch. You get stopped when *this PR* makes things worse.

A sample PR with three violations: a checkout function at complexity 13, a 106-token duplicated block in a Go file, and one JSON file growing by 622 lines. diffgate fails it with the exact locations. Fix the branch, re-run: PASS, all functions at complexity 5 or below. In CI it emits `::error` annotations that land on the PR's Files tab:

```
diffgate check --base origin/main --format github
- run: npx -y diffgate-cli check --base ${{ github.event.pull_request.base.sha }} --format github
```

There is also a `measure` mode that prints numbers with no verdict, for the "measure first, gate later" crowd. A 500-file diff checks in about 0.7 seconds on a laptop. Zero runtime dependencies, Node 20+.

Complexity is a **token-based cyclomatic approximation**: count branch keywords and operators (`if`, `for`, `while`, `&&`, `||`, `??`, ternaries, `elif`, `except`...) per function. Same class as `lizard`: fast and deterministic, not compiler-grade.

Duplication detection normalizes identifiers to `id` and literals to `str`/` num`, then finds repeated token windows with a rolling hash, so copy-paste with renamed variables still matches. Strings are blanked to a single `str` marker rather than nothing, which avoids mass false hits on fixtures full of string literals. I hand-computed complexity fixtures in JS, Python, and Go (all 5) and the tool agreed on all of them; the suite is 38 tests, all green.

`diffgate.json` in the repo root.
Honest comparison, checked 2026-10-31:

```
git clone https://github.com/techdevfun/diffgate.git
cd diffgate
npm install && npm run build
npm link   # or run ./bin/diffgate directly
```

(The `diffgate-cli` npm package is being published; until the registry listing is live, install from source. The README will be updated the moment `npm install -g diffgate-cli` works.)

Then drop `diffgate check` into CI and let it argue with your pull requests. It is deterministic, local, and has no model, no account, and no network to phone home to.

Roadmap: new-lines-without-coverage when an lcov/cobertura file is supplied, more languages, and per-file growth budgets. Repo: [github.com/techdevfun/diffgate](https://github.com/techdevfun/diffgate). MIT licensed.
