Web application performance: principles nobody teaches you
Load times, bundle sizes, Core Web Vitals — these are indicators, not objectives. Understanding the real causes of poor performance and how to measure them correctly is half the battle.
The biggest myth in performance optimization
You migrate to a faster CDN, reduce image sizes, and add lazy loading. The site is still slow. What happened?
You probably fixed the visible problems, not the real problems. Optimization without precise measurement is guessing dressed up as engineering.
Performance levels: a hierarchy you need to understand
Performance has layers. An optimization at the wrong level doesn't help (or makes things worse):
1. The algorithm — O(n²) complexity doesn't become O(n) by adding a cache.
2. The database — An index-less query doing a table scan on 10 million rows won't be fixed by putting Redis in front.
3. Network architecture — N+1 queries with 200ms latency each = 20 seconds for 100 items.
4. Rendering — A 3MB bundle blocking the main thread before showing anything.
5. Caching — Inefficient, but only at the end. Caching doesn't save a wrong architecture.
How to measure correctly
Free tools (Lighthouse, WebPageTest) are excellent, but have limits: they measure synthetic experience, not real user experience.
What to add mandatorily:
- Real User Monitoring (RUM): Google CrUX or a tool like Datadog RUM to see p75/p95 on real users
- Database query profiling: EXPLAIN ANALYZE in PostgreSQL, slow query log in MySQL
- Backend tracing: OpenTelemetry + Jaeger/Zipkin to see where time is lost in requests
- Frontend profiling: React Profiler for components, Chrome DevTools Performance tab for runtime
The classic case: N+1 queries
The most frequent bottleneck in ORM applications:
GET /api/orders
→ SELECT * FROM orders (1 query, 200ms)
→ FOR EACH order: SELECT * FROM users WHERE id = ? (100 queries × 50ms = 5 seconds)
Total: 5.2 seconds for 100 orders
The solution: eager loading or a proper JOIN reduces this to 250ms.
The lesson: no cache helps if you don't find N+1 first.
Core Web Vitals: what actually matters
- LCP (Largest Contentful Paint): Optimize the hero image or the font blocking render
- INP (Interaction to Next Paint): Identify click handlers doing synchronous work on main thread
- CLS (Cumulative Layout Shift): Reserve explicit space for async-loading elements
Don't optimize LCP if INP is 800ms — users feel interactivity more than loading speed.
Common wrong priorities
❌ Minifying CSS when the problem is a 3-second query ❌ Adding a CDN when the problem is synchronous business logic on the server ❌ Optimizing the frontend when the problem is an API making 20 external calls
Conclusion
Good performance is the result of correct architectural decisions made from the start, not of patches applied at the end. The most effective optimizations happen at the logic and database level — not at the CSS compiler level.
If you have a slow application and don't know where to start, a 1-2 week performance audit is the shortest path to clarity.