Back to Blog
PerformanceJune 25, 20264 min read

Performance Budgets: Automating Regression Testing with Lighthouse CI

Performance budgets are critical for keeping your site fast. Learn how to automate regression testing using Lighthouse CI and GitHub Actions to catch bloat.

performanceweb performancelighthousegithub actionsautomationcicddevopsWeb VitalsFrontend

We’ve all been there: a minor feature request lands, the PR gets merged, and suddenly your LCP jumps by 400ms. It’s the classic "death by a thousand cuts" scenario, where individual commits seem harmless until the aggregate weight crushes your site's performance.

I used to rely on manual audits, but that’s a fool’s errand. If you aren't automating your checks, you’re just hoping for the best. To actually maintain a fast site, you need to treat performance as a first-class citizen in your CI/CD pipeline.

Why Performance Budgets Matter

A performance budget is your line in the sand. It’s a set of constraints—like bundle size, image weight, or Core Web Vitals—that your application cannot exceed. When you define these, you move from "we should be faster" to "this PR cannot be merged because it exceeds our 250kb JS budget."

Setting these up isn't just about speed; it's about team culture. When developers see a red checkmark in GitHub because they added a heavy library, they learn to optimize early. It beats refactoring a bloated codebase three months later.

If you're already optimizing your main application, you might want to look into how Performance budgets: Enforcing Bundle Size and Vital Thresholds provide the foundational logic for these constraints.

Setting Up Lighthouse CI

Lighthouse CI is the gold standard for this. It runs the same audits you see in Chrome DevTools but does so in an isolated, repeatable environment.

First, install the CLI:

Bash
npm install -g @lhci/cli

You’ll need a lighthouserc.js file in your root. This is where you define your thresholds. Don't go overboard; start with what you can actually control, like Total Blocking Time (TBT) or specific asset sizes.

JAVASCRIPT
module.exports = {
  ci: {
    assert: {
      preset: CE9178">'lighthouse:recommended',
      assertions: {
        CE9178">'categories:performance': [CE9178">'error', {minScore: 0.9}],
        CE9178">'total-byte-weight': [CE9178">'error', {maxNumericValue: 500000}],
      },
    },
  },
};

Integrating with GitHub Actions

This is where the magic happens. By adding a workflow file in .github/workflows/lighthouse.yml, you trigger these audits on every pull request. We’ve found that running this on a staging build is the most reliable way to get accurate numbers.

YAML
jobs:
  lhci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm install && npm run build
      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v9
        with:
          configPath: './lighthouserc.js'

When this runs, the action will comment on your PR with a summary of the audit. If a number dips below your budget, the build fails. It’s blunt, effective, and keeps everyone honest.

Lessons from the Field

We once tried setting an aggressive 100ms budget for INP (Interaction to Next Paint) across all devices. It failed constantly because our staging environment’s network throttling was inconsistent. We had to dial it back and focus on the "p95" performance metrics rather than absolute perfection.

Also, don't forget that performance isn't just about the initial load. If you're building complex data-heavy apps, you might need to handle intense server-side logic effectively, which is why I often suggest checking out Next.js Server Actions: Implementing Synthetic Monitoring and Backpressure to ensure your backend isn't bottlenecking your frontend speed.

FAQ: Common Performance Budget Pitfalls

Should I block merges for every minor regression? No. Start with "warnings" in your CI logs. Once the team gets used to the metrics, flip the switch to "error" to block merges. If you start by breaking everyone's workflow, they'll just disable the tool.

How do I handle third-party scripts? Third-party code is the biggest performance killer. If a marketing script bloats your bundle, you need a process to audit its necessity. Sometimes, you have to exclude these from your strict budgets, but be honest about why.

What happens if the budget is met, but the site still feels slow? Budgets are guardrails, not a total solution. They won't catch bad UX patterns or slow API response times. Use them in tandem with Real User Monitoring (RUM) to get the full picture.

Wrapping Up

Automating your performance budgets is a journey. You’ll start with a few basic checks, realize your infrastructure is noisy, and eventually build a robust system that catches regressions before they hit your users.

Don't treat this as a "set it and forget it" task. Revisit your budgets every quarter. As your app grows, your constraints should evolve. What I’m still figuring out is how to better handle flaky tests in CI—sometimes the network jitter ruins a perfectly good build, and that’s a frustration I haven't fully solved yet.

Similar Posts