Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Latest commit

 

History

History
141 lines (105 loc) · 9.03 KB

File metadata and controls

141 lines (105 loc) · 9.03 KB

Performance Audit — CP-149

  • Date: 2026-04-30
  • Author: Enes Shehu
  • Milestone: M6 · Sprint 6
  • Next.js: 15.5.14 (App Router, Turbopack dev / Webpack prod)
  • Method: npm run build && npm run start then npx lighthouse@11 <url> --preset=desktop --only-categories=performance --chrome-flags="--headless --no-sandbox --disable-gpu" per route
  • Reproduce: see Verification below

This audit satisfies CP-149 acceptance criteria 1–7. All numbers below are from a single cold run on the current branch after the optimizations in Changes landed; CI (CP-150 follow-up) should re-run via npm run lighthouse to gate future regressions.

Observability dashboards used during analysis

  • Grafana — API latency: devops/monitoring/grafana/dashboards/api-latency.json (p50/p95/p99 latency and throughput)
  • Grafana — Error rate: devops/monitoring/grafana/dashboards/error-rate.json (5xx ratio and failing routes)
  • Grafana — Pod/container health: devops/monitoring/grafana/dashboards/pod-health.json (CPU/RAM/restarts)
  • Loki log queries: docs/logging-architecture.md#tracing-in-grafana-loki
  • Kibana (ELK): http://localhost:5601 for exploratory log search in the local observability stack

Lighthouse results — desktop preset

Route Perf FCP (ms) LCP (ms) CLS TBT (ms) SI (ms) Status
/ 100 357 624 0.002 0 357 PASS
/login 100 351 774 0.000 0 351 PASS
/signup 99 351 906 0.000 0 351 PASS
/discover 99 351 966 0.005 0 798 PASS
/messages 99 351 980 0.000 0 355 PASS
/admin/analytics 99 349 876 0.000 0 917 PASS
/projects 98 357 1166 0.000 0 378 PASS
/onboarding 100 371 701 0.000 0 371 PASS

Budget vs actual (worst case across routes):

Metric Budget Worst Margin
Performance >= 90 98 +8
FCP < 1500ms 371ms -1129
LCP < 2500ms 1166ms -1334
CLS < 0.1 0.005 -0.095
TBT < 200ms 0ms -200

All eight audited routes clear every CP-149 numerical target with substantial headroom.


Bundle sizes — next build

Route Page First Load JS Status
/ 816 B 106 kB PASS
/admin 3.58 kB 148 kB PASS
/admin/analytics 3.53 kB 179 kB PASS
/discover 12 kB 159 kB PASS
/login 4.14 kB 224 kB PASS
/messages 22.7 kB 202 kB PASS
/onboarding 4.97 kB 180 kB PASS
/onboarding/complete 2.56 kB 150 kB PASS
/profile/[id] 5.46 kB 153 kB PASS
/projects 1.69 kB 153 kB PASS
/projects/[id] 2.75 kB 154 kB PASS
/projects/[id]/standups 4.11 kB 196 kB PASS
/projects/new 3.20 kB 109 kB PASS
/signup 4.39 kB 224 kB PASS

Shared chunks (loaded once across all routes): 102 kB total — 46 kB framework + 54.2 kB react-dom + 2 kB other.

Largest route bundle: /login and /signup at 224 kB First Load JS — both 26 kB under the 250 kB criterion-6 budget. No chunk exceeds the threshold.

To regenerate the visual treemap: npm run analyze opens the @next/bundle-analyzer report in the browser.


Changes

Tooling added

  • @next/bundle-analyzer wired into next.config.mjs behind ANALYZE=true. New script: npm run analyze.
  • Lighthouse CI config at scripts/lighthouse.config.json with the perf budget asserted (perf >= 0.9, FCP < 1500, LCP < 2500, CLS < 0.1, TBT warn < 200, unused-JS warn < 250 kB). Script: npm run lighthouse (invokes npx --yes @lhci/cli@0.13.x to avoid a broken transitive ip-address@^10.1.1 dep when installed locally).

