What "Production-Ready SQL" Means in Interviews
Senior loops downgrade candidates who can write correct queries that wouldn't survive a code review. Here's what production-ready actually means.
A correct query can still fail an interview. Senior loops at Meta, Stripe, and Airbnb downgrade candidates whose SQL returns the right answer but wouldn't survive a code review. The interviewer's exact note in the rubric: "correct, but not production-ready."
Most candidates have never been told what that phrase means. They assume "production-ready" is a vague style preference. It isn't. It's a specific, learnable bar that separates senior offers from mid-level. This guide is what production-ready SQL actually looks like, and how to get there before your next loop.
What "Production-Ready" Means
A production-ready query is one a senior teammate would approve in a code review without asking for changes. It returns the right answer and:
- Is readable by someone who didn't write it
- Handles the edge cases that exist in real data
- Is structured so that pieces can be reused or modified
- Performs acceptably on the actual data volume
- Makes its assumptions explicit, not implicit
A junior query satisfies #1 only on the day it's written. A production query satisfies all five — six months later, on someone else's screen, against data that's evolved.
The Eight Markers of Production-Ready SQL
Senior interviewers aren't looking for poetry. They're looking for a specific set of habits. Each one is small. Together they signal "I've shipped SQL that other people had to maintain."
Marker 1 — Named CTEs over Nested Subqueries
A correct but not-production query stuffs everything into a single 40-line select with nested subqueries. A production query decomposes into named CTEs.
Not production:
SELECT user_id, total_revenue, total_orders, total_revenue::numeric / NULLIF(total_orders, 0) AS aov
FROM (
SELECT user_id, SUM(amount) AS total_revenue, COUNT(*) AS total_orders
FROM (
SELECT * FROM orders WHERE order_date >= CURRENT_DATE - INTERVAL '90 days' AND status = 'completed'
) recent
GROUP BY user_id
) summarized
WHERE total_orders >= 3;
Production:
WITH recent_orders AS (
SELECT *
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '90 days'
AND status = 'completed'
),
user_totals AS (
SELECT
user_id,
SUM(amount) AS total_revenue,
COUNT(*) AS total_orders
FROM recent_orders
GROUP BY user_id
)
SELECT
user_id,
total_revenue,
total_orders,
ROUND(total_revenue::numeric / NULLIF(total_orders, 0), 2) AS avg_order_value
FROM user_totals
WHERE total_orders >= 3;
Same answer. Six months from now, only the second one is editable without rewriting.
Marker 2 — Explicit Null Handling
Real data has nulls. Production queries assume they exist and decide what to do about them.
COUNT(column)ignores nulls — sometimes that's what you want, sometimes notSUMandAVGskip nulls silently — can mask data quality issues- Joining on a nullable column will drop or duplicate rows depending on join type
- Division by a column that might be zero or null silently breaks without
NULLIF
The production move is to think through null cases for every column you touch. The senior signal is naming them out loud: "channel is nullable for organic acquisitions — I'm treating those as 'organic' explicitly rather than letting them drop."
Marker 3 — Sensible Time Window Semantics
>= CURRENT_DATE - INTERVAL '30 days' is the right form most of the time. But junior candidates regularly write > CURRENT_DATE - INTERVAL '30 days' (off by one day) or BETWEEN without thinking about boundary inclusion.
Production-ready means:
- State which boundary is inclusive: "Last 30 days, including today, excluding today's incomplete day"
- Use closed-open intervals when joining time ranges:
start <= event_time AND event_time < end - Be explicit about timezone: "This assumes the events are in UTC — if they're in user-local time, I'd need to convert first"
Marker 4 — Defensive Aggregation Choices
COUNT(*) counts rows. COUNT(user_id) counts non-null user IDs. COUNT(DISTINCT user_id) counts unique users. These return different numbers on the same data.
Production-ready means you pick the right one and you say why. Defaulting to COUNT(*) when the question is about users is the most common junior mistake. The interviewer notices.
Same with SUM over a joined table — if you joined a 1-to-many, you're double-counting. The fix is to aggregate before joining, or use SUM(DISTINCT ...), or restructure the query. The production query makes the join multiplicity explicit.
Marker 5 — Window Functions Used Deliberately, Not Defensively
Window functions are powerful and often necessary. They're also often overused. A junior candidate reaches for ROW_NUMBER() for problems that just need MIN. A senior candidate uses windows when they're the right tool — for deduplication, ranking, or running calculations.
The production-ready move is asking "do I need a window here, or am I overcomplicating?" before reaching for OVER (PARTITION BY ...). When you do use one, name what it's doing in a comment if the why isn't obvious from context.
Marker 6 — Performance Awareness on Real Data Sizes
You're not expected to optimize for petabytes in an interview. You ARE expected to know when a query won't scale.
SELECT DISTINCTon a billion-row table is expensive- A
JOINwithout indexed keys is a full scan on both sides - Window functions over the entire table without a
PARTITION BYare sequential scans - A
WHEREclause that wraps the indexed column in a function (WHERE DATE(created_at) = ...) prevents index use
Production-ready means flagging these as you go: "This DISTINCT on a billion-row table would be slow — in production I'd probably materialize this as a daily aggregate instead."
Marker 7 — Consistent, Readable Formatting
Tabs vs spaces, uppercase vs lowercase, where the commas go — none of these matter individually. What matters is internal consistency within the query.
Pick one style and stick to it for the whole problem:
- SQL keywords UPPERCASE
- Column names lowercase with underscores
- One column per line in SELECT clauses with three or more columns
- Indentation visually shows the query structure
- Commas at the end of lines (preferred) or leading commas (acceptable) — never mixed
The interviewer doesn't care which style you pick. They care that you have one.
Marker 8 — Comments Only Where Why Isn't Obvious
Junior candidates over-comment ("this CTE gets the users"). Senior candidates under-comment, but the comments they DO write capture non-obvious decisions.
Good comment:
-- Use MIN here because users can have multiple activation events;
-- we want the first one
MIN(activated_at) AS first_activated_at
Bad comment:
-- Get the user_id and email
SELECT user_id, email
The production-ready move is to assume the reader can read SQL but not your mind. Comment the why, never the what.
What an Interviewer Hears When They See Production-Ready SQL
The internal monologue of a senior reviewer when they see clean, decomposed, edge-case-aware SQL:
"This person has shipped before. I can trust their PRs without line-by-line review. I can put them on an ambiguous problem and not babysit. I can promote them within 18 months."
That mental note is what bumps you from a 3 to a 4 on Code Quality and often from a 3 to a 4 on Communication. Two dimension upgrades from the same set of habits. Production-ready SQL is a force multiplier on the rubric.
What an Interviewer Hears When They See "Correct But Not Production"
The opposite monologue:
"They can solve problems but they'll need a senior reviewing their PRs. Not promotion material yet — could maybe get there with 6-12 months of mentorship. I'll vote 'no hire' for senior, 'weak yes' for mid-level."
This is how candidates with technically-correct queries end up downleveled. They didn't fail the technical bar. They failed the production-readiness bar that was always quietly there.
The Production-Ready Checklist
Before declaring "I'm done" on any interview query, walk through this checklist out loud. The walk-through itself is a senior signal:
- Nulls. Did I handle them in every aggregation and join?
- Distinct. Am I counting rows when I should be counting entities?
- Time windows. Are my boundaries explicit and correct?
- Division. Did I
NULLIFand cast to numeric where needed? - CTEs. Is each one doing one thing with a clear name?
- Joins. Have I named the multiplicity (1-to-1, 1-to-many) for each?
- Performance. Would this run on real data volume? Where's the bottleneck?
- Readability. Would my teammate understand this in two minutes?
Twenty seconds of checklist walk-through at the end of every problem. Habit-forming, easy, and the difference between "correct" and "production-ready."
How to Build This
Production-ready SQL isn't a topic you study. It's a set of habits you build by writing queries that you re-read a week later.
The drill:
- Write a query today, fully and to your current standard
- Don't look at it for a week
- Re-read it cold, as if a teammate wrote it
- Note everything that's unclear, fragile, or off
- Rewrite it with those fixes
- Compare the two versions side by side
After ten cycles of this, your "first draft" becomes what your "rewrite" looked like. The habit becomes automatic. That's when interviewers start writing "production-ready" in the positive column of your rubric.
The candidates who level up from mid to senior do this drill consciously. The ones who plateau don't. SQL correctness is the floor. Production-readiness is the ceiling — and it's a learnable one.
Want a structured way to practice? Queryflo's Learn track includes interview-style problems with senior-quality reference solutions you can compare your work against. Five problems with cold-read review beats fifty without.
Learn advanced SQL
Cohort analysis, LTV/CAC, retention curves, A/B testing — FAANG-level analytical SQL.
Learn advanced SQL