# App Performance Best Practices

Lessons learned from Tradeify build (Feb 2026). Apply to all future web app builds.

## Code Splitting
- **Always lazy-load pages** with `React.lazy()` + `Suspense` from day one
- Only eagerly import the landing/home page and shared layout
- This alone cut Tradeify's main bundle from 870KB to 293KB (66% reduction)
- Heavy components (maps, charts, admin panels) should always be lazy-loaded

## Database Queries
- **Never `select('*')` on large tables** -- only fetch the columns the UI actually uses
- **Never load all rows client-side** -- use server-side filtering, pagination, and `limit()`
- **Verify column names** before deploying -- query the table schema first (`select('*').limit(1)` then check keys)
- **Supabase default max rows is 1,000** -- must be bumped in dashboard Settings > API > Max Rows if needed
- Use `.limit(500)` for initial loads, let filters narrow server-side
- Use `{ count: 'exact', head: true }` for total counts without fetching data

## Caching
- **Cache API results in sessionStorage** for instant back-navigation (5 min TTL)
- Cache per-filter combination so switching filters is instant on repeat
- Always restore from cache on mount before firing API calls

## Pre-Deploy Checklist
- **Test queries locally** (node script) before deploying -- verify row counts, column names, response times
- **Check bundle sizes** after build (`ls -lhS dist/assets/*.js`)
- **Never deploy without building first** -- catch TypeScript errors before they hit production

## Mobile Performance
- Keep initial page load under 300KB JS
- Debounce search inputs (300ms) to avoid hammering APIs
- Use `onPointerUp` instead of `onClick` for iOS Safari touch handling
- Remove `truncate` CSS on important text (names) -- let it wrap instead

## General Rules
- Start with performance constraints, don't bolt them on later
- 13,000 rows × `select('*')` = 2 second load = bad UX
- 500 rows × specific columns = 400ms = acceptable
- Cached result = 0ms = ideal
