---ci--- project: atelier phase: 0 milestone: v0.4 status: complete requirements: covered: [ATELIER-92, ATELIER-93, ATELIER-94, ATELIER-95, ATELIER-96, ATELIER-97, ATELIER-98, ATELIER-99, ATELIER-100, ATELIER-101, ATELIER-102, ATELIER-103, ATELIER-104, ATELIER-105, ATELIER-106, ATELIER-107, ATELIER-108, ATELIER-109, ATELIER-110, ATELIER-111, ATELIER-112, ATELIER-113, ATELIER-114, ATELIER-115, ATELIER-116, ATELIER-117] partial: [] ---/ci---
20 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.4 Chaos Anti-Patterns (from IDEATE-36, IDEATE-37, D-068, ATELIER-110)
These are named, cross-cutting violations specific to the v0.4 domains. Single-breach per D-068. Reject on sight.
| Anti-Pattern | Breaches | Why |
|---|---|---|
| Edge sync conflict loop (two nodes oscillate divergent state forever; no convergence) | edge P4 Sync Conflicts are Bounded, Not Infinite; C1, C5 | Infinite oscillation is a correctness failure, not eventual consistency; the sync is a livelock |
| Edge TTL-less cache (edge cache with no TTL and no explicit invalidation; stale forever under partition) | edge P6 Cache Invalidation is Explicit; C1, C3 | Stale-forever under partition is a silent correctness defect |
| Edge blocking call on constrained device (a synchronous blocking I/O call on a low-power IoT device with no timeout) | edge P3 Resources are Constrained, P5 (retry safety); C8, C1 | Blocks the constrained node; no timeout = hang; retry is unsafe without idempotency |
| Messaging shared subscription (two consumers share one subscription; each message processed by an arbitrary consumer, breaking per-consumer ordering and idempotency) | messaging P2 Ordering, P3 Idempotency; C1 | Shared subscription breaks ordering and dedup; the messaging-shared-subscription bad example lives here |
| Messaging blocking consumer (a consumer blocks on a slow downstream call with no timeout; the broker re-delivers to the same stuck consumer) | messaging P6 Backpressure is Bounded; C1, C8 | Blocks the partition; lag grows; no backpressure escape |
| Messaging unbounded retry / no DLQ (a poison message is retried forever with no dead-letter queue; the consumer never makes progress) | messaging P5 Dead-Letter Handling is Defined; C1, C5 | Infinite retry is a livelock; poison messages must DLQ |
v0.4 Deployable Artifact Types (IDEATE-36, D-020)
The following standalone file types are forbidden under languages/ and elsewhere in the framework (carried forward from v0.2/v0.3 and extended for language-derived docs). All examples and derived-doc code are .md files with fenced code only.
| Forbidden standalone artifact | Belongs in | Why |
|---|---|---|
Standalone .ts file |
fenced code in languages/*.md (or examples/*.md) |
Runtime TypeScript artifact; violates docs-only contract (v0.4 extension) |
Standalone .py file |
fenced code in languages/*.md (or examples/*.md) |
Runtime Python artifact; violates docs-only contract (v0.4 extension) |
Standalone .go file |
fenced code in languages/*.md (or examples/*.md) |
Runtime Go artifact; violates docs-only contract (v0.4 extension) |
Standalone .rs file |
fenced code in languages/*.md (or examples/*.md) |
Runtime Rust artifact; violates docs-only contract (v0.4 extension) |
Standalone .yaml / .tf / .sh |
fenced code in examples/*.md |
(Carried forward from v0.2/v0.3) Runtime deployable artifact |
v0.4 Language-Derived-Doc Anti-Patterns
| Anti-Pattern | Breaches | Why |
|---|---|---|
| Language-derived doc introducing a new P-rule (e.g., "TS P1", "Python P2") | D-063, ARCHITECTURE.md hierarchy, matrix row-count invariant | Languages apply, not derive; breaks the hierarchy (core > domains > languages) and the 190-row invariant |
| Language-derived doc with no domain P-rule trace (orphan section — header names no domain P-rule) | D-066, matrix completeness | No trace = no traceability; the doc is not connected to the framework |
Language-derived doc with runtime artifact (standalone .ts/.py/.go/.rs file) |
D-020, PROJECT.md no-runtime-code | Violates the docs-only contract; must be fenced code in .md |
| Language first-principles stub gaining P1–P10 numbering | D-063 | Languages are not domains; no P-rules to number; breaks the apply-don't-derive scope |
v0.4 Domain-Specific Anti-Patterns
Edge
| Anti-Pattern | Breaches | Why |
|---|---|---|
| Central-region-only deployment for a latency-bound workload | P1 Proximity is the Design Driver | Violates the proximity constraint; latency is a correctness defect |
| App that crashes on disconnect (no offline state) | P2 Offline is a First-Class State | Partition is the norm; crash-on-disconnect is unengineered |
| Undeclared edge-node resource budget (assumes infinite CPU/memory) | P3 Resources are Constrained and Declared | Unbounded = OOM on constrained nodes |
| Sync loop that oscillates forever (CRDT without merge-semantics, LWW without monotonic clock) | P4 Sync Conflicts are Bounded | Livelock; convergence is never reached |
| Non-idempotent edge write (cache-fill or device command retried with side effects) | P5 Edge Operations are Idempotent | Retry doubles the side effect |
| TTL-less edge cache under partition | P6 Cache Invalidation is Explicit | Stale-forever; silent staleness |
| Crash-on-node-failure (no partial-degradation contract) | P7 Partial Degradation is Engineered | Whole-system crash on one node's failure |
| Random geographic placement (no location-aware routing) | P8 Geographic Distribution is a First-Class Constraint | Latency and data-residency uncontrolled |
| Shared edge-device credential (one key for the whole fleet) | P9 Identity is Constrained at the Edge | One compromise = fleet compromise |
| Fire-and-forget telemetry (no on-node buffer; data lost on partition) | P10 Edge Observability Survives Partition | Partition blinds the operator |
Messaging
| Anti-Pattern | Breaches | Why |
|---|---|---|
| Schemaless message (no versioned contract; consumer parses by guess) | P1 Messages are Contracts | No boundary; consumer breaks silently on shape change |
| "It's FIFO" with no documented partition contract | P2 Ordering is a Property, Not an Assumption | Undocumented ordering = assumed ordering = wrong ordering |
| Non-idempotent consumer under at-least-once delivery | P3 Consumers are Idempotent | Redelivery doubles the effect |
| Unstated delivery semantic (at-least-once vs exactly-once guessed) | P4 Delivery Semantics are Explicit | The tradeoff is made blindly |
| No dead-letter queue (poison message retried forever or dropped) | P5 Dead-Letter Handling is Defined | Livelock or silent loss |
| Unbounded retry budget (no cap; slow consumer stalls the partition) | P6 Backpressure is Bounded | Lag grows without bound |
| Default partition key (no rationale; hotspot or wrong-order) | P7 Partitioning is Intentional | Hotspot or ordering violation |
| Ephemeral broker (no retention; no replay) | P8 Replay and Retention are Configured | Not a durable log; replay impossible |
| Silent schema change (producer breaks consumers with no version bump) | P9 Schemas Evolve Compatibly | Breaking change without migration |
| Silent backlog (no lag metric; consumer falls behind invisibly) | P10 Messaging is Observable | Cannot fix what you cannot see |
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 |