Production Observability
Systematic observability for frontend applications — what to observe, how to alert, and how to respond when things go wrong in production.
Observability is the practice of making a system's internal state inferable from its external outputs. For frontend applications, this means knowing whether the app is working for real users — not just whether the server responds. A well-observed application surfaces problems before users report them.
What to Observe
Observability covers four pillars. Each answers a different question about production health.
| Pillar | Question it answers | Frontend equivalent |
|---|---|---|
| Logs | What happened? | Console errors, API error responses, client-side exceptions |
| Metrics | How many? How often? How slow? | Page load time, API latency, error rate, Core Web Vitals |
| Traces | What caused it? | Request waterfall, component render timing, API call chains |
| Alerts | Should someone look right now? | Threshold-based or anomaly-based notifications |
Minimum viable frontend observability
| What to observe | Why | How |
|---|---|---|
| Page load time (LCP) | Largest Contentful Paint is the user's perception of speed. >2.5s is poor. | web-vitals library or Vercel Speed Insights |
| Interaction readiness (INP) | Interaction to Next Paint measures how responsive the page feels. >200ms is poor. | web-vitals library |
| JavaScript errors | Unhandled exceptions that break functionality | Global window.onerror, error boundary capture, Sentry |
| API failure rate | Percentage of API requests that return non-2xx responses | Fetch wrapper with counter |
| API latency (p95/p99) | Slowness that affects only a subset of users | Fetch wrapper with timing |
| Client-side exceptions | Errors in React render, React error boundary catches | Error boundary componentDidCatch or onError callback |
Frontend Observability
Error tracking with Sentry
Sentry provides structured error capture with context, breadcrumbs, and source maps. Set it up in the root layout or provider.
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1, // 10% of transactions for performance monitoring
environment: process.env.NODE_ENV,
integrations: [Sentry.replayIntegration()],
});What to capture in every error
The error tracking service is only useful if errors contain enough context to reproduce the issue.
| Context | How to capture |
|---|---|
| User identity | Sentry.setUser({ id: user.id, email: user.email }) |
| Current route | Sentry.setTag('route', pathname) |
| Browser and OS | Automatically captured by Sentry SDK |
| Console breadcrumbs | Automatically captured by Sentry SDK |
| Network requests | Replay integration captures fetch/XHR |
| Custom context | Sentry.setContext('form', { projectName: 'foo' }) |
Performance monitoring with Web Vitals
import { onLCP, onINP, onCLS } from 'web-vitals';
function reportWebVitals() {
onLCP((metric) => {
// Send to analytics: metric.value is in milliseconds
console.log('LCP:', metric.value);
});
onINP((metric) => {
console.log('INP:', metric.value);
});
onCLS((metric) => {
console.log('CLS:', metric.value);
});
}On Vercel, Speed Insights and Analytics handle this automatically. For other platforms, send these values to your analytics provider or custom endpoint.
Alerting Strategy
Not every problem needs a notification. Alert on symptoms that require human intervention.
| Alert level | Example | Response time |
|---|---|---|
| Critical | Error rate > 5% in last 5 minutes, site is down | Immediately (on-call) |
| Warning | Error rate > 1%, p95 latency > 3s, LCP exceeds threshold | Within business hours |
| Informational | New API endpoint timing baseline established | Logged, no immediate action |
Alert rules for frontend
| Alert | Threshold | Why |
|---|---|---|
| JavaScript error rate spike | >2× baseline in 10 minutes | A new deployment may have introduced a breaking change |
| API failure rate | >5% over 5 minutes | Backend may be degraded or rolling back |
| LCP degradation | >3s for >5% of users | User experience is measurably worse |
| INP degradation | >300ms for >5% of users | Interactivity feels sluggish |
| 404 pages | Spike in 404s (non-API) | A link or redirect may be broken |
Alert fatigue is dangerous. If an alert fires repeatedly without requiring action, adjust its threshold or silence it. Noisy alerts cause teams to ignore critical ones.
Incident Response
When an alert fires, the team should follow a predictable process. Define it before the first incident.
Frontend incident severity levels
| Severity | Definition | Response |
|---|---|---|
| SEV-1 | Core user flow broken for all users. E.g., login page crashes, checkout fails. | Immediate investigation. Rollback or hotfix. |
| SEV-2 | Major flow broken for a subset of users. E.g., dashboard fails for a specific browser. | Fix within 24 hours or rollback. |
| SEV-3 | Minor functional or visual issue. E.g., broken image, wrong copy, layout shift. | Fix in next sprint. |
| SEV-4 | Cosmetic, low-impact. E.g., slightly misaligned element on a specific screen size. | Fix when convenient. |
Runbook approach for common scenarios
| Scenario | First action | Second action | Third action |
|---|---|---|---|
| Error rate spike | Check Sentry for new error or error grouping. | Check deployment log — was a deploy pushed in the last 30 minutes? | Rollback latest deployment if change is correlated. |
| LCP degradation | Check which pages are affected. Check if a new image or font was added. | Check Core Web Vitals dashboard for LCP element breakdown. | Optimise or revert the heavy resource. |
| API failure spike | Check if the failure is across all endpoints or specific. | Check backend monitoring or status page. | If backend is degraded, show cached data or maintenance page. |
Post-incident checklist
After resolving an incident, document:
- What was the trigger (alert or user report)?
- What was the root cause?
- How long did it take to detect, diagnose, and resolve?
- What change would prevent this from recurring?
- What change would make detection or diagnosis faster next time?
Analytics Integration
Observability and analytics serve different purposes but share infrastructure.
| Observability | Analytics | |
|---|---|---|
| Purpose | Detect and diagnose problems | Understand user behaviour |
| Time horizon | Real-time to hours | Days to months |
| Data set | Errors, performance, all sessions | Page views, events, sampled sessions |
| Audience | Engineering team | Product team, stakeholders |
Common analytics setup
// Page view tracking in layout
function AnalyticsProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
useEffect(() => {
if (process.env.NODE_ENV === 'production') {
trackPageView({ path: pathname, referrer: document.referrer });
}
}, [pathname]);
return <>{children}</>;
}
// Custom event tracking
function trackEvent(name: string, properties?: Record<string, unknown>) {
if (process.env.NODE_ENV !== 'production') return;
// Send to analytics provider
}On Vercel, you can use the built-in @vercel/analytics package which handles page views automatically. See Deployment for the CI/CD setup that deploys alongside monitoring.
The distinction between observability and analytics keeps your error data clean. Do not send every page view to Sentry, and do not send every error stack trace to your analytics provider.
Deployment
Ship with confidence using automated CI/CD pipelines, environment-based configuration, preview deployments, rollback strategies, and production monitoring.
Project Context Files
Configure AGENTS.md, .cursorrules, and CLAUDE.md to improve AI agent output — define conventions, architecture rules, and design system constraints before agents write code.