brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
audit-performanceAudit and fix runtime performance (Core Web Vitals, load priority). Use when "slow page", "LCP/INP/CLS", "fetchpriority", "long tasks", or "bfcache". JS payload → audit-bundle-size. Instant nav → enhance-web-instant-nav. Load limits → test-load.
Independent GitHub snapshot · Automated classification · Original documentation governs
Degree of freedom: MIXED — what to optimize [HIGH freedom];
measure-first Sentry/vitals and EXPLAIN [LOW freedom — run exactly].
Audit-and-fix exception. Measure, then optimize. JS payload →
audit-bundle-size. Instant navigations →enhance-web-instant-nav. Breaking point →test-load. Timeouts/retries →audit-resilience. When fixing: change only the measured bottleneck and re-measure it; log other slow paths you notice as follow-ups, not fixes. Edit the existing file in place rather than rewriting it. Add a perf test only where the repo already keeps them or the task asks; scratch measurements stay out of the repo.
EXPLAIN / N+1 loop (file:line)Observe: Sentry p95
/users= 2.3s; handlerfindAllthen per-userOrder.findByUserId(app/api/users/route.ts:31). Interpret: N+1; TTFB/LCP suffer on that page. Classify: N+1. Severity: Critical (p95 far over 200ms). Finding:/users| N+1 | eager-load orders | then re-measure
Before optimizing, measure. Collect data from multiple sources.
If Sentry performance monitoring is enabled, fetch real production metrics:
sentry:search_events
{
"organizationSlug": "<ORG_SLUG>",
"projectSlug": "<PROJECT_SLUG>",
"regionUrl": "<REGION_URL>",
"query": "slowest transactions by p95 duration in last 7 days",
"limit": 20
}
sentry:search_events
{
"organizationSlug": "<ORG_SLUG>",
"projectSlug": "<PROJECT_SLUG>",
"regionUrl": "<REGION_URL>",
"query": "web vitals LCP INP CLS performance scores in last 7 days",
"limit": 20
}
Check for performance-related issues:
sentry:search_issues
{
"organizationSlug": "<ORG_SLUG>",
"projectSlugOrId": "<PROJECT_SLUG>",
"regionUrl": "<REGION_URL>",
"query": "performance issues slow queries N+1 in last 30 days",
"limit": 20
}
Fetch current performance targets:
firecrawl:firecrawl_search
{
"query": "web vitals thresholds good score <current year>",
"limit": 3,
"sources": [{ "type": "web" }]
}
Lab (Lighthouse) is for diagnosis; field decides pass/fail.
web-vitals/attribution in the app (not the base build): it names the
LCP element, the INP interaction target, and the CLS-shifting node.budget.json (see references/loading-priority-2026.md
§Budgets). Fail the PR on regression, not on absolute score.Record all three in the report's "Production Metrics" block before touching code.
# Field (source of truth): CrUX / PSI field tab → p75 LCP, INP, CLS
# Lab, repeatable, budgeted:
npx @lhci/cli autorun --config=lighthouserc.json
# Early Hints present?
curl -sv https://example.com/ 2>&1 | grep -E "< HTTP/.* 103"
# Speculation: DevTools → Application → Speculative loads
# bfcache: DevTools → Application → Back/forward cache → Test
# LCP element: web-vitals/attribution → onLCP(({attribution}) => attribution.element)
| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | <2.5s | 2.5-4s | >4s |
| INP (Interaction to Next Paint) | <200ms | 200-500ms | >500ms |
| CLS (Cumulative Layout Shift) | <0.1 | 0.1-0.25 | >0.25 |
npm run build -- --analyze # framework-specific
npx source-map-explorer 'dist/**/*.js'
Checklist:
lazy() / dynamic())loading="lazy" on below-fold imagessrcSet for different screen sizesDeep syntax: references/loading-priority-2026.md. Instant-nav implementation → enhance-web-instant-nav.
LCP element (find it first with web-vitals/attribution or DevTools → Performance → LCP)
loading="lazy" — lazy on the LCP image is an automatic Critical findingfetchpriority="high" (Next.js: <Image priority />, which also injects the preload)background-image, not injected by client JS, not behind a hydration boundary<link rel="preload" as="image" imagesrcset imagesizes> when the LCP image is responsive and not already preloaded by the frameworkloading="lazy" decoding="async"; <picture> AVIF → WebP → JPEG fallbackFonts
font-display: swap (or optional for non-brand text); fallback metrics set (size-adjust, ascent-override) so swap causes no CLS — next/font does this automatically, verify it's actually usedEarly Hints (103)
103 Early Hints with preconnect for the image/font CDN and preload for the LCP image + critical CSS. Verify: curl -sv https://site/ 2>&1 | grep -i "< HTTP/.* 103"res.writeEarlyHints({ link: [...] }); otherwise configure at the CDNSpeculation Rules (next-navigation speed)
<script type="speculationrules"> present with prefetch document rules at moderate eagerness for in-site linksprerender only for high-probability next pages (pagination, "next article", product detail); eagerness moderate/conservative? unless expects_no_vary_search is setSec-Purpose: prefetch;prerender is present; analytics/ads guarded by document.prerendering + prerenderingchangeenhance-web-instant-navbfcache
unload listeners (use pagehide); no Cache-Control: no-store on HTML; no open WebSocket / IndexedDB transaction at pagehideRender strategy (Next.js 16 / React 19)
'use client' only at interactive leaves<Suspense> so the shell streams; static shell + dynamic holes (cacheComponents / 'use cache') instead of blocking the whole routereactCompiler: true) before any manual memo/useMemo/useCallback — manual memo only where Profiler proves the Compiler missed itINP (responsiveness)
scheduler.yield() (fallback setTimeout(0)) or startTransitionoffsetHeight, getBoundingClientRect) inside input handlerscontent-visibility: auto + contain-intrinsic-size on long off-screen sectionsasync/defer, Next <Script strategy="lazyOnload"> (or "worker" via Partytown where safe), consent-gated, facades for embeds (lite-youtube, static map image → iframe on click)CLS
<img>, <video>, <iframe>, ad slot has width/height or aspect-ratiotransform/opacity (GPU-accelerated)@import chains (bundled instead)<link rel="preload">)N+1 Detection:
// BAD: N+1
const users = await User.findAll();
for (const user of users) {
const orders = await Order.findByUserId(user.id);
}
// GOOD: eager loading
const users = await User.findAll({ include: [Order] });
Checklist:
SELECT *)Index verification: [LOW freedom — run exactly]
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = '123';
Cache-Control, ETag)| Layer | Tool | TTL | Use For |
|---|---|---|---|
| Browser | Cache-Control headers | Varies | Static assets, API responses |
| CDN | Vercel/CloudFront/Cloudflare | 1h-1d | Static pages, images |
| API | Redis/Memcached | 5m-1h | Expensive queries, computed data |
| ORM | Query cache | 1m-5m | Repeated identical queries |
<link rel="preconnect">)For specific performance bottlenecks, research current solutions:
firecrawl:firecrawl_search
{
"query": "<framework> <specific bottleneck> performance optimization <current year>",
"limit": 5,
"sources": [{ "type": "web" }]
}
Then deep-read the best result:
firecrawl:firecrawl_scrape
{
"url": "<best-result-url>",
"formats": ["markdown"],
"onlyMainContent": true
}
Check official framework docs via Context7:
context7:resolve-library-id
{
"libraryName": "<framework>",
"query": "performance optimization"
}
| Issue | Solution | Impact |
|---|---|---|
| LCP image lazy / not prioritized | fetchpriority="high" + preload; remove loading="lazy" from LCP | High (−300 to −800 ms LCP) |
| Fast first page, slow second | Speculation Rules prefetch (moderate) | High (next-nav TTFB ≈ 0) |
| Manual memo everywhere | Enable React Compiler | High (INP; delete redundant memo) |
| Large bundle | Code split routes, lazy load heavy libs | High |
| Slow images | WebP + lazy load + responsive | High |
| No caching | Add Cache-Control headers | High |
| N+1 queries | Eager load / batch | High |
| Missing indexes | Add database indexes | High |
| Unoptimized fonts | Subset + preload + font-display:swap | Medium |
| No compression | Enable gzip/brotli | Medium |
| Expensive re-renders | React Compiler first; manual memo only with Profiler evidence | Medium |
| Layout shifts | Set explicit width/height on images/embeds | Medium |
EXPLAIN, not "this looks slow"audit-bundle-size; breaking point → test-load; timeouts → audit-resiliencememo/useMemo without a measured re-render; measure again after the fix## Performance Audit: [Project Name]
### Production Metrics (from Sentry)
- LCP: [value] — [good/needs work/poor]
- INP: [value] — [good/needs work/poor]
- CLS: [value] — [good/needs work/poor]
- Slowest transactions: [list with p95 times]
### Critical Issues (fix immediately)
| # | Area | Issue | Impact | Fix |
|---|------|-------|--------|-----|
| 1 | DB | N+1 on /users endpoint | p95 = 2.3s | Eager load orders relation |
### Optimizations (prioritized)
| # | Area | Issue | Impact | Effort | Fix |
|---|------|-------|--------|--------|-----|
| 1 | FE | Bundle 450KB gzipped | High | Low | Code split routes |
### Already Optimized
- [list of performance areas that are well-implemented]
### Monitoring Recommendations
- [what to add to track performance over time]
### Research Sources
- [URL] — [what optimization pattern it provided]