In my previous post on prompt engineering, we explored how to communicate effectively with AI coding assistants to get better, more production-ready code. We learned that well-crafted prompts can turn an LLM into a powerful pair programmer. But there's a crucial piece missing from that picture: what happens after the AI generates the code? Even the most carefully prompted AI can hallucinate APIs, introduce subtle bugs, or misunderstand context. As we lean harder into AI-assisted development, we're experiencing a profound shift in how software gets built—and we need new guardrails to match.
The industry is living through a remarkable transformation. AI coding assistants have effectively commoditized certain aspects of software engineering. Tasks that once required hours of boilerplate writing—generating REST endpoints, writing unit tests, creating data models—now take seconds. Junior-level implementation work is being automated at scale. GitHub reports that developers using AI tools are 55% more productive, and some teams are shipping features 2-3x faster. This is incredible progress, but it comes with a critical caveat: AI is probabilistic, not deterministic. Unlike traditional code generation tools, LLMs don't execute logic—they predict the most likely next tokens based on patterns. This means they can confidently suggest code that looks right but contains subtle flaws, uses deprecated APIs, or simply hallucinates functions that don't exist.
This is where CI/CD pipelines become absolutely essential. When human developers were the bottleneck, continuous integration caught our occasional mistakes. Now that AI can generate code at 10x speed, CI/CD transforms from a nice-to-have into a mandatory safety net. Think of it this way: AI agents give you a race car, and CI/CD is your seatbelt, airbag, and crash testing facility rolled into one. You want to go fast, but you need guardrails that automatically catch issues before they reach production. In this post, we'll explore why AI needs guardrails, how to set up effective CI/CD pipelines, and how to build a workflow that lets you move quickly while maintaining system reliability.
Why AI Agents Need Guardrails
AI coding assistants are remarkably capable, but they're fundamentally different from traditional tooling. When you use a compiler or a linter, you get deterministic results—the same input always produces the same output. LLMs, however, operate on probability distributions. They generate code based on what "looks like" correct code from their training data. This introduces several failure modes that traditional development workflows weren't designed to handle:
Hallucinations: LLMs can confidently invent APIs, libraries, or function signatures that don't exist. For example, an AI might suggest using pandas.read_csv_streaming() because it understands you need streaming functionality, even though that specific method doesn't exist in pandas. The code looks plausible, passes a quick visual inspection, but fails at runtime.
Context Drift: As conversations with AI get longer or you prompt across multiple sessions, the model can lose track of your project's architecture, dependencies, or conventions. It might suggest a solution that worked in a different context but breaks assumptions in your codebase—like using a synchronous API in an async function or importing from the wrong module version.
Outdated Patterns: LLMs are trained on historical code, which means they sometimes suggest deprecated approaches. An AI might recommend using React class components with lifecycle methods when your team has standardized on functional components with hooks, or it might use an older authentication pattern that has known security vulnerabilities.
Overfitting to the Prompt: Sometimes the AI optimizes for your specific request but misses the bigger picture. If you ask it to "make this function faster," it might remove input validation or error handling to achieve better performance, inadvertently introducing bugs.
These issues don't mean AI is unreliable—far from it. They mean AI outputs need verification. This is exactly what CI/CD pipelines excel at: automated, consistent verification at scale. Every time an AI suggests code, your pipeline should automatically run tests, linters, security scans, and integration checks. If the AI hallucinates an API or breaks existing functionality, the CI pipeline catches it immediately—before the code reaches production, and often before a human even reviews it.
The key insight is this: AI agents let you write code faster than ever, but CI/CD ensures you're not accumulating technical debt faster than ever too. By automating quality checks, you create a tight feedback loop where AI can iterate rapidly, and bad suggestions get filtered out automatically. This is how you achieve both velocity and reliability.
Setting Up GitHub Actions for AI-Generated Code
Let's make this concrete. If you're using AI to help write code, here's how you set up a robust CI/CD pipeline using GitHub Actions. The goal is to create a system where every PR—whether written by a human or assisted by AI—goes through the same rigorous automated checks.
Basic CI Pipeline
Here's a simple but effective GitHub Actions workflow that runs tests on every push and pull request:
# .github/workflows/test.yml
name: Test CI
on:
push:
branches: [ "main", "master" ]
pull_request:
branches: [ "main", "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Run linter
run: npm run lint
This workflow does several important things:
- Triggers automatically on every push and PR to your main branches
- Tests across multiple Node versions (using a matrix strategy), catching compatibility issues
- Caches dependencies to speed up subsequent runs
- Runs both tests and linters, ensuring code quality and style consistency
Why This Matters for AI Code
When an AI generates code, this pipeline acts as an immediate quality gate. Let's say you prompt an AI: "Add a function to process user uploads." The AI generates the code and you commit it. Before that code can be merged:
- Tests verify behavior: If the AI misunderstood requirements or introduced a bug, your existing test suite catches it
- Linters enforce standards: If the AI used inconsistent formatting or violated team conventions, the linter fails the build
- Multi-version testing catches edge cases: If the AI used a feature only available in Node 22, but you support Node 20, the matrix build reveals the issue
This is exactly the workflow I use on this blog. Every PR runs tests across Node 20.x and 22.x. For example, in PR #94, the CI pipeline validated that SEO improvements didn't break any existing functionality. The tests are currently unit tests—they verify individual components and functions work correctly in isolation.
Beyond Unit Tests: The Full Testing Pyramid
Now, here's the critical point: unit tests alone aren't enough for production systems. In my 2018 post on testing with Jest, I introduced the testing pyramid—a framework for thinking about test coverage:
/\
/ \ Manual/E2E Tests (few, expensive, broad coverage)
/____\
/ \
/ \ Integration Tests (moderate, test component interactions)
/__________\
/ \
/ \ Unit Tests (many, fast, narrow focus)
/________________\
For this blog (a static site), unit tests are sufficient. But in real-world applications—especially those involving AI-generated code—you need the full pyramid:
-
Unit Tests (what we have now): Fast, focused tests that verify individual functions and components. Great for catching logic errors and regressions.
-
Integration Tests (what you should add): Tests that verify how multiple components work together. For example, if your AI generated a new API endpoint, integration tests would verify it correctly interacts with your database, authentication middleware, and response formatting.
-
Browser/E2E Tests (critical for user-facing apps): Automated browser tests using tools like Playwright or Cypress that simulate real user interactions. These catch issues that only appear when JavaScript runs in an actual browser, CSS loads, and APIs are called in sequence. If an AI-generated React component looks fine in isolation but breaks the checkout flow, E2E tests catch it.
Here's a more complete CI pipeline for a production app:
name: Full Test Suite
on: [push, pull_request]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npm test
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npm run test:integration
e2e-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-screenshots
path: test-results/
This pipeline runs three layers of tests in parallel, dramatically increasing confidence that AI-generated code works correctly. If an AI hallucinates an API call or introduces a subtle UI bug, one of these test layers will catch it.
Best Practices: Building a Reliable AI + CI/CD Workflow
Based on experience working with AI-assisted development and robust CI/CD pipelines, here are key practices for making this combination work:
1. Fail Fast, Fail Loud
Configure your CI to fail the build immediately when tests break, and make failures highly visible. Use branch protection rules to prevent merging when CI fails:
# Repository settings → Branches → Branch protection rules
Require status checks to pass before merging
Require branches to be up to date before merging
When AI generates buggy code, you want to know immediately, not after it's been merged and deployed.
2. Add AI-Specific Guardrails
Beyond standard tests, add checks specifically designed to catch AI mistakes:
- Security scanning: Use tools like
npm auditor Snyk to catch vulnerable dependencies AI might suggest - Type checking: If using TypeScript, run
tsc --noEmitto catch type errors (AI often gets types slightly wrong) - API validation: Add tests that verify external API calls match actual API contracts, catching hallucinated endpoints
- Performance budgets: Set thresholds for bundle size, memory usage, or response times—AI sometimes optimizes for functionality while ignoring performance
- name: Security audit
run: npm audit --audit-level=high
- name: Type check
run: npm run type-check
- name: Check bundle size
run: npm run build && npx bundlesize
3. Require Test Coverage
When AI generates new code, it should also generate tests (you can prompt for this explicitly). Enforce minimum coverage thresholds in CI:
- name: Test with coverage
run: npm test -- --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80}}'
If an AI adds a feature without tests, the coverage check fails, forcing you to either write tests yourself or prompt the AI to generate them.
4. Use Preview Deployments
Tools like Vercel, Netlify, or Heroku Review Apps automatically deploy each PR to a temporary URL. This lets you manually verify AI-generated features in a real environment before merging. You can see this in action on my blog's PRs—Vercel automatically deploys every PR for visual review.
5. Iterate with AI on Test Failures
Here's a powerful workflow: when CI fails on AI-generated code, feed the error message back to the AI. For example:
- AI generates code, you commit it
- CI runs, tests fail with specific error messages
- You prompt: "The CI build failed with this error: [paste error]. Fix the code to pass the tests."
- AI analyzes the error, generates a fix
- Repeat until CI passes
This creates a tight feedback loop where the AI learns from actual test failures, not just theoretical requirements.
Embracing Speed with Safety
The combination of AI coding assistants and robust CI/CD pipelines represents a fundamental shift in software development. We're moving from a world where writing code was the bottleneck to one where verifying and integrating code is the bottleneck. AI can generate implementations at incredible speed, but without automated guardrails, that speed becomes recklessness.
By investing in comprehensive CI/CD pipelines—unit tests, integration tests, browser tests, security scans, and all the rest—you create an environment where AI can work at full speed while quality remains consistently high. The pipeline becomes your safety net, catching hallucinations, bugs, and regressions automatically. You get the productivity boost of AI without the risk of accumulating technical debt.
As AI continues to evolve and take on more complex development tasks, the importance of automated testing will only grow. The developers and teams who thrive in this new era won't be those who avoid AI or those who blindly trust it—they'll be the ones who combine AI's speed with engineering discipline enforced through CI/CD.
So if you're using AI to write code, ask yourself: do you have guardrails in place? Are your tests comprehensive enough to catch AI mistakes? Is your CI pipeline fast enough to provide immediate feedback? If not, now's the time to invest. The race car is here—make sure you've got the safety equipment to match.
Want to dive deeper into AI-assisted development? Check out my previous post on Prompt Engineering for Developers, or explore the fundamentals of testing in Using Jest and Enzyme for Testing React Apps. For questions or discussion, feel free to reach out on GitHub.
