Defect Scenarios Basics
7 examples to get you started with modeling defect scenarios - 5 basic and 2 intermediate.
Search across all documentation pages
7 examples to get you started with modeling defect scenarios - 5 basic and 2 intermediate.
EXPLAIN, pg_stat_statements).A many-to-many join counted as one-to-many doubles revenue in dashboards.
-- Bad: tags join multiplies order rows
SELECT o.order_id, SUM(oi.amount) AS total
FROM orders o
JOIN order_items oi ON oi.order_id = o.order_id
JOIN order_tags ot ON ot.order_id = o.order_id
GROUP BY o.order_id;
-- Each line item repeated per tagRelated: Fan-Out Join Explosion - cardinality debugging
Application deletes parents; children remain forever.
CREATE TABLE comments (
comment_id bigint PRIMARY KEY,
post_id bigint NOT NULL -- no REFERENCES clause
);
DELETE FROM posts WHERE post_id = 99;
-- comments with post_id = 99 still existON DELETE action.Related: Missing Foreign Keys - integrity enforcement
Adding enum value during traffic holds ACCESS EXCLUSIVE lock briefly.
CREATE TYPE order_status AS ENUM ('draft', 'placed');
-- Later migration under load:
ALTER TYPE order_status ADD VALUE 'shipped';
-- Brief lock; apps queue on hot pathstatus columns.text + CHECK or expand-contract enum strategy.Related: Enum Migration Pain - safe enum changes
Stored local wall time breaks when DST shifts or users travel.
CREATE TABLE meetings (
meeting_id bigint PRIMARY KEY,
starts_at timestamp WITHOUT TIME ZONE -- ambiguous instant
);
INSERT INTO meetings VALUES (1, '2026-03-08 02:30:00');
-- Which timezone? DST gap? Unknown.timestamp without time zone stores no offset - global apps mis-schedule meetings.timestamptz stored in UTC.Related: Timestamp Without Timezone Bugs - DST incidents
Standard data-quality query for missing FK enforcement.
SELECT c.comment_id, c.post_id
FROM comments c
LEFT JOIN posts p ON p.post_id = c.post_id
WHERE p.post_id IS NULL;Row estimate jumps after adding an innocent join.
EXPLAIN (ANALYZE, BUFFERS)
SELECT COUNT(*) FROM orders o
JOIN order_items oi ON oi.order_id = o.order_id
JOIN order_promotions op ON op.order_id = o.order_id;orders * items * promotions expectation.Related: Fan-Out Join Explosion - rewrite patterns
Turn incident into automated prevention.
-- Lint rule pseudo-check: tables ending in _id must have FK metadata
SELECT c.conrelid::regclass AS table_name, c.conname
FROM pg_constraint c
WHERE c.contype = 'f';*_id columns."REFERENCES on new FK-shaped columns.Related: Defect Scenarios Best Practices - lint integration
Stack versions: This page was written for PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, and PostGIS 3.5+.
Reviewed by Chris St. John·Last updated Jul 19, 2026