Part 7 · 1 chapters · ~8 min
ORMs at Staff Level
Profiling ORM output with query logs and pg_stat_statements, unsafe generated migrations, transactions through an ORM (propagation, nesting, savepoints), pool interaction across instances, multi-tenancy patterns, when to drop to SQL and how to structure a codebase that does both, testing strategies, and writing the team's ORM position as an RFC.
11
The position, written down
code
RFC: how we use the ORM (excerpt) 1 the ORM for CRUD, simple relations and most reads; SQL (via the query builder or raw, parameterised) for reports, bulk operations, locking money paths and anything where the generated SQL is worse than hand-written 2 every list endpoint declares its loading strategy; strict lazy-loading errors on in tests (Rails strict_loading, Django nplusone, Hibernate statistics assertions) 3 generated migrations are reviewed as SQL; dangerous operations need a safe pattern (Postgres course P10) 4 transactions are explicit at the service layer, short, and never span external calls 5 pool size per process × processes × replicas stays under the pooler's server pool (Scaling course P2) 6 tenancy enforced in a repository base class AND by database row-level security (Auth course P7) 7 tests run against real Postgres (Testcontainers); no in-memory database substitutes 8 query budget: p95 query count per endpoint tracked; regressions fail CI
| testing strategy | trade-off |
|---|---|
| real database per test run (Testcontainers) | accurate; seconds of start-up |
| transaction rollback per test | fast; hides commit-time behaviour (deferred constraints, on_commit hooks) |
| SQLite or in-memory substitutes | fastest; different SQL dialect and behaviour: misses real bugs |