AI assistants changed the volume of code a team produces. Copilot, Cursor, and Claude ship finished diffs in minutes, and human review keeps the same attention window as before. The quality gate is the mechanism that holds the project standard when volume passes what review can reach.
An assistant does not know the business rule that lives only in the team's Slack, the folder structure the project adopted, nor the security team's sanitization policy. Generated code passes lint, passes accelerated review, and the defect reaches production weeks later, far from the diff that caused it.
What goes into a quality gate
A quality gate is an objective criterion the code has to satisfy before advancing in the pipeline. The SonarQube quality gates manual defines the coverage-on-new-code condition, a shape that does not punish the legacy code. The list I set up for a JavaScript and TypeScript project:
- Coverage on new code. An 80% floor on diff lines, excluding test files.
- Lint and typecheck with zero errors. ESLint, PHPStan, Pylint,
tsc --noEmit. - Security static analysis. Semgrep with an OWASP ruleset or the equivalent. The Semgrep documentation covers writing your own rules and using ready-made rulesets.
- Dependency vulnerabilities.
npm audit, Dependabot, or Snyk. - Type safety. TypeScript on
strict, with a count ofanyin new code at zero. - Complexity per function. The ESLint
complexityrule at 10 catches the AI function that wraps theifinside the loop inside thetry.
// .eslintrc.json
{
"rules": {
"complexity": ["error", 10],
"max-lines-per-function": ["warn", 50]
}
}
Three calibrations once AI enters the flow
A gate that passed with three PRs a week fails when the same team opens fifteen. Three adjustments I make when a team turns the assistant on:
1. Coverage measured on the diff
Requiring 80% across the whole project stalls the legacy code and breeds resistance. The new-code condition catches the code the AI wrote in this diff.
# sonar-project.properties
sonar.coverage.exclusions=**/*.test.*
sonar.qualitygate.wait=true
# gate: coverage on new code >= 80%
2. Short, specific rules
A ten-line Semgrep rule beats a giant ruleset nobody reads. Start with eval(), unsanitized innerHTML, and SQL built by string concatenation.
# .semgrep.yml
rules:
- id: no-eval
pattern: eval(...)
message: "eval() usage detected: code injection risk"
severity: ERROR
3. A gate that blocks the merge
qualitygate.wait=true on the SonarQube scan makes the job fail when the condition does not pass, and branch protection requires the green job before merge. A gate that only posts alerts disappears after two weeks of heavy use.
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.qualitygate.wait=true
What the gate unlocks
A blocking gate changes the team's contract with the assistant. An AI-generated PR goes through the same approval flow as a human PR, with the compiler and the analyzer answering before the reviewer. Juniors using an assistant get the same standard feedback as seniors, because the gate does not distinguish who wrote the code.
A team that measures PR cycle time finds that a fast gate (lint, typecheck, unit test) stops rework within the first minute of CI, and a slow gate (full scan, E2E) can run only on main or on the large PR. With that, the assistant works at its own pace and the project standard survives the volume.
Enjoyed this content?
I build web products and AI solutions the right way — solid architecture, maintainable code, and real delivery.
Let's talk