Originally published at kunalganglani.com — read it there for inline code, hero image, and live links.
AI Generated Code Quality [2026]: A 0–100 Audit Rubric + CI Gates
AI-generated code quality is what happens when you stop treating model-written diffs like “helpful suggestions” and start treating them like untrusted input. Same bar as human code. Same scrutiny. Because if you merge a 2,000-line agent diff without gates, you’re not moving fast. You’re signing up for a rewrite tax and playing security-incident roulette.
The keyword “ai generated code quality” might only show ~10 searches/month in Google Ads Keyword Planner, but that number is basically a lie by omission. The demand shows up in adjacent queries. In my own Google Search Console data, this topic neighborhood already has 1,178 impressions, with one related query at position 1.1 and “ai code quality” sitting around position 13.8. That’s a pretty clean signal that people want something operational, not another “do code review” pep talk.
What’s changed in 2026 isn’t that AI writes “ugly code.” It’s that agentic workflows can generate large, coherent-looking multi-file changes fast enough that review turns into performance art. The failure mode isn’t a missing semicolon. It’s subtle logic bugs, dependency hallucinations, and insecure glue code that “works” until it doesn’t.
Here’s the framework I use to decide ship vs refactor vs rewrite, plus CI gates that catch the two most expensive classes of failures: hallucinated APIs and silent security regressions.
How to evaluate AI-generated code quality (the 0–100 rubric)
Most advice on AI-generated code review reads like: “be careful.” That’s not a process. That’s a mood.
What I want instead is a score an engineering lead can use in a 10-minute pass to answer one question:
Is this diff worth owning?
Below is a rubric that produces a 0–100 score, with explicit weights. It’s built for AI agents generating multi-file changes, not Copilot nudging you toward a nicer map().
The scoring model
- Maintainability (30 points)
- Testing (25 points)
- Security posture (25 points)
- Rewrite likelihood (20 points)
A couple rules I won’t compromise on:
- Hard fails beat scores. If a hard-fail gate trips (secrets, unsafe deserialization, prompt injection exposure, etc.), the score is irrelevant.
- The score is about ownership cost, not aesthetic quality.
Here’s the rubric table you can drop into a PR template.
| Dimension | Weight | What “good” looks like | Common AI failure mode | How to measure quickly |
|---|---|---|---|---|
| Maintainability | 30 | Small modules, clear boundaries, boring patterns, consistent naming, low cognitive load | Over-abstracted helpers, copy-pasted logic, accidental frameworks, unnecessary patterns |
git diff size, churn zone check, module graph intuition, cyclomatic complexity hotspots |
| Testing | 25 | Tests match risk, not coverage theater. Fast unit tests + a few high-signal integration tests | Snapshot tests of outputs, missing edge cases, no negative tests | Changed lines vs tests ratio, critical path tests, mutation testing spot-check |
| Security posture | 25 | Inputs validated, dependencies pinned, secrets never touched, safe defaults | Insecure output handling, SSRF primitives, auth bypasses, dependency sprawl | SAST + dependency audit + secret scan + threat model prompts |
| Rewrite likelihood | 20 | Diff fits existing architecture, obvious ownership, low coupling, minimal new surface area | New subsystem invented in a PR, poor cohesion, unclear data model | “Could I explain this in 2 minutes?”, number of new concepts, number of new files, ownership clarity |
Interpreting the score: ship / refactor / rewrite
I use three bands:
- 85–100: Ship. You still review it. You’re just not inheriting a hidden mortgage.
- 70–84: Refactor before merge (or immediately after behind a flag). The idea is fine. The implementation will rot if you let it land as-is.
- < 70: Rewrite (or throw it away and re-prompt with constraints). You’re buying future pain.
Then I layer in a few red flags that override “but the score looks okay” optimism:
- Any new authz path with no explicit tests.
- Any code that touches money, identity, or PII without an integration test.
- Any dependency added “because it was easier.”
If the diff introduces a new dependency, a new trust boundary, and no tests, it’s not a feature. It’s a liability.
Minimal “hard fail” checks vs “soft score” checks
Hard fails (block merge):
- Build/compile/typecheck fails.
- Dependency resolution fails (lockfile inconsistent, missing package, unpublished version).
- Secrets detected.
- Known-vulnerable dependency above your severity threshold.
- License policy violation (if you ship commercial software).
Soft score inputs (warn, but don’t always block):
- Complexity spikes.
- Coverage deltas.
- Lint/style issues.
- “Too many files touched” (context-dependent, but in 2026 it matters).
Common AI-generated code issues (bugs, security, maintainability)
When people ask “What are the common problems with AI-generated code?”, they usually mean formatting. That’s the least interesting part.
The real list is the stuff that wastes weeks.
1) Hallucinated APIs and phantom packages
This is the most operationally expensive failure mode because it’s convincing.
You get code that references a symbol that doesn’t exist. Or it pulls in a package name that sounds right but isn’t real. If you’re lucky, CI explodes at compile time. If you’re unlucky, it’s a dynamic language and the error shows up in prod after a deploy.
If you want this to stop being a human-review problem, make it a build gate.
2) Logic that’s “reasonable,” not correct
Models crank out code that passes a casual skim. They struggle with:
- boundary conditions
- concurrency hazards
- subtle invariants
- partial failure behavior
I’ve shipped enough automation tools to know the code that hurts you is the code that fails rarely.
3) Dependency sprawl and supply chain risk
Agents love adding libraries because it reduces prompt complexity. That’s great for the model and terrible for you. More dependencies means:
- more vulnerability exposure
- more maintenance surface
- more transitive junk you didn’t sign up for
If your diff adds 3+ new dependencies for a small feature, assume it’s compensating for missing design clarity.
4) Unowned abstractions
A human writes abstractions because they expect to maintain them. An agent writes abstractions because it has seen them in training data.
The smell is simple: the abstraction has no obvious future usage, and nobody on the team would have chosen it on purpose.
5) Security footguns in glue code
AI-generated code is disproportionately “glue”: adapters, request handlers, parsers, serialization, auth middleware.
That’s also where security bugs live.
If you want a concrete taxonomy, map your checks to the OWASP GenAI Security Project’s LLM Top 10 (the evolution of the original “OWASP Top 10 for LLM Applications”) and your org’s governance to NIST’s AI Risk Management Framework (AI RMF 1.0). The point isn’t compliance cosplay. The point is getting everyone to use the same words when you say “this is risky.”
Testing strategy for AI-generated code (what to require before shipping)
“What tests should be required before shipping AI-generated code?” is the wrong framing.
The right framing is: what tests buy down the specific risks AI introduces?
I require a minimum set, then I scale up based on blast radius.
The minimum test bar (my default)
- Unit tests for new pure logic (especially parsing, mapping, validation). If there’s branching, there’s a test.
- One integration test per new external boundary (DB, queue, HTTP API, file system). Mocks can exist, but they can’t be your only defense.
- Negative tests. At least 2 per boundary: invalid input and partial failure.
- Property tests or fuzz tests for input-handling code when the feature processes untrusted input.
If the diff touches auth, payments, or user-generated content, the bar goes up. If it touches all three, it goes up a lot.
Why fuzzing matters more in the AI era
Fuzzing is one of those things where the boring answer keeps being the right one.
Agent-written code routinely misses adversarial inputs. Not because it’s “careless.” Because next-token prediction isn’t rewarded for thinking like an attacker.
Google’s OSS-Fuzz exists because fuzzing finds real crashes and vulnerabilities at scale. It’s been running since 2016, originally motivated by Heartbleed-era lessons, and it supports multiple languages now.
If you can’t integrate OSS-Fuzz, steal the idea. Add a small fuzz target for:
- parsers
- URL handling
- file format decoding
- any “accept string, output structured object” code
Even running a fuzz target for 60 seconds in CI can catch embarrassing crashes early.
Coverage is not the metric you want
Coverage is easy to game, and AI is great at gaming it by accident.
What I look at instead:
- Changed-lines-to-tests ratio (if you add 300 lines and 0 tests, you didn’t ship. You borrowed).
- Mutation testing spot-checks for critical logic (even if it’s just on a single module).
- Contract tests when interacting with internal service APIs.
If you want a clean mental model for testing non-deterministic AI systems, it’s adjacent but not identical. I wrote about that in non-deterministic AI system testing.
Security considerations for AI-assisted coding
“Is AI-generated code secure?” The only honest answer is: it’s as secure as your review + CI + supply chain controls.
The model is not your security boundary. Your pipeline is.
Translate OWASP + NIST into code-level checks
Here’s how I map high-level guidance into stuff that actually runs on every PR.
- Input validation and output encoding: treat new endpoints as hostile. Require explicit validation.
-
Secrets handling: block merges on leaked tokens,
.envfiles, or debug logs containing credentials. - Dependency trust: every new dependency is a new threat actor.
- Authn/authz paths: require tests for role checks and failure modes.
- Logging and PII: AI-written debug logs are notorious for being too chatty.
If you’re building AI agents or shipping AI in production, also account for prompt injection and tool misuse. The agent writes code. The agent can also be attacked through your repo context.
- For repo-level threats, see my guide on prompt injection.
- For agent security posture more broadly, start at AI security.
Vendor guidance still matters (but don’t outsource judgment)
GitHub’s docs on Copilot’s responsible use are worth reading because they’re unusually direct about limitations and the need for human review.
I treat vendor guidance as minimum viable hygiene. Your org still needs policy.
At Rise People, when I built a SOC 2 scaffolding CLI that got adopted org-wide, the lesson was blunt: baking controls into scaffolding beats relying on PR-time review. The same logic applies to AI-assisted coding. Put the guardrails in the default workflow, not in someone’s head.
How to integrate checks into CI/CD (gates before merge)
This is the part most posts conveniently skip. They’ll say “add CI checks,” then bail right before telling you what to run and what to fail on.
Here’s a practical gate stack that specifically targets hallucinated API detection in CI, insecure patterns, and supply chain risk.
Gate 1: compile/typecheck + dependency resolution (hallucinated API killer)
This is your cheapest, highest-signal gate.
- TypeScript:
tsc --noEmitpluspnpm install --frozen-lockfile - Go:
go test ./...plusgo mod tidycheck - Python:
uv pip install(orpip) pluspyright/mypydepending on your posture
The goal is mechanical. If the agent referenced a non-existent symbol, package, or type, it should fail before review.
A rule I use:
- If a PR can’t pass a clean build in under 10 minutes, it’s too big for an AI-generated diff unless it’s been decomposed.
Gate 2: “unknown method” contract tests for internal APIs
Hallucinated APIs aren’t only external. They show up as made-up methods on internal clients.
Two patterns that work:
- Schema validation: if you have OpenAPI/JSON Schema, validate requests/responses.
- Mock that fails on unknown methods: configure test doubles so an unrecognized method call throws immediately.
This is especially valuable in dynamic languages where the compiler won’t save you.
Gate 3: SAST + dependency audit + SBOM
You don’t need to boil the ocean. You need consistent enforcement.
Minimum:
- SAST (language-dependent)
- dependency vulnerability scan (block on critical/high)
- SBOM generation
If you’re already moving toward provenance, align with SLSA-style thinking. In 2026, supply chain guarantees aren’t just for “big tech.” They’re becoming table stakes for regulated industries.
If you want a practical adjacent blueprint, my Rust reproducible builds + SBOM + signed artifacts post goes deep on how to make provenance concrete.
Gate 4: secret scanning (hard fail)
Secret scanning should be a hard fail because it’s too late after merge. Don’t negotiate with entropy.
If you need a setup walkthrough: gitleaks + pre-commit + CI.
Gate 5: fuzz targets for input-handling (selective, high ROI)
Add fuzzing where AI tends to under-test:
- string parsing
- file processing
- protocol decoding
You don’t need to fuzz your whole repo. Pick the modules where a crash is a CVE.
A tiny GitHub Actions skeleton (no fluff)
I’m keeping this short on purpose. You can expand it per language.
- build/typecheck
- tests
- dependency audit
- secrets scan
This is the shape that matters.
How to review AI-generated code in pull requests (without burning your team)
A PR review process that worked in 2022 falls apart under a 2026 agent diff.
Here’s what I enforce:
- Diff size budget: if an agent opened a PR touching 50+ files, it’s almost always a decomposition failure.
- Explain in plain English: the author must describe the change in 5–10 sentences and name the trust boundaries.
- Call out generated code explicitly: reviewers should know which parts were AI-authored, because it changes how skeptical you should be.
- Require tests before deep review: no green build, no human time.
- Ownership clarity: every new module needs an obvious owning team or it’s dead on arrival.
If your team is drowning in PR volume, workflow policy matters as much as the rubric. I wrote a broader take in AI coding team workflow policy and a tactical one in review AI-generated code checklist.
Does AI-generated code increase technical debt? (and how to estimate rewrite likelihood)
Yes. AI-generated code increases technical debt when it increases uncertainty: unclear intent, unclear invariants, unclear ownership.
The trap is that it often looks “complete.” That completeness creates false confidence, and false confidence is how debt gets funded.
What metrics measure code maintainability (that actually help managers)
I like metrics that correlate with future cost, not engineer pride.
- Churn: files with high change frequency. If your AI diff lands in a high-churn zone, your rewrite likelihood jumps.
- Coupling: number of modules/services touched. A feature that touches 6 subsystems is a coordination tax.
- Complexity hotspots: cyclomatic complexity on critical paths. If complexity spikes by 20%+ in a PR, you should feel it.
- Bus factor: if only one person understands the new subsystem, the debt is immediate.
The rewrite likelihood sub-score (20 points)
I score rewrite likelihood by asking five questions. Each is 0–4 points:
- Cohesion: does this code do one thing, or is it a junk drawer?
- Coupling: how many other components does it reach into?
- Ownership: who will maintain it in 6 months?
- Change fit: does it match the existing architecture, or invent a new one?
- Test signal: do tests describe intent or just assert outputs?
If you score under 12/20 here, I treat it as a rewrite candidate unless the feature is tiny.
For the deep version of this idea applied to vibe-coded systems, see vibe coding tech debt audit.
Ship/refactor/rewrite decision matrix
- Ship if: score ≥ 85, no hard fails, diff fits existing architecture.
- Refactor if: score 70–84, tests exist but maintainability is noisy.
- Rewrite if: score < 70, or rewrite likelihood is low, or the diff created a new subsystem without a clear reason.
If you want my broader philosophy on why rewrites from scratch are usually a trap, that’s here: software rewrite from scratch fallacy.
Adapt the rubric by language (TypeScript, Go, Python) and stack (backend vs frontend)
The rubric stays the same. The signals change.
TypeScript (frontend or Node backend)
- Hallucinated APIs show up as type errors. Make
tsc --noEmitnon-negotiable. - Watch for overuse of
anyoras unknown as. If the diff introduces 10+ new casts, you’re buying runtime bugs. - Frontend-specific: accessibility regressions. If the agent touched UI, require at least one a11y check (even if it’s just Axe in CI).
If you’re deep in TS tooling, my TypeScript 7 native compiler benchmark is relevant to keeping typecheck fast enough to be a gate.
Go (backend)
- Compile/test gates are strong.
go test ./...catches a lot of hallucinations. - Concurrency bugs are still human territory. Require explicit tests around goroutines/channels when changed.
- Dependency additions matter. A “small” PR that adds 2 new modules is a smell.
If you’re upgrading toolchains, see Go 1.27 upgrade guide.
Python (backend / ML glue)
- Dynamic runtime means hallucinated APIs can sneak through. Use
pyright(ormypy) to make it more like TypeScript. - Pin dependencies and lock them. Agents love “pip install whatever.” Don’t let them.
- Tests must cover runtime behavior. Python makes it easy to accidentally rely on monkeypatch magic.
If you’re standardizing Python packaging, uv workspace monorepo is the cleanest baseline I’ve found.
Backend vs frontend: different blast radii
- Backend: prioritize correctness, security posture, and failure modes.
- Frontend: prioritize maintainability, accessibility, and performance regressions.
The rubric weights usually hold, but on a payments backend I’ll bump security to 35 and shrink maintainability.
My prediction for the rest of 2026
Teams that win on AI-generated code quality won’t be the ones with the fanciest model. They’ll be the ones with the most boring, enforceable gates.
If you’re serious, do this next week: add the 0–100 rubric to your PR template, pick 3 hard-fail CI gates (build/typecheck, dependency audit, secret scan), and enforce a diff size budget.
The uncomfortable truth is that in 2026, “move fast” doesn’t mean writing code fast. It means catching bad code before it becomes architecture. Your CI is the last adult in the room. Make it count.
Originally published on kunalganglani.com
Top comments (0)