Files
atelier/review/anti-patterns.md
T
Jon Chery d1aa5daf2b docs(milestone): complete v0.2 — infrastructure-as-code + kubernetes
---ci---
project: atelier
phase: 5
milestone: v0.2
status: complete
requirements:
  covered: [ATELIER-36, ATELIER-37, ATELIER-38, ATELIER-39, ATELIER-40, ATELIER-41, ATELIER-42, ATELIER-43, ATELIER-44, ATELIER-45, ATELIER-46, ATELIER-47, ATELIER-48, ATELIER-49, ATELIER-50, ATELIER-51, ATELIER-52, ATELIER-53, ATELIER-54, ATELIER-55, ATELIER-56, ATELIER-57, ATELIER-58, ATELIER-59]
  partial: []
---/ci---
2026-08-05 02:20:17 +00:00

166 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (C1C8)
| 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 |
### Infrastructure as Code
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Unpinned provider (`source` without `version`) | P5 Version Everything | Unversioned drift |
| `terraform apply` without a read `plan` | P4 Plan Before Apply | Unreviewed mutation |
| Local state in a shared environment | P8 Remote State with Locking | No lock = corruption |
| Hardcoded secret in HCL | P10 Secrets Never in Code | Committed secret |
| Copy-pasted block instead of a module | P6 Modules Compose | Bug duplicated |
| Manual change to a managed resource | P9 Drift is Recoverable | Unreconciled drift |
| Admin credentials in CI | P7 Least Privilege Providers | Overbroad grant |
| Committed `terraform.tfstate` | P3 State is Truth, P10 | Secret-bearing artifact in repo |
### Kubernetes
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Bare pod (no controller) | P2 Pods are Mortal | No recovery/scale |
| No resource requests in prod | P4 Requests and Limits | BestEffort, first evicted |
| Liveness probe checks a dependency | P5 Probes Drive Health | Cascade restart |
| `cluster-admin` bound to a workload | P7 RBAC by Intent | Overbroad grant |
| `:latest` image tag in prod | P5 Version Everything | Unversioned drift |
| `emptyDir` for data that must persist | P8 Storage is Explicit | Data lost on pod death |
| Secrets baked into the image | P9 Config and Secrets Separate | No rotation without rebuild |
| `default` namespace in prod | P6 Namespaces Bound Blast Radius | No blast boundary |
| Shared PVC across StatefulSet replicas | P8 Storage is Explicit | Concurrent write corruption |
| `Delete` reclaim policy on prod storage | P8, P5 Reversibility | PVC delete = data delete |
## 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 |
## v0.2 Chaos Anti-Patterns (from IDEATE-13, IDEATE-14)
| Anti-Pattern | Breaches | Why |
|--------------|----------|-----|
| Orphaned P-rule (a domain principle with no matrix row) | matrix completeness, C6 | Breaks the conflict-resolution arbiter; the rule has no core trace |
| Deployable example artifact (standalone `.tf`/`.yaml` under `examples/`) | PROJECT.md "no runtime code", D-025 | Violates the docs-only contract; examples must be `.md` with fenced code |
| Unlisted v0.2 doc (new doc not added to MANIFEST) | manifest rule | Not part of the framework by definition |