Back to Blog
The CI/CD Standard: Automated Quality Assurance for Rapid Deployment
Photo from Unsplash

Article content

The Deploy You Don't Have to Think About

The sign of a mature CI/CD pipeline isn't that it's fast. It's that you stop thinking about it. You push a branch, open a PR, and by the time you've written the description the automated checks have already run. Linting passed. Types passed. Tests passed. Lighthouse score held. You don't review any of that manually; the pipeline is the reviewer for the things that can be automated.

That's the target. Here's the anatomy of how to get there.

The Anatomy of a High-Performance Pipeline

1. Automated Linting & Type Checking

This is the first line of defense. By enforcing strict TypeScript checks and ESLint rules (like the ones in this repository), we ensure that code is syntactically correct and adheres to team standards before it is even built.

2. Unit & Integration Testing

Using frameworks like Vitest or Jest, we verify that individual functions and components behave as expected. The distinction that matters in practice: unit tests should cover pure logic (a price calculation, a date formatter, a validation function) where mocking is cheap and the test runs in milliseconds. Integration tests, which render a component tree and simulate real interaction with something like Testing Library, are slower and more brittle, so reserve them for the flows that actually break in production: form submission, cart updates, auth redirects. A codebase with hundreds of shallow unit tests and zero integration tests will still ship a broken checkout flow, because the unit tests never exercised the seam where two correct pieces combine incorrectly.

AI agents are increasingly useful here, but not as a replacement for judgment: ask an agent to generate edge-case inputs for a function you've already written (negative numbers, empty strings, unicode, off-by-one boundaries), review what it produces, then commit only the cases that would have actually caught a real bug. Blindly accepting a generated test suite tends to produce high coverage numbers with low bug-catching value, because the agent writes tests that match the implementation rather than the specification.

3. Visual Regression Testing

Tools like Playwright or Percy take screenshots of your UI and compare them against a baseline. If a CSS change in the footer accidentally breaks the navigation menu, the pipeline fails, preventing a broken UI from going live.

The failure mode teams hit first is flakiness from non-deterministic rendering: a loading spinner mid-animation, a randomly-ordered list, a timestamp in the UI. Freeze these before the screenshot: mock Date.now() to a fixed value, disable CSS animations in the test environment, and sort any list that doesn't have a stable natural order. A visual regression suite that fails 5% of the time on unrelated changes gets ignored within a month, and an ignored gate is worse than no gate, because it gives false confidence.

4. Automated QA Gates

Final stage checks (Lighthouse CI, bundle size budgets) ensure performance and accessibility scores haven't regressed below your defined thresholds before anything merges. Set the budget as a hard number, not a vague aspiration: "LCP must stay under 2.5s on the product page" is enforceable in CI, "the site should feel fast" is not. Bundle size budgets work the same way, fail the build if a route's JavaScript exceeds a set threshold (250KB gzipped is a reasonable starting point for most content sites), and require an explicit override comment in the PR if someone genuinely needs to exceed it.

Here's a minimal GitHub Actions workflow that covers all four stages for a Next.js project:

# .github/workflows/ci.yml
name: CI
 
on:
  push:
    branches: [main, staging]
  pull_request:
 
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'yarn'
 
      - run: yarn install --frozen-lockfile
 
      # Stage 1 — Lint & type check
      - run: yarn lint
      - run: yarn tsc --noEmit
 
      # Stage 2 — Unit & integration tests
      - run: yarn test --run
 
      # Stage 3 — Build (catches SSR errors)
      - run: yarn build
 
      # Stage 4 — Lighthouse CI (requires LHCI config)
      - uses: treosh/lighthouse-ci-action@v11
        with:
          uploadArtifacts: true
          temporaryPublicStorage: true

The build step is worth calling out specifically: it catches SSR errors that unit tests miss. Many teams skip it in CI because it's slow, and then discover the production build is broken on deploy. A component that reads window at module scope, for example, passes every unit test (which runs in a DOM-shimmed environment) and fails only when Next.js actually tries to server-render it. If you've hit a hydration error in production that never showed up locally, that gap between "tests passed" and "build succeeded" is usually where it lived. See debugging hydration and SSR issues with AI for the specific patterns that cause this class of bug.

Staging the Gates by Speed

Not every check belongs at every stage. Ordering the pipeline from fastest to slowest saves real CI minutes, because a fast failure means the slow stages never run: lint and type-check first (seconds), unit tests second (tens of seconds), the build third (a minute or two), then Playwright and Lighthouse last (the slowest stages, often two to five minutes). If a PR fails linting, there's no reason to wait for a full Lighthouse run to tell the author. Most CI providers support running independent stages in parallel once the fast gates pass, which keeps wall-clock time down without weakening any individual check.

What "No Manual Sign-Off" Actually Requires

The phrase implies more trust in the pipeline than most teams have earned yet. Getting there requires two things beyond the four stages above: a flaky-test policy (a test that fails intermittently gets quarantined and fixed within a sprint, not silenced with a retry loop that hides the underlying race condition) and branch protection rules that actually block merges on failing checks, not just display a red X that reviewers learn to ignore. Accessibility checks belong in this gate too. Running an automated a11y scanner (axe-core integrates directly into Playwright) catches missing alt text and contrast failures the same way Lighthouse catches performance regressions; see using AI for WCAG accessibility compliance for how to combine automated scanning with the manual review it can't replace.

Conclusion

Pick the stage that's currently missing from your pipeline and add it this week. If you have no automated tests at all, start with TypeScript strict mode and ESLint: they catch a surprisingly large class of bugs before runtime, cheaply. If you have tests but no Lighthouse budget, add that next, with a hard numeric threshold, not a vague target. The pipeline compounds: each new gate catches a class of issue so nobody has to catch it manually in a code review, or worse, in production.


Sources & References

  • GitHub Actions Documentation — official reference for workflow syntax and CI configuration
  • Playwright Documentation: Visual Comparisons — screenshot-based regression testing setup and API
  • "Continuous Delivery" by Jez Humble & David Farley — the foundational text on deployment pipeline design
  • web.dev: Lighthouse CI — Google's guide to running Lighthouse audits as an automated CI gate
  • axe-core (Deque Systems) — open-source accessibility testing engine used by most automated a11y scanners
Newer Post

Figma Dev Mode: Closing the Gap Between Design and Code

Older Post

Bridge the Gap: Tools for Zero-Latency Local Development

Suggested Reading

Architectural Note: Research, drafting, and code for this post were augmented by Gemini (Google DeepMind), directed and verified by Maas Mirzaa. How this workflow works →