Performance is a feature, but "let's be faster" is not a plan. In a SaaS product where dashboards grow every sprint, speed erodes one innocent dependency at a time. The fix isn't heroics, it's a budget you enforce in CI so regressions get caught before they ship. Here's how I set them.
Budget the metrics users feel
Start with Core Web Vitals, because Google ranks on them and users feel them:
- LCP (Largest Contentful Paint), under 2.5s. The main content, not a spinner.
- INP (Interaction to Next Paint), under 200ms. This replaced FID and is the real measure of "does the app feel snappy?"
- CLS (Cumulative Layout Shift), under 0.1. Reserve space for images and async content.
These are field targets. Measure them with real-user monitoring (RUM), not just a lab run on your fast laptop.
Budget the bytes that cause them
Vitals are outcomes; bytes are the input you control. Set hard limits on the initial payload:
- JavaScript (initial route): ~170kb gzipped is a sane ceiling for an interactive app.
- CSS: keep it lean, Tailwind's purge should leave you well under 30kb.
- Images: serve modern formats (AVIF/WebP), size them for the layout, and lazy-load below the fold.
Enforce it in CI, or it won't happen
A budget nobody checks is a wish. Wire it into the pipeline so a regression fails the build. Two layers work well.
Bundle size gate with size-limit:
{
"size-limit": [
{ "path": ".next/static/chunks/main-*.js", "limit": "170 kB" }
]
}
Lighthouse CI for the vitals, asserting on the numbers:
// lighthouserc.js
module.exports = {
ci: {
assert: {
assertions: {
"largest-contentful-paint": ["error", { maxNumericValue: 2500 }],
"cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }],
},
},
},
};
Now "it got slower" is a red check, not a vibe someone raises three releases later.
The habits that keep budgets green
- Code-split by route and lazy-load heavy, rarely-used surfaces (charts, editors, modals).
- Ship less JavaScript. Prefer Server Components and server-side data fetching where the framework allows.
- Audit dependencies before adding them. A date library can cost more than the feature it enables, check the gzipped size first.
- Defer the non-critical. Analytics, chat widgets, and feature flags should never block first paint.
- Watch the p75, not the average. Averages hide your slowest users; the 75th percentile is what Google reports and what real people experience.
Make it cultural
The tooling is the easy 20%. The other 80% is treating a failed budget like a failed test, something you fix, not override. Put the numbers in the PR template. Celebrate when someone deletes a dependency. Once the team internalizes that fast is the baseline and slow needs justification, the budget stops being a gate and becomes how you build.
Fast products feel trustworthy. In SaaS, that trust compounds into retention, which is why performance is never just an engineering concern.
- #Performance
- #Core Web Vitals
- #SaaS
- #Web Development
- #CI