Change your database schema on a live system without downtime or data loss. This workflow makes the migration safe, adds the right indexes, catches N+1 queries the change might introduce, and locks in current behavior with a test safety net before you deploy.
Analyze the migration for the operations that take a full-table lock - ADD COLUMN NOT NULL DEFAULT, ALTER TYPE, a non-CONCURRENT index - and rewrite it into a safe, ordered expand-contract sequence with a paired rollback.
Given the slow query and the table DDL, get an index recommendation that gets the column order right (equality before range), uses covering columns where they help, and creates the index CONCURRENTLY so it does not block writes.
Scan the ORM code touched by the change for N+1 access patterns - loops over lazy-loaded relations, missing eager loading, serializer-triggered queries - and get the idiomatic batching fix for your ORM.
Before deploying, generate characterization tests that pin the current behavior of the affected code paths. If the migration or query changes alter a result anywhere, the tests tell you immediately.
Everything is static - no database connection needed. The checks run on the migration and ORM files you are already editing, so you can run them in a feature branch before touching any environment.
When you paste an EXPLAIN plan into /explain-plan, include EXPLAIN (ANALYZE, BUFFERS) output if you have it. Actual-vs-estimated row counts are where the real problems hide.
Run /n1-scan after adding any new relation access. A single .map() over lazy-loaded records can turn one query into a thousand in production.