CI/CD quality gates, sometimes written CICD gates, are automated checks wired into your continuous integration pipeline that fail a pull request the moment generated code violates a defined standard, before a human reviewer ever looks at the diff. CI/CD quality gates exist because AI-generated TypeScript code doesn't look obviously wrong. It follows patterns, uses the right library APIs, compiles. That's exactly the problem. A gate only helps if it actually inspects the files you think it does, which is what the lint coverage audit measures.
What it also does: introduce any types when inference gets hard, skip the error case in unit tests, generate as SomeType casts instead of proper validation, and assume array indexes are always safe. None of this fails a casual review. All of it causes production bugs. I've watched it happen on real projects, fast AI output followed by slow debugging.
A CI pipeline that treats AI commits exactly like human commits, no exceptions, is the only reliable defense. Not code review, not pair programming, not "being careful." Mechanical gates that run on every push.
This is the GitHub Actions workflow I use. Four gates, roughly 30 lines of YAML total.
TL;DR:
- Gate 1:
tsc --noEmitcatchesanytypes and unsafe casts- Gate 2: ESLint with
--max-warnings 0makes lint violations build-breaking- Gate 3: Vitest coverage threshold (80%) forces edge case tests
- Gate 4: CodeRabbit on every PR catches design issues the mechanical gates miss
For the broader quality strategy these gates fit into, see Vibe Coding TypeScript.
Quick take: AI-generated TypeScript code carries 1.7x more defects than human-written code, per CodeScene's research. Four mechanical GitHub Actions gates catch most of it before merge: tsc --noEmit with noUncheckedIndexedAccess, ESLint at --max-warnings 0, an 80% Vitest coverage floor, and CodeRabbit for judgment calls. This pipeline catches something broken on roughly one in four AI-generated pull requests.
Why Does AI Code Need Automated Gates?
CodeScene's 2026 research found that AI-generated code carries 1.7x more defects than human-written code, with type-related errors making up the majority. That's not a knock on the tools, it's a structural reality. AI models predict plausible tokens. They don't run your code mentally and verify edge cases.
Speed makes this worse, not better. A developer who writes 10 lines slowly is also thinking about error handling. A developer who reviews 200 AI-generated lines is pattern-matching, not auditing. The defects that slip through are exactly the ones that look fine on a quick scan: a missing null check, a function typed as any, a test that never calls the failure branch.
Automated gates don't get tired. They don't skim. They run the same checks on every push regardless of who wrote the code or how much coffee you've had.
Gate 1, What Does the TypeScript Strict Compile Check Catch?
The fastest gate is also the most effective. tsc --noEmit runs the TypeScript compiler over your entire codebase without emitting output files. No setup beyond a tsconfig.json. (TypeScript Compiler Options, 2024)
In GitHub Actions:
- name: TypeScript check
run: npx tsc --noEmit
This catches any types, unsafe type assertions, missing return types, and the as SomeType casts that AI tools generate when they can't infer the correct type. For this gate to do real work, your tsconfig.json needs strict: true plus noUncheckedIndexedAccess:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true
}
}
In my experience, this gate alone eliminates roughly 40% of the type bugs AI tools introduce. noUncheckedIndexedAccess is the one that does the most work against AI-generated code, it forces items[0] to be treated as T | undefined instead of T, which catches the "assume the array isn't empty" pattern AI uses constantly.
See the strict TypeScript config for the full breakdown of what each strict flag catches.
Gate 2, What Do TypeScript-Specific ESLint Rules Catch?
tsc checks types. ESLint catches patterns that compile fine but signal AI shortcuts. The critical flag here is --max-warnings 0: warnings become errors in CI. No exceptions. (GitHub Actions Documentation, 2024)
- name: ESLint
run: npx eslint . --max-warnings 0
Three rules do the most work against AI-generated TypeScript. Add these to your .eslintrc:
{
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/explicit-function-return-type": "error"
}
}
Here's the opinionated take: @typescript-eslint/no-explicit-any should be "error", not "warn". AI tools use any the moment a stricter type requires more reasoning. A warning lets it accumulate across 50 files before someone notices. An error means the PR doesn't merge. That's the only version of this rule that works.
no-non-null-assertion blocks the ! operator. explicit-function-return-type forces every function to declare what it returns. AI tools skip return types constantly, this rule makes that a build failure.
Gate 3, Why Set a Test Coverage Threshold?
AI coding tools generate tests. That's not the same as generating good tests. What they generate is happy-path coverage: call the function with valid inputs, assert it returns the expected output. The error cases, null input, network failure, empty array, malformed data, don't exist. If you're specifically testing AI-generated React components, the React component testing strategies guide covers the patterns that catch the gaps coverage thresholds alone won't expose.
Vitest's coverage thresholds fix this indirectly. Set a minimum and the build fails when coverage drops below it:
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
thresholds: {
lines: 80,
functions: 80,
branches: 75,
},
include: ['src/**/*.ts'],
exclude: ['src/**/*.d.ts'],
},
},
});
GitHub Actions step:
- name: Test coverage
run: npx vitest run --coverage
80% line coverage is a reasonable floor. It's not the number that matters, it's what happens when the number drops. The developer (or AI tool) has to write the missing tests. That's when the error paths get covered.
Is 80% the right threshold for every project? Probably not. But it's a defensible starting point, and you can raise it once the baseline is established.
How do you roll a new coverage threshold into an existing repo without breaking every open PR overnight? Follow this order.
- Run
npx vitest run --coverageonce againstmainto record the current baseline, don't guess at the starting number. - Set the threshold a few points below that baseline so the gate passes today and only blocks regressions, not the entire history.
- Raise the threshold by 5 points every couple of weeks until you reach 80%, giving the team time to backfill tests gradually.
- Once at 80%, treat any further increase as a deliberate team decision, not a default you bump just because you can.
Gate 4, What Does Automated AI Code Review Catch?
The first three gates are mechanical. They catch specific patterns with no judgment. This gate is different, it reads the code and flags design-level issues. For TypeScript projects on GitHub, CodeRabbit setup is the fastest path to get this running.
A minimal .coderabbit.yaml that focuses on TypeScript-specific patterns:
reviews:
profile: "assertive"
instructions: |
Flag any use of `any` type or non-null assertion operators.
Check that functions have explicit return types.
Flag tests that don't include at least one assertion on an error path.
Note missing input validation on function parameters that accept external data.
This gate runs on PRs only, not on every push. It's qualitative, not a hard pass/fail, but it surfaces the issues the compiler can't: a function that technically type-checks but has no input validation, a test that covers the success path but assumes the happy path is all that matters.
I've found CodeRabbit catches around 1 in 4 AI PRs doing something that passes all three mechanical gates but is still wrong. Usually it's a missing null check on external data, or a test that only runs with valid inputs.
The Four Gates at a Glance
| Gate | What it catches | Runs on |
|---|---|---|
| 1. Strict compile check | Type errors, implicit any | Every push |
| 2. TypeScript-specific ESLint rules | Unsafe patterns the compiler allows | Every push |
| 3. Test coverage threshold | Untested code paths | Every push |
| 4. Automated AI code review | Design-level issues: missing null checks, happy-path-only tests | PRs only, qualitative not pass/fail |
How Do You Put the Full Workflow Together?
All four gates as a single GitHub Actions workflow. Save this as .github/workflows/quality.yml:
name: Quality Gates
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: TypeScript check
run: npx tsc --noEmit
lint:
runs-on: ubuntu-latest
needs: typecheck
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: ESLint
run: npx eslint . --max-warnings 0
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Test coverage
run: npx vitest run --coverage
The jobs run in sequence: typecheck first, then lint, then test. Set up branch protection in GitHub Settings > Branches to require all three to pass before merge. CodeRabbit runs automatically on PRs once installed, no additional workflow configuration needed.
The whole thing is about 40 lines of YAML. It runs in under 2 minutes on a typical TypeScript project.
These gates don't slow down AI-assisted development, they make it sustainable. The alternative is accumulated technical debt: any types spreading through the codebase, untested error paths waiting to fail in production, and type assertions that mask real bugs. No AI tool reverses that easily once it's established. A 2-minute CI check on every push does.
Related Tutorials
- AI Code Review for TypeScript: CodeRabbit + GitHub Actions
- AI coding tools 2026
- Cursor AI TypeScript tips