Anti-Patterns
A catalog of violations. Each entry names the principle it breaches. Use this to recognize and reject patterns on sight.
How to Use
When you see a pattern listed here, it is a defect. Cite the principle it violates and require a fix. These are not "to be reviewed later"; they are rejected on sight.
Core Anti-Patterns (C1–C8)
| Anti-Pattern |
Breaches |
Why |
| Code that "mostly works" |
C1 Correctness |
Mostly correct is incorrect |
function doStuff() |
C2 Clarity |
Name reveals nothing |
| A 500-line function |
C3 Simplicity |
Complexity is a liability |
| Config in a distant repo, read silently |
C4 Locality |
Coupling you cannot see |
A migration with no down |
C5 Reversibility |
Irreversible bet |
| A component reading global state implicitly |
C6 Composability |
Hidden dependency |
| A service with no logs |
C7 Observability |
Cannot debug what you cannot see |
| Loading all records into memory |
C8 Economy |
Unbounded = OOM |
Domain Anti-Patterns
UI/UX
| Anti-Pattern |
Breaches |
Why |
Image without alt |
P2 Accessibility |
Disqualifying |
| "Delete" with no confirmation |
P5 Forgiveness |
Irreversible surprise |
color: #3b82f6 in a component |
P8 Consistency (via tokens) |
Bypasses design system |
| "Submit" on a delete button |
P3 Clarity |
Wrong verb |
| Layout shift on image load |
P7 Hierarchy / CLS |
Visual instability |
API
| Anti-Pattern |
Breaches |
Why |
/getUsers (verb in URL) |
P2 Clarity |
Resources are nouns |
| 200 with an error body |
P9 Error Transparency |
Status code lies |
| 500 with a stack trace |
P8 Security, P9 |
Information leak |
No Idempotency-Key on a POST |
P6 Idempotency |
Retry is unsafe |
| 10MB response by default |
P7 Performance |
Unbounded payload |
Security
| Anti-Pattern |
Breaches |
Why |
eval() of any string |
P4, P5 |
Code injection |
| Hardcoded API key in source |
P9 Secret Hygiene |
Committed secret |
catch (e) {} (swallow) |
P7, P8 |
Silent failure, fail-open |
md5 for password hashing |
P6 Crypto |
Broken primitive |
Open CORS * in production |
P1, P10 |
Zero trust violated |
chmod 777 |
P2 Least Privilege |
Maximum privilege |
| Logging the request body |
P9 Secret Hygiene |
Token leak |
Data
| Anti-Pattern |
Breaches |
Why |
VARCHAR for a UUID |
P7 Type Fidelity |
Wrong type |
| No FOREIGN KEY |
P3, P9 |
Unenforced relationship |
FLOAT for money |
P7, P1 |
Floating point error |
is_deleted without filtering |
P8 Lifecycle |
Soft-delete leak |
SELECT * |
P10 Performance |
Unbounded columns |
Testing
| Anti-Pattern |
Breaches |
Why |
| Test that cannot fail |
P10 No Test Theater |
Not a test |
Date.now() in a fixture |
P3 Determinism |
Flaky |
| Shared fixture mutated across tests |
P2 Independence |
Order-dependent |
| 500 e2e tests, 50 unit |
P4 Fast Feedback |
Inverted pyramid |
name: "test" fixture |
P7 Realism |
Hides bugs |
Performance
| Anti-Pattern |
Breaches |
Why |
| N+1 query in a loop |
P3 Complexity |
O(N) queries |
| No timeout on HTTP call |
P4, P8 (concurrency) |
Hang forever |
| Cache with no invalidation |
P5 Caching |
Stale forever |
| Unbounded in-memory sort |
P4 Resource Bounds |
OOM |
| Optimization without measurement |
P1 Measure First |
Guesswork |
Observability
| Anti-Pattern |
Breaches |
Why |
console.log("here") |
P1 Structured |
Not queryable |
user_id as a metric label |
P4 Cardinality |
Unbounded bill |
| Average latency only |
P8 SLO |
Hides the tail |
No trace_id propagation |
P2 Correlation |
Cannot trace |
Logs without request_id |
P3 Context |
No correlation |
Errors
| Anti-Pattern |
Breaches |
Why |
catch (e) { return null } |
P2 Fail Loudly |
Silent failure |
throw new Error("error") |
P3 Fail Specifically |
Generic |
return null for "not found" |
P1 Errors are Data |
Conflates absence with error |
| Retry without backoff |
P5, P8 |
Retry storm |
throw in a recovery path |
P6 |
Fail fast in wrong place |
Documentation
| Anti-Pattern |
Breaches |
Why |
| Doc with no examples |
P3 Examples |
Incomplete |
| Stale doc (wrong, not updated) |
P4 Currency |
Worse than no doc |
| Unlisted doc (not in MANIFEST) |
(framework rule) |
Not part of framework |
| No audience statement |
P2 Audience |
Who is this for? |
Concurrency
| Anti-Pattern |
Breaches |
Why |
| Shared mutable state, no lock |
P1 Immutability |
Race condition |
| Unbounded queue |
P9 Bounded Queues |
OOM |
channel.send() with no timeout |
P8 Timeout |
Hang |
| Mutex held across I/O |
P3 Lock Scope |
Lock too long |
| Spawned work with no cancellation |
P7 Cancellation |
Orphaned work |
DevOps
| Anti-Pattern |
Breaches |
Why |
| Manual deploy script |
P2 Automation |
Not repeatable |
| No rollback path |
P4 Rollback |
Irreversible deploy |
| Big-bang deploy |
P5 Progressive |
All-or-nothing |
| Rebuild per environment |
P7 Immutability |
Different artifacts |
| Snowflake server |
P1, P6 |
Not reproducible |
Cross-Cutting Anti-Patterns
| Anti-Pattern |
Breaches |
Why |
| "I'll add tests later" |
C1 (no proof of correctness) |
Later never comes |
| "It's just a prototype" |
C5 (irreversible by default) |
Prototypes go to prod |
| Copy-paste code |
C6 (no composition) |
Bug duplicated |
| God object |
C3, C6 |
One thing, many things |
| Leaky abstraction |
C6, C2 |
Hidden coupling |