Convex Performance Runbook
Use this document when the admin app feels slower, a dashboard starts flickering under load, or a client reports stale or delayed operational data.
This is not a generic scaling guide.
It is the maintenance runbook for this repo after the Convex data-fetching and hot-path refactor.
Current Baseline
The repo already has the important foundations in place:
- cached client-side Convex queries in
apps/admin/src - stable-query behavior for high-churn admin screens
- shared session/access provider patterns
- reduced full-screen loading churn
- backend hot-path cleanup in:
apps/admin/convex/system.tsapps/admin/convex/billing.tsapps/admin/convex/routing.ts
Do not add speculative complexity before there is evidence the current approach is under pressure.
What Counts As A Real Signal
Investigate when one or more of these becomes true:
- admin pages feel slow on normal operator workflows, not just cold boot
- dashboards visibly re-render too often or take too long to settle
- billing or routing screens lag after filters change
- Convex functions start reading far more documents or bytes than expected
- mutations begin retrying due to contention
- a table has grown enough that previously acceptable indexed
.collect()usage becomes expensive
Do not investigate based on vague anxiety alone.
First Checks
Start here before changing schema or adding denormalized state:
- Reproduce the slow path in the admin app.
- Check the relevant Convex functions in the dashboard or Insights UI.
- Identify whether the issue is:
- too many subscriptions
- too many documents read
- too many bytes read
- mutation contention
- repeated N+1 lookups
- Confirm the hot path is real in production or staging-like data, not just local noise.
What To Inspect By Area
Dashboard / Reporting
Check:
apps/admin/convex/system.tsapps/admin/src/features/dashboard/components/dashboard-page.tsxapps/admin/src/features/activity/components/activity-feed-page.tsx
Watch for:
- broad date-window queries growing too large
- too many independent live dashboard subscriptions
- summary queries that should move to snapshot/materialized-summary patterns
Billing
Check:
apps/admin/convex/billing.tsapps/admin/src/features/billing/components/billing-operations-page.tsx
Watch for:
- invoice history tables growing large enough that status-based scans become costly
- receivables calculations reading too much invoice history
- installer-specific delivery-event filtering growing too expensive
Routing
Check:
apps/admin/convex/routing.tsapps/admin/src/features/routing/components/*
Watch for:
- repeated installer capacity checks becoming hot
- high assignment churn causing many indexed assignment-count reads
- contention around routing mutations
Escalation Order
Use the lightest fix that addresses the real bottleneck.
Level 1
Do first:
- tighten indexes
- replace broad scans with narrower indexed reads
- batch repeated
ctx.db.get(...)lookups - move repeated shared reads behind one provider or one wrapper query
- reduce unnecessary live subscriptions
Level 2
Do only if Level 1 is not enough:
- introduce snapshot/materialized-summary queries for dashboard analytics
- denormalize installer active-load counters
- denormalize receivables summaries
These are valid patterns, but they add maintenance cost and should be justified by real usage.
Current “Eventually” Items
These are not required now.
Revisit only if evidence shows they are hot:
- denormalized active-assignment counters for installer capacity
- denormalized receivables summaries for billing dashboards
- more explicit snapshotting for analytics-style dashboard cards
For this product shape, these are scale-stage optimizations, not baseline requirements.
Rules For Future Changes
- Do not add raw
convex/reactusage in admin feature components; use the wrapper layer inapps/admin/src/core/data/client - Do not add new dashboard/reporting queries that start with full-table
.collect()unless the table is clearly small and expected to stay small - Prefer proving a hot path with real measurements before introducing denormalized state
- When you add a new backend domain with operational dashboards, audit its read shape before shipping
Validation After Any Performance Work
Run all of these:
pnpm --filter @ied/admin typecheckpnpm --filter @ied/admin lintpnpm --filter @ied/admin exec convex dev --once
If the change affects runtime behavior in a visible admin workflow, also click through the affected screen manually.