Your Lighthouse score on office WiFi is a vanity metric. What matters is how MyAfya Profile loads when a nurse opens it on a 3G connection in a rural health centre — 400kbps, 300ms latency, and a phone that's already running twelve apps.

We target under 900ms to interactive on those conditions. Not because it's a nice number — because anything slower and staff stop waiting. They write on paper instead. Your app loses.

1. Set a performance budget and enforce it

Before a single component ships, we agree on limits:

  • Initial JS bundle: under 150KB gzipped
  • Critical CSS: inlined, under 14KB
  • First meaningful paint: under 1.5s on simulated 3G
  • Total page weight on first load: under 500KB

CI fails if a PR pushes past budget. No exceptions for "we'll optimise later."

// vite.config.ts — bundle size guard
build: {
  rollupOptions: {
    output: { manualChunks: { vendor: ['react', 'react-dom'] } }
  },
  chunkSizeWarningLimit: 150
}

2. Asset discipline

Every image is WebP or SVG. Every font is subset to Latin + the characters you actually use. Hero photos don't belong in admin dashboards — icons do.

We lazy-load below the fold, but never lazy-load what the user needs immediately. The patient search bar loads with the page. The analytics chart loads after.

If you wouldn't wait for it on 3G, your users in Kisumu won't either.

3. Cache aggressively, invalidate carefully

Static assets get immutable cache headers. API responses that rarely change — clinic settings, drug formulary lists — get service worker caching with background refresh. Dynamic data like live queue status hits the network, but the shell renders instantly from cache.

EduBridge uses this pattern for school dashboards accessed on shared tablets. The app feels native even when the connection stutters.

4. Test on real throttled connections

Chrome DevTools "Slow 3G" is a starting point, not the finish line. We test on actual devices with Safaricom and Airtel SIMs. We test during peak hours. We test with low battery mode on — because yes, that throttles the CPU too.

  • WebPageTest from a Nairobi probe location
  • Physical device lab with mid-range Android phones
  • Synthetic monitoring from East African endpoints post-launch

5. Progressive enhancement, not degradation

Core flows work without JavaScript where possible. Forms submit. Links navigate. The enhanced experience — instant search, optimistic updates — layers on top. If the JS bundle fails to load, the app still functions. Barely pretty, fully usable.

What fast feels like

Users don't thank you for performance. They just use the product. Slow apps get abandoned silently. Fast apps become the default tool — and that's the only metric that matters.

Building for markets where 4G is the exception? We optimise for reality, not benchmarks.