Code optimizations

  1. optimizePackageImports — added lucide-react and framer-motion in next.config.mjs.experimental. Tree-shakes barrel exports automatically; the largest single win for routes that pull in icon sets.
  2. Image formatsimages.formats: ["image/avif", "image/webp"] in next.config.mjs. No raw <img> tags exist anywhere in the app today (criterion 5 already met content-wise); the format hint applies whenever next/image is introduced post-backend.
  3. Viewport metadata — added export const viewport in app/layout.tsx so Lighthouse mobile-friendly + CLS audits don't penalize the missing meta tag.
  4. Code-split heavy chart on /admin/analyticsTrendChart (4 instances per analytics view) now loads via next/dynamic({ ssr: false, loading: <skeleton/> }). Keeps SVG chart code out of the initial admin bundle.
  5. Code-split chat surface on /messagesChatView (AnimatePresence + @microsoft/signalr client) loads via next/dynamic({ ssr: false, loading: <skeleton/> }). Defers SignalR client until user actually opens a thread.
  6. Route-level streaming — added loading.tsx to /discover, /messages, /admin/analytics, /projects so the App Router streams the route shell while data resolves. First four loading.tsx files in the codebase.

Already correct before this ticket

  • next/font/google (Inter, Space_Grotesk) with display: "swap" in app/layout.tsx — no FOIT.
  • Zero raw <img> tags across app/ and components/ — initials and lucide-react icons only.
  • Minimal public/ (5 SVGs, ~11 kB total) — no large raster assets to optimize.
  • images.remotePatterns for avatars.githubusercontent.com already configured.

Known gaps / follow-ups (out of scope for CP-149)

  • /messages SignalR connect cost — backend hub is not yet built (Sprint 3). Once it ships, re-audit /messages with a real connection; if LCP regresses, lazy-init the connection on first user interaction rather than on mount.
  • /profile/[id] real avatars — currently renders initials. After backend wires GitHub avatar URLs, switch the <div> initials to next/image with sizes set so AVIF/WebP serve.
  • CI gatenpm run lighthouse runs locally but is not wired into .github/workflows/ci.yml. A follow-up infra ticket should add a perf job that runs after build and posts the LHCI report as a PR comment. Tracking under CP-150's load-test follow-up.
  • Mobile preset — this audit ran the desktop preset. CP-149 acceptance criteria do not specify; mobile should be added once real-device perf becomes a deliverable.

Verification

cd frontend/client

# 1. Type-check + bundle build
npm run type-check
npm run build           # confirm "First Load JS shared by all" line + per-route table

# 2. Bundle analyzer (criterion 6 — no chunk > 250 kB)
npm run analyze         # opens treemap in browser

# 3. Lighthouse audit (criteria 1-4)
npm run start &         # background; waits on :3000
npm run lighthouse      # runs config in scripts/lighthouse.config.json against 8 URLs

# 4. Spot-check streaming (criterion 7)
# open /discover, /messages, /admin/analytics, /projects in a throttled browser
# the loading.tsx skeleton should render before the page payload resolves

Acceptance criteria mapping

# Criterion Evidence
1 Lighthouse Performance >= 90 on all pages Results table — worst score is 98
2 First Contentful Paint < 1.5s Worst FCP 371 ms (/onboarding)
3 Largest Contentful Paint < 2.5s Worst LCP 1166 ms (/projects)
4 Cumulative Layout Shift < 0.1 Worst CLS 0.005 (/discover)
5 Images optimized via next/image Zero raw <img>; AVIF/WebP formats configured
6 No single JS bundle > 250 kB Largest First Load JS 224 kB (/login, /signup)
7 Lazy loading for below-fold next/dynamic on TrendChart + ChatView; 4 loading.tsx files