configuration/principle/data/anti-pattern.data.json
configuration/principle/data/anti-pattern.data.json continued, part 2 of 2.
"formed_by": "Build deep request-time chains across services or modules, making latency, availability, and failure behavior multiplicative.",
"detected_by": [
"sync_depth_above_threshold",
"request_path_many_remote_calls",
"cascading_timeout"
],
"measured_by": ["synchronous call depth per request path"],
"refactored_by": [
"lexicon:coarse-grained-endpoint",
"lexicon:introduce-async-event",
"lexicon:query-projection",
"architecture:timeout-pattern",
"architecture:bulkhead-pattern"
],
"enforced_by": [
"call-depth limits in architecture review",
"timeout and bulkhead policies"
],
"severity": "discouraged",
"exemplar": {
"before": "const foo = await a();\nconst bar = await b(foo);\nconst baz = await c(bar);\nreturn slowSyncCall(a, b, c);",
"after": "const [foo, bar, baz] = await Promise.all([a(), b(), c()]);",
"lang": "ts"
}
},
{
"id": "chatty-interface",
"name": "Chatty Interface",
"definition": "A defect in which one operation needs many small remote calls.",
"type": "anti-pattern",
"scope": [
"modularity",
"contract_compatibility"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Require many small remote calls to complete one user or business operation.",
"detected_by": [
"N_plus_1_API_calls",
"many_calls_per_screen",
"loop_contains_remote_call"
],
"measured_by": ["remote calls per user operation"],
"refactored_by": [
"lexicon:coarse-grained-endpoint",
"lexicon:query-projection",
"lexicon:batch-fetch"
],
"enforced_by": [
"API review",
"performance tests that bound call counts"
],
"severity": "discouraged",
"exemplar": {
"before": "const results = [];\nfor (const id of fooIds) results.push(await fooApi.get(id));",
"after": "const results = await fooApi.getMany(fooIds);",
"lang": "ts"
}
},
{
"id": "n-plus-one-query",
"name": "N Plus One Query",
"aliases": ["N+1 Query"],
"definition": "A defect in which a collection is fetched and then one further query is issued per item.",
"type": "anti-pattern",
"scope": ["architecture_evolution"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Fetch a collection, then issue one query or remote call per item rather than fetching required related data intentionally.",
"detected_by": [
"query_inside_loop",
"remote_call_inside_loop",
"query_count_scales_with_rows"
],
"measured_by": ["queries per request as the row count grows"],
"refactored_by": [
"lexicon:batch-fetch",
"lexicon:query-projection"
],
"enforced_by": ["query-count assertions in integration tests"],
"severity": "discouraged",
"exemplar": {
"before": "const foos = await fooStore.all();\nfor (const foo of foos) foo.bar = await barStore.find(foo.barId);",
"after": "const foos = await fooStore.all();\nconst bars = await barStore.findMany(foos.map(f => f.barId));",
"lang": "ts"
}
},
{
"id": "cache-poisoning-by-design",
"name": "Cache Poisoning by Design",
"definition": "A defect in which a cache key omits an input the entry depends on, so one request is served another's result.",
"type": "anti-pattern",
"scope": [
"correctness_verification",
"security_governance"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Cache data without key correctness, tenant isolation, authorization context, invalidation, or schema version, or key the entry by time or lifetime alone.",
"detected_by": [
"cache_key_missing_user_or_tenant",
"cache_without_version",
"cache_keyed_by_time",
"no_invalidation",
"authorization_not_in_cache_key"
],
"measured_by": [
"cache keys missing tenant",
"identity or version"
],
"refactored_by": [
"lexicon:cache-contract",
"lexicon:context-keyed-cache",
"lexicon:cache-invalidation-on-write",
"architecture:versioning"
],
"enforced_by": [
"cache-key review",
"tests that vary tenant and version"
],
"severity": "discouraged",
"exemplar": {
"before": "fooCache.set(request.path, response);",
"after": "if (response.ok && response.cacheable) {\n fooCache.set(cacheKey(request.identity, request.path, fingerprint(request.inputs, SCHEMA_VERSION)), response);\n}",
"lang": "ts"
}
},
{
"id": "retry-storm",
"name": "Retry Storm",
"definition": "A defect in which clients retry a failing dependency so aggressively that the retries prolong the failure.",
"type": "anti-pattern",
"scope": ["resilience_recovery"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Allow many clients or workers to retry failed dependencies aggressively and synchronously, increasing pressure on the failing system.",
"detected_by": [
"no_backoff",
"no_jitter",
"unbounded_retries",
"retry_on_non_idempotent_operation"
],
"measured_by": [
"retries per failed call",
"retry share of load during incidents"
],
"refactored_by": [
"lexicon:bounded-retry",
"lexicon:backoff",
"lexicon:jitter",
"architecture:circuit-breaker-pattern",
"lexicon:idempotency-key"
],
"enforced_by": ["a resilience policy that requires bounded retries with backoff and jitter"],
"severity": "discouraged",
"exemplar": {
"before": "while (true) { try { return await call(); } catch { } }",
"after": "return retry(call, { attempts: 5, backoff: exponentialJitter(), giveUp: dlq });",
"lang": "ts"
}
},
{
"id": "timeout-omission",
"name": "Timeout Omission",
"definition": "A defect in which calls to external systems carry no timeout, cancellation or deadline, so a caller can wait for ever on work that never completes.",
"aliases": ["Infinite Wait"],
"type": "anti-pattern",
"scope": ["resilience_recovery"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Call external systems without explicit timeouts, cancellation, or deadline propagation.",
"detected_by": [
"HTTP_call_without_timeout",
"DB_query_without_timeout",
"missing_cancellation_token",
"no_deadline_propagation"
],
"measured_by": ["outbound calls without a timeout"],
"refactored_by": [
"architecture:timeout-pattern",
"lexicon:deadline-propagation",
"lexicon:cancellation",
"lexicon:fail-fast-or-fall-back"
],
"enforced_by": ["a lint rule that requires a timeout on outbound calls"],
"severity": "discouraged",
"exemplar": {
"before": "const foo = await fetch(fooUrl);",
"after": "const foo = await fetch(fooUrl, { signal: AbortSignal.timeout(5000) });",
"lang": "ts"
}
},
{
"id": "missing-backpressure",
"name": "Missing Backpressure",
"definition": "A defect in which a system accepts work faster than it can process it, with no limit or shedding.",
"aliases": ["Unbounded Ingestion"],
"type": "anti-pattern",
"scope": ["resilience_recovery"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Accept work faster than the system can process it without queue limits, admission control, rate limits, or shedding.",
"detected_by": [
"unbounded_queue",
"no_rate_limit",
"no_admission_control",
"memory_grows_with_load"
],
"measured_by": ["queue depth and memory growth under load"],
"refactored_by": [
"lexicon:bounded-queue",
"architecture:rate-limiting",
"lexicon:load-shedding",
"architecture:backpressure"
],
"enforced_by": [
"load tests",
"checks that every queue is configured with a bound"
],
"severity": "discouraged",
"exemplar": {
"before": "stream.on(\"data\", d => queue.push(process(d)));",
"after": "stream.pipe(new BoundedFooProcessor({ highWaterMark: 100 }));",
"lang": "ts"
}
},
{
"id": "silent-data-corruption",
"name": "Silent Data Corruption",
"definition": "A defect in which invalid data is accepted, transformed or stored without any check noticing.",
"type": "anti-pattern",
"scope": [
"contract_compatibility",
"correctness_verification"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Accept, transform, or persist invalid data without validation, checksums, invariants, reconciliation, or audit.",
"detected_by": [
"missing_boundary_validation",
"no_invariant_check",
"impossible_state_in_database",
"reconciliation_failures"
],
"measured_by": ["invariant violations and reconciliation mismatches"],
"refactored_by": [
"lexicon:validate-at-the-boundary",
"lexicon:invariant-check",
"lexicon:reconciliation-job",
"lexicon:data-change-audit"
],
"enforced_by": [
"boundary validation",
"invariant checks",
"reconciliation jobs"
],
"severity": "discouraged",
"exemplar": {
"before": "const total = Number(a) + Number(b);\nsave(total);",
"after": "const total = Money.add(Money.parse(a), Money.parse(b));\nsave(total);",
"lang": "ts"
}
},
{
"id": "lost-update",
"name": "Lost Update",
"aliases": ["Whole-File Write Race"],
"definition": "A defect in which concurrent writers overwrite each other's changes without a version check.",
"type": "anti-pattern",
"scope": ["state_transaction"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Allow concurrent writers to overwrite each other without version checks, locks, compare-and-swap, or transaction isolation.",
"detected_by": [
"last_write_wins_without_version",
"no_optimistic_lock",
"concurrent_update_defects"
],
"measured_by": ["concurrent update conflicts found in tests"],
"refactored_by": [
"architecture:optimistic-locking",
"architecture:pessimistic-locking",
"lexicon:merge-policy",
"lexicon:transaction-isolation-level"
],
"enforced_by": [
"optimistic-locking checks",
"concurrency tests"
],
"severity": "discouraged",
"exemplar": {
"before": "const foo = await load(id);\nfoo.count += 1;\nawait save(foo);",
"after": "await fooStore.update(id, { count: increment(1) }, { expectedVersion: foo.version });",
"lang": "ts"
}
},
{
"id": "dual-write",
"name": "Dual Write",
"definition": "A defect in which related state is written to two systems with no atomicity or compensation.",
"type": "anti-pattern",
"scope": ["event_messaging"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Write related state to two systems without atomicity, outbox, saga, reconciliation, or compensation.",
"detected_by": [
"database_write_then_message_publish",
"two_databases_updated_without_transaction_or_outbox",
"manual_repair_needed"
],
"measured_by": ["writes to two systems outside one transaction or outbox"],
"refactored_by": [
"architecture:outbox-pattern",
"architecture:saga-pattern",
"architecture:idempotent-consumer",
"lexicon:reconciliation-job"
],
"enforced_by": [
"an outbox or saga",
"verified by integration tests"
],
"severity": "discouraged",
"exemplar": {
"before": "await db.save(foo);\nawait searchIndex.add(foo);",
"after": "await db.save(foo);\nawait outbox.append(fooCreatedEvent(foo));",
"lang": "ts"
}
},
{
"id": "read-your-writes-violation",
"name": "Read-Your-Writes Violation",
"definition": "A defect in which a writer reads back a stale copy of what it has just written.",
"type": "anti-pattern",
"scope": ["contract_compatibility"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Let users or processes perform a write and then read from a stale replica, cache, projection, or eventually consistent view without explicit consistency contract.",
"detected_by": [
"write_then_stale_read_defect",
"cache_not_invalidated_after_write",
"replica_read_after_write"
],
"measured_by": ["stale reads observed after writes"],
"refactored_by": [
"lexicon:read-from-primary-after-write",
"lexicon:cache-invalidation-on-write",
"lexicon:consistency-contract"
],
"enforced_by": ["consistency-contract tests that read after a write"],
"severity": "discouraged",
"exemplar": {
"before": "await primaryDb.write(foo);\nconst view = await replicaDb.read(foo.id);",
"after": "await primaryDb.write(foo);\nconst view = await readAfterWrite(foo.id, { consistency: \"read-your-writes\" });",
"lang": "ts"
}
},
{
"id": "security-theater",
"name": "Security Theater",
"definition": "A defect in which visible security controls leave the real threat unreduced, or can be bypassed.",
"type": "anti-pattern",
"scope": [
"security_governance",
"model_governance"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Add visible security controls that do not reduce the actual threat model or can be bypassed by alternate paths.",
"detected_by": [
"control_not_linked_to_threat",
"bypass_endpoint",
"client_only_security",
"audit_passes_but_attack_succeeds"
],
"measured_by": [
"controls not linked to a threat",
"bypass paths found"
],
"refactored_by": [
"architecture:threat-modeling",
"lexicon:server-side-enforcement",
"lexicon:penetration-testing",
"architecture:policy-as-code"
],
"enforced_by": [
"threat-model review",
"penetration tests"
],
"severity": "discouraged",
"exemplar": {
"before": "if (password.length > 0) grantFooAccess(user);",
"after": "const verified = await verifyPassword(password, user.passwordHash);\nif (!verified) throw new UnauthorizedError();\ngrantFooAccess(user);",
"lang": "ts"
}
},
{
"id": "authorization-scattering",
"name": "Authorization Scattering",
"definition": "A defect in which authorization checks are spread across layers with no central policy.",
"type": "anti-pattern",
"scope": [
"security_governance",
"model_governance"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Spread authorization checks across controllers, services, repositories, UI, and ad hoc conditionals without a central policy model.",
"detected_by": [
"repeated_role_checks",
"missing_policy_engine",
"endpoint_without_authz",
"inconsistent_resource_access"
],
"measured_by": [
"endpoints without a policy check",
"duplicated role checks"
],
"refactored_by": [
"lexicon:centralize-policy",
"architecture:policy-as-code",
"architecture:attribute-based-access-control",
"architecture:role-based-access-control",
"lexicon:authorization-tests"
],
"enforced_by": ["a policy engine, with authorization tests per endpoint"],
"severity": "discouraged",
"exemplar": {
"before": "if (user.role === \"admin\") deleteFoo();\nif (user.role === \"admin\" || user.id === foo.owner) editFoo();",
"after": "if (policy.can(user, \"delete\", foo)) deleteFoo();\nif (policy.can(user, \"edit\", foo)) editFoo();",
"lang": "ts"
}
},
{
"id": "secret-sprawl",
"name": "Secret Sprawl",
"definition": "A defect in which credentials and keys are stored across code, logs and configuration.",
"type": "anti-pattern",
"scope": [
"modularity",
"security_governance"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Store credentials, tokens, keys, certificates, or sensitive configuration across code, config files, logs, tickets, and local environments.",
"detected_by": [
"secret_in_repo",
"secret_in_log",
"shared_static_token",
"manual_secret_distribution"
],
"measured_by": ["secrets found in source and logs"],
"refactored_by": [
"architecture:secrets-management",
"lexicon:secret-rotation",
"lexicon:repository-secret-scan",
"lexicon:least-privilege-credential"
],
"enforced_by": [
"secret scanning in the build",
"a secret store"
],
"severity": "discouraged",
"exemplar": {
"before": "const key = \"sk_live_abc123\";\nconst dbPass = \"hunter2\";",
"after": "const key = await secrets.get(\"foo.api.key\");\nconst dbPass = await secrets.get(\"foo.db.password\");",
"lang": "ts"
}
},
{
"id": "personal-data-oversharing",
"name": "Personal Data Oversharing",
"definition": "A defect in which more personal data is collected, kept, logged or exposed than the declared purpose needs.",
"aliases": ["Unbounded Data Collection"],
"type": "anti-pattern",
"scope": ["architecture_evolution"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Collect, store, log, transmit, or expose more personal data than needed for the declared purpose.",
"detected_by": [
"personal_data_in_logs",
"unused_sensitive_fields",
"broad_export",
"missing_data_minimization"
],
"measured_by": ["personal-data fields in logs and exports"],
"refactored_by": [
"lexicon:purpose-binding",
"lexicon:field-redaction",
"lexicon:retention-policy"
],
"enforced_by": [
"privacy review",
"log redaction rules"
],
"severity": "discouraged",
"exemplar": {
"before": "logger.info(\"created foo\", { email: user.email, ssn: user.ssn });",
"after": "logger.info(\"created foo\", { userId: user.id });",
"lang": "ts"
}
},
{
"id": "observability-noise",
"name": "Observability Noise",
"definition": "A defect in which logs, metrics and alerts are too many and too low in signal to act on.",
"type": "anti-pattern",
"scope": ["observability_traceability"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Emit excessive, low-signal logs, metrics, traces, or alerts without severity, ownership, cardinality control, or actionability.",
"detected_by": [
"high_alert_ack_without_action",
"high_cardinality_metrics",
"logs_without_context",
"duplicate_alerts"
],
"measured_by": [
"alerts acknowledged without action",
"high-cardinality metrics"
],
"refactored_by": [
"lexicon:define-signal-quality",
"lexicon:cardinality-reduction",
"lexicon:runbook-owner",
"lexicon:sampling-and-aggregation"
],
"enforced_by": [
"alert-ownership rules",
"metric cardinality limits"
],
"severity": "discouraged",
"exemplar": {
"before": "logger.info(\"entering loop\");\nfor (const f of foos) logger.info(\"iter\", f);",
"after": "logger.info(\"foo.batch.processed\", { count: foos.length, durationMs });",
"lang": "ts"
}
},
{
"id": "log-as-control-flow",
"name": "Log-as-Control-Flow",
"definition": "A defect in which a failure is logged as though logging handled it, and execution continues.",
"type": "anti-pattern",
"scope": [
"correctness_verification",
"resilience_recovery"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Log errors or warnings as if logging itself handles the failure, while the system continues without recovery, propagation, or safe fallback.",
"detected_by": [
"catch_log_continue",
"logged_error_without_return_or_throw",
"critical_log_no_alert"
],
"measured_by": ["catch blocks that log and continue"],
"refactored_by": [
"lexicon:introduce-typed-result",
"lexicon:fail-fast-or-fall-back",
"lexicon:recovery-policy",
"lexicon:alert-on-critical-failure"
],
"enforced_by": ["a lint rule against catch blocks that only log"],
"severity": "discouraged",
"exemplar": {
"before": "try { await chargeFoo(foo); } catch (error) { logger.error(\"foo.charge.failed\", error); }\nshipFoo(foo);",
"after": "const outcome = await chargeFoo(foo);\nif (!outcome.ok) throw new ChargeFailedError(foo.id, outcome.error);\nshipFoo(foo);",
"lang": "ts"
}
},
{
"id": "manual-runbook-dependency",
"name": "Manual Runbook Dependency",
"definition": "A defect in which repeatable operational steps, such as detecting a failure and recovering from it, are performed by hand during incidents and deploys, so recovery waits on a person.",
"aliases": [
"Manual-Only Recovery",
"Manual Intervention Dependency"
],
"type": "anti-pattern",
"scope": ["resilience_recovery"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Perform by hand the repeatable operational actions during incidents, deploys, migrations, or recovery.",
"detected_by": [
"same_manual_incident_steps",
"manual_migration_sequence",
"operator_specific_knowledge"
],
"measured_by": ["manual steps repeated across incidents"],
"refactored_by": [
"lexicon:automate-the-runbook",
"lexicon:guardrails",
"lexicon:precondition-check",
"lexicon:execution-log"
],
"enforced_by": ["operations review of manual steps repeated across incidents"],
"severity": "discouraged",
"exemplar": {
"before": "const RUNBOOK = \"on failure, ssh in and run restart-foo.sh\";",
"after": "health.onUnhealthy(() => orchestrator.restart(\"foo\"));",
"lang": "ts"
}
},
{
"id": "big-bang-release",
"name": "Big-Bang Release",
"definition": "A defect in which a large, irreversible change reaches every user at once, with no staged rollout.",
"aliases": ["Big-Bang Deployment"],
"type": "anti-pattern",
"scope": ["state_transaction"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Ship a large, irreversible, all-user change without staged rollout, feature flags, canary, rollback, or blast-radius control.",
"detected_by": [
"no_canary",
"no_feature_flag",
"no_rollback_plan",
"large_release_batch"
],
"measured_by": [
"release batch size",
"releases without a rollback path"
],
"refactored_by": [
"architecture:feature-toggle",
"architecture:canary-deployment",
"architecture:blue-green-deployment",
"lexicon:rollback-plan",
"lexicon:small-batch-release"
],
"enforced_by": ["release gates that require a canary or a feature flag and a rollback plan"],
"severity": "discouraged",
"exemplar": {
"before": "deployEverything(\"foo\", \"bar\", \"baz\");",
"after": "release(\"foo\", { strategy: canary(0.1) });",
"lang": "ts"
}
},
{
"id": "irreversible-migration",
"name": "Irreversible Migration",
"definition": "A defect in which a schema or data change can neither run beside the old version nor be rolled back.",
"type": "anti-pattern",
"scope": [
"contract_compatibility",
"architecture_evolution"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Apply schema, data, or infrastructure changes that cannot safely run alongside old versions or be rolled back.",
"detected_by": [
"drop_column_before_consumers_removed",
"destructive_data_transform_no_backup",
"no_backward_compatible_phase"
],
"measured_by": ["migrations without a down step or a compatible phase"],
"refactored_by": [
"lexicon:expand-contract-migration",
"lexicon:backup",
"lexicon:dual-read-and-write",
"lexicon:rollback-plan"
],
"enforced_by": [
"migration review",
"rollback tests"
],
"severity": "discouraged",
"exemplar": {
"before": "await db.exec(\"ALTER TABLE foo DROP COLUMN legacy_name\");",
"after": "await migrate({ up: addFooName, down: restoreFooName });",
"lang": "ts"
}
},
{
"id": "big-upfront-frozen-architecture",
"distinctFrom": [
{
"id": "architecture:lava-flow",
"reason": "Frozen architecture fixes decisions too early, while lava flow keeps obsolete code too long."
},
{
"id": "architecture:premature-abstraction",
"reason": "Frozen architecture fixes system-level decisions early, while premature abstraction extracts one shared abstraction early."
},
{
"id": "architecture:zombie-code",
"reason": "Frozen architecture is a decision made too early, while zombie code is dead code left behind."
}
],
"name": "Big-Upfront Frozen Architecture",
"definition": "A defect in which major architectural decisions are fixed before the forces they answer are known.",
"type": "anti-pattern",
"scope": ["domain_boundary"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Lock in major architectural decisions before validating domain forces, quality attributes, operational realities, and change vectors.",
"detected_by": [
"heavy_architecture_before_usage",
"ADR_without_evidence",
"future-proofing_without_feedback"
],
"measured_by": ["decisions recorded without evidence"],
"refactored_by": [
"architecture:minimum-viable-architecture",
"architecture:evolutionary-architecture",
"architecture:fitness-functions",
"lexicon:decision-review"
],
"enforced_by": [
"decision records that require evidence",
"periodic decision review"
],
"severity": "discouraged",
"exemplar": {
"before": "const ARCHITECTURE = designAllModulesForNextFiveYears();",
"after": "const foo = defineModule(\"foo\", { exports: { createFoo } });\nregistry.add(foo);",
"lang": "ts"
}
},
{
"id": "architecture-astronaut",
"distinctFrom": [
{
"id": "architecture:over-abstraction",
"reason": "An astronaut builds frameworks for needs that have not arrived, while over-abstraction adds layers beyond the variation that exists."
},
{
"id": "architecture:speculative-generality",
"reason": "An astronaut builds whole frameworks and meta-models, while speculative generality is one abstraction built for variation with no evidence of arriving."
}
],
"name": "Architecture Astronaut",
"definition": "A defect in which abstract frameworks, meta-models and architectural structure are built ahead of the concrete needs they should serve.",
"aliases": ["Over-Architecture"],
"type": "anti-pattern",
"scope": [
"model_governance",
"domain_boundary"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Prefer abstract frameworks, taxonomies, meta-models, and generic engines over concrete user, domain, and operational needs.",
"detected_by": [
"generic_platform_before_product_need",
"few_real_consumers",
"high_framework_workaround_count"
],
"measured_by": ["generic components with few real consumers"],
"refactored_by": [
"lexicon:anchor-to-use-cases",
"lexicon:vertical-slice-proof",
"lexicon:inline-abstraction",
"lexicon:measure-delivery-cost"
],
"enforced_by": ["design review against real use cases"],
"severity": "discouraged",
"exemplar": {
"before": "class AbstractFooMetaStrategyOrchestrationEngineFactory {}",
"after": "class CreateFoo { execute(input: CreateFooInput): Foo {} }",
"lang": "ts"
}
},
{
"id": "feature-only-design",
"name": "Feature-Only Design",
"definition": "A defect in which architecture serves immediate features while its quality attributes go unaddressed.",
"type": "anti-pattern",
"scope": [
"security_governance",
"performance_scaling"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Optimize architecture for immediate feature delivery while ignoring quality attributes such as security, operability, scalability, maintainability, and evolvability.",
"detected_by": [
"no_SLOs",
"no_security_review",
"no_operability_requirements",
"quality_attribute_absent_from_ADR"
],
"measured_by": [
"quality attributes absent from decisions",
"services without SLOs"
],
"refactored_by": [
"lexicon:quality-scenarios",
"architecture:fitness-functions",
"architecture:architecture-review",
"lexicon:risk-register"
],
"enforced_by": [
"architecture review with quality scenarios",
"SLO gates"
],
"severity": "discouraged",
"exemplar": {
"before": "function addFooFeature() { hack(); patch(); bypassLint(); }",
"after": "function addFooFeature(input: CreateFooInput) { return createFoo.execute(input); }",
"lang": "ts"
}
},
{
"id": "test-pyramid-inversion",
"name": "Test Pyramid Inversion",
"definition": "A defect in which a test suite relies mainly on slow end-to-end tests, with few unit and contract tests.",
"type": "anti-pattern",
"scope": ["contract_compatibility"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Rely mainly on slow, brittle end-to-end tests while unit, contract, component, and property tests are sparse.",
"detected_by": [
"high_E2E_ratio",
"slow_CI",
"flaky_integration_tests",
"low_unit_contract_coverage"
],
"measured_by": [
"share of end-to-end tests",
"suite duration"
],
"refactored_by": [
"lexicon:unit-tests",
"lexicon:contract-testing",
"lexicon:component-tests",
"architecture:property-based-testing"
],
"enforced_by": [
"test-suite composition review",
"limits on CI duration"
],
"severity": "discouraged",
"exemplar": {
"before": "test.e2e(\"create foo\", fullBrowserFlow);\ntest.e2e(\"rename foo\", fullBrowserFlow);\ntest.e2e(\"delete foo\", fullBrowserFlow);",
"after": "test.unit(\"FooValidator rejects an empty name\", () => expect(() => validateFoo({ name: \"\" })).toThrow());\ntest.integration(\"FooRepository persists a Foo\", async () => {\n await fooRepository.save(foo);\n expect(await fooRepository.find(foo.id)).toEqual(foo);\n});\ntest.e2e(\"the critical signup path\", criticalPathOnly);",
"lang": "ts"
}
},
{
"id": "mock-mirage",
"name": "Mock Mirage",
"definition": "A defect in which tests assert calls on mocks rather than observable behavior or contracts.",
"type": "anti-pattern",
"scope": [
"contract_compatibility",
"correctness_verification",
"observability_traceability"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Overuse mocks so tests verify internal calls rather than observable behavior or contracts.",
"detected_by": [
"tests_fail_on_refactor_without_behavior_change",
"assert_called_everywhere",
"no_contract_tests"
],
"measured_by": ["tests that break on refactors with no change in behavior"],
"refactored_by": [
"lexicon:test-observable-behavior",
"lexicon:contract-testing",
"lexicon:fake-at-the-boundary"
],
"enforced_by": [
"contract tests",
"review of mock usage"
],
"severity": "discouraged",
"exemplar": {
"before": "const store = { save: fn(), find: fn().returns(foo) };",
"after": "const store = new InMemoryFooStore();\nrunFooStoreContract(store);",
"lang": "ts"
}
},
{
"id": "flaky-test-normalization",
"name": "Flaky Test Normalization",
"definition": "A defect in which intermittent test failures are accepted and rerun until they pass.",
"type": "anti-pattern",
"scope": [
"semantic_consistency",
"correctness_verification"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Accept intermittent test failures as normal and rerun until green instead of fixing nondeterminism or isolation defects.",
"detected_by": [
"rerun_to_pass",
"quarantined_tests_never_fixed",
"time_order_random_test_failures"
],
"measured_by": [
"rerun rate",
"quarantined tests past their fix date"
],
"refactored_by": [
"lexicon:isolate-test-state",
"lexicon:inject-clock-and-randomness",
"lexicon:fix-the-race",
"lexicon:fake-at-the-boundary"
],
"enforced_by": ["a CI policy that fails on a retried test and tracks every quarantined one"],
"severity": "discouraged",
"exemplar": {
"before": "test.retry(5)(\"foo works sometimes\", async () => { await sleep(random()); expect(await getFoo()).toBeTruthy(); });",
"after": "test(\"foo is created deterministically\", async () => { const foo = await createFoo.execute(input); expect(foo.id).toBe(expectedId); });",
"lang": "ts"
}
},
{
"id": "prompt-sprawl",
"name": "Prompt Sprawl",
"definition": "A defect in which prompts, model parameters and output schemas are scattered through code without versions or evaluation.",
"type": "anti-pattern",
"scope": [
"contract_compatibility",
"model_governance"
],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Scatter prompts, retrieval rules, model parameters, safety instructions, and output schemas across code without versioning, evaluation, or ownership.",
"detected_by": [
"prompt_literals_in_many_files",
"no_prompt_registry",
"no_eval_for_prompt_change",
"model_params_scattered"
],
"measured_by": ["prompt literals outside the registry"],
"refactored_by": [
"lexicon:prompt-registry",
"lexicon:prompt-versioning",
"lexicon:evaluation-suite",
"lexicon:centralized-model-configuration"
],
"enforced_by": ["a prompt registry, with an evaluation required on every change"],
"severity": "discouraged",
"exemplar": {
"before": "const a = model.run(\"summarize this foo: \" + foo);\nconst b = model.run(\"pls summarize foo \" + foo);",
"after": "const summary = model.run(FOO_PROMPTS.summarize({ foo }));",
"lang": "ts"
}
},
{
"id": "ungrounded-content",
"name": "Ungrounded Content",
"definition": "A defect in which the model produces answers or decisions without retrieved evidence or disclosed uncertainty.",
"type": "anti-pattern",
"scope": ["model_governance"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Generate answers, classifications, plans, or decisions without evidence retrieval, source references, confidence limits, or unsupported-claim handling.",
"detected_by": [
"answer_without_sources_when_sources_required",
"no_retrieval_trace",
"unsupported_claims",
"confidence_not_disclosed"
],
"measured_by": ["answers without a source where one is required"],
"refactored_by": [
"architecture:retrieval-augmented-generation",
"lexicon:evidence-citation",
"lexicon:claim-validation",
"lexicon:abstain-or-disclose-uncertainty"
],
"enforced_by": [
"retrieval evaluation",
"claim validation against the retrieved sources"
],
"severity": "discouraged",
"exemplar": {
"before": "const answer = await model.run(question);\nreturn answer;",
"after": "const context = await retrieve(question);\nconst answer = await model.run(FOO_PROMPTS.answer({ question, context }));\nreturn withCitations(answer, context);",
"lang": "ts"
}
},
{
"id": "model-version-ambiguity",
"name": "Model Version Ambiguity",
"definition": "A defect in which models, prompts or indexes are used without recording their version and configuration.",
"type": "anti-pattern",
"scope": ["model_governance"],
"requires": [],
"reinforces": [],
"enables": [],
"conflicts_with": [],
"tensions_with": [],
"formed_by": "Use models, embeddings, prompts, or evaluation artifacts without recording version, configuration, dataset, or inference context.",
"detected_by": [
"model_name_missing_version",
"embedding_index_unversioned",
"eval_results_without_config",
"prompt_not_versioned"
],
"measured_by": ["inferences logged without a model version"],
"refactored_by": [
"lexicon:model-registry",
"lexicon:prompt-versioning",
"lexicon:inference-context-record",
"lexicon:governance-log"
],
"enforced_by": [
"a model registry",
"inference logging that requires a version"
],
"severity": "discouraged",
"exemplar": {
"before": "const result = await model.run(prompt);",
"after": "const result = await model.run(prompt, { model: \"foo-llm-2024-06\", temperature: 0 });\nlogger.info(\"foo.inference\", { model: result.model });",
"lang": "ts"
}
}
]
}