Ugly SQL Survives Code Review — Formatted Queries Do Not Hide Cross Joins
The PR adds a “simple” report endpoint. The ORM log is one 8,000-character line. Two reviewers approve green CI. Production CPU melts on the first real customer because a missing ON clause produced a cartesian product. Nobody saw it — not because they were careless, but because the SQL was unreadable.
Formatting as a review surface
Pretty SQL makes structure obvious:
SELECT
o.id,
c.email,
SUM(i.amount) AS total
FROM orders AS o
JOIN customers AS c
ON c.id = o.customer_id
JOIN order_items AS i
ON i.order_id = o.id
WHERE o.created_at >= $1
GROUP BY o.id, c.email;
The same query as a single line hides the join intent. Reviewers scan for:
- Join conditions present for every table
- Filters that match the product story (
deleted_at, tenant_id) - Aggregations that match GROUP BY
ORconditions that disable indexes accidentally- Subqueries that could be windows or CTEs for clarity
ORM culture without shame
ORMs are fine. Blind trust in generated SQL is not. For any query that touches more than two tables or wraps reporting:
- Log SQL in the feature branch.
- Format it with a SQL formatter.
- Paste the formatted form into the PR (or attach explain output).
- Run
EXPLAIN/EXPLAIN ANALYZEon realistic data volumes.
Dialect matters (Postgres vs MySQL vs BigQuery). Formatters that understand your dialect keep keywords and functions from becoming noise.
Maintainability beyond the first merge
Six months later someone adds a column. If the team’s habit is formatted CTEs with named steps, the change is local. If the habit is nested subselects in one line, every edit is archaeology. Adopt a house style:
- Uppercase keywords or lowercase — pick one
- One column per line in wide selects
- Explicit
ASaliases - CTEs for multi-step reports instead of nested soup
Put the formatter in editor save or CI for .sql migration files. Application strings are harder; at least format when reviewing.
Logging without leaking
Format for humans, but bind parameters stay out of the SQL string when possible. If you must interpolate for a ticket, redact emails and tokens. Readable SQL in an error tracker is worthless if it contains session cookies.
Diffs and blame
Formatted SQL produces meaningful git blame. A one-line monster attributes the entire query to whoever last touched a comma. Encourage migrations and report queries to live in .sql files or clearly delimited raw strings so formatters and linters can run. Some teams forbid multi-table queries inside application code without an accompany EXPLAIN screenshot in the ticket — harsh, but effective after one outage.
When comparing plans across environments, paste the same formatted text so reviewers are not arguing about whitespace. Store EXPLAIN output next to the query in the wiki for expensive paths; update it when indexes change.
Soft rules for ORMs
- Prefer explicit selects for list endpoints
- Watch default
JOINstrategies that eager-load graphs you do not need - For reports, a readable SQL view or CTE often beats a clever repository method
Formatting will not invent an index. It will make the missing WHERE tenant_id = ? obvious enough that someone asks before merge — which is the entire maintainability win.
What formatting will not save: bad indexes, N+1 call patterns, and selecting blobs you do not need. Formatting makes those problems visible. Visibility is the point of maintainable SQL — the planner does not care about newlines, but your teammates (and future you at 2 a.m.) absolutely do.