d1aa5daf2b
---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---
166 lines
8.0 KiB
Markdown
166 lines
8.0 KiB
Markdown
# 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 |
|
||
|
||
### 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 | |