9ebc9c8868
---ci--- project: atelier phase: 6 milestone: v0.3 status: complete requirements: covered: [ATELIER-60..91] partial: [] ---/ci---
13 KiB
13 KiB
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 |
v0.3 Chaos Anti-Patterns (from IDEATE-20, IDEATE-24, IDEATE-25, IDEATE-27)
These are named, cross-cutting violations specific to the v0.3 domains. Reject on sight.
| Anti-Pattern | Breaches | Why |
|---|---|---|
| GitOps push-pattern (external CI pushes manifests to the cluster instead of an in-cluster agent pulling from git) | gitops P3 Pull, Don't Push; C1, C4 | Inverts the source-of-truth flow; requires push credentials into the cluster; breaks the reconciliation model (IDEATE-24, D-042) |
i18n LTR-only assumption (layout assumes left-to-right; no dir attribute, physical CSS properties only) |
i18n P6 Text Direction is a Layout Primitive; C1, C4 | Disqualifying for RTL/Bidi users; locale-correctness violation (IDEATE-25, D-043) |
| AI/ML orphan-model (a deployed prediction endpoint whose model has no lineage trace — no record of training run, dataset, or version) | ai-ml P3 Lineage is Traceable End-to-End; C7, C1 | Unreviewable, unrollbackable; the model is an unattributed artifact (IDEATE-27, D-045) |
| Compliance mutable audit log (audit records can be edited or deleted by an operator) | compliance P1 Audit Logs are Append-Only; C1, C5 | Destroys the audit trail; the audit log's value is immutability — mutation is itself an incident |
v0.3 Deployable Artifact Types (IDEATE-20, D-020)
The following standalone file types are forbidden under examples/ and elsewhere in the framework. Examples are .md files with fenced code only.
| Forbidden standalone artifact | Belongs in | Why |
|---|---|---|
.po / .pot resource files |
fenced code in examples/good//examples/bad/*.md |
Runtime localization artifact; violates docs-only contract |
.rego / .cedar / .sentinel policy files |
fenced code in examples/*.md |
Runtime policy artifact; violates docs-only contract |
Model artifacts (.pkl, .onnx, .pt, .h5, .safetensors) |
fenced code + prose in examples/*.md |
Runtime model artifact; violates docs-only contract |
Signed manifests as standalone files (.sig, .att, .intoto.jsonl) |
fenced code in examples/*.md |
Runtime attestation artifact; violates docs-only contract |
Standalone .yaml / .tf / .sh |
fenced code in examples/*.md |
(Carried forward from v0.2) Runtime deployable artifact |
v0.3 Domain-Specific Anti-Patterns
GitOps + Operators
| Anti-Pattern | Breaches | Why |
|---|---|---|
Push-based deploy (external CI kubectl apply into the cluster) |
P3 Pull, Don't Push | Inverts the model; requires push credentials; bypasses reconciliation |
Manual kubectl apply/kubectl edit on a GitOps-managed resource |
P8 Reconcile, Don't Mutate by Hand | Unreconciled drift; the next loop overwrites it — silent and unattributed |
cluster-admin GitOps robot (controller bound to cluster-admin) |
P10 Least Privilege Reconciliation | Overbroad grant; blast radius = entire cluster |
| No sync-failure notification (silent drift on health degradation) | P9 Failure is Observable and Surfaced | Silent drift is the bug the loop was supposed to surface |
AI / ML
| Anti-Pattern | Breaches | Why |
|---|---|---|
| Unreproducible training run (unpinned data, code, config, or environment) | P1 Reproducibility is the First Class | Unreviewable; cannot debug, cannot rollback |
"Use the latest model" (serving points at model:latest instead of a pinned version) |
P5 Models are Versioned Artifacts | Unversioned drift; rollback undefined |
| Notebook in production (training/serving flow is a Jupyter notebook) | P9 Pipelines Compose, Notebooks Don't | No contracts, no composition, no reproducibility |
| Orphan model (deployed prediction with no lineage trace) | P3 Lineage is Traceable End-to-End | Unattributed artifact; cannot trace to data/code (IDEATE-27) |
i18n
| Anti-Pattern | Breaches | Why |
|---|---|---|
Inline string concatenation ("Hello, " + name + "!" in code) |
P3 Resources are External, Not Inline | Not extractable; breaks translations; word-order differs per locale |
if (n == 1) plural branching (hand-rolled plural logic) |
P4 Plural and Gender are Parameterized | Wrong for Arabic, Russian, Polish; ICU MessageFormat handles plurals |
LTR-only layout (no dir attribute, physical CSS left/right) |
P6 Text Direction is a Layout Primitive | Disqualifying for RTL/Bidi (IDEATE-25) |
Hand-rolled date/number formatter (new Date().toString(), manual string formatting) |
P5 Formatting is Locale-Aware | Locale-incorrect; ignores ICU/CLDR |
Compliance
| Anti-Pattern | Breaches | Why |
|---|---|---|
Mutable audit log (operator can UPDATE/DELETE audit records) |
P1 Audit Logs are Append-Only | Destroys the audit trail; mutation is itself an incident |
Shared/generic identity in audit (admin or system as the actor for all actions) |
P7 Identity is Attributable | No attribution; no accountability; cannot investigate |
| Secret leaked in audit log (request body or token captured in an audit event) | P9 Secrets and Sensitive Data are Redacted in Audit | Audit log becomes a secret exfiltration channel |
| Manual evidence assembly at audit time (scramble to collect logs/scans/attestations on demand) | P6 Evidence is Collected Continuously | Audit-unready; evidence gathered under pressure is incomplete and unreliable |