Skip to main content

Engineering Anti-Patterns

What's silently degrading the codebase — and how do you find it before it compounds?

Anti-patterns are not bugs. They compile, they ship, they pass tests. They degrade slowly — each one slightly increasing the cost of the next change until the codebase resists improvement. Detection is the discipline. See Engineering Quality Benchmarks for the thresholds that define healthy.

Architecture Violations

Inline Adapter Construction

// In server actions or API routes
const db = new PostgresDatabaseAdapter(drizzle(connectionString));
const repo = new ContactsDrizzleRepository(db);

Pushes infrastructure into the bootstrap layer. Breaks hex boundaries. Creates multiple adapter instances instead of singletons. Makes testing harder.

Detection: Grep for new.*Adapter or new.*Repository in apps/ and server action files.

Fix: Use composition root getters. All wiring goes through singleton factory functions.


Infrastructure in Application Layer

// In use-cases or application services
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import type { LlamaParseReader } from 'llamaindex';

Couples use-cases to concrete implementations. Defeats the entire port/adapter pattern.

Detection: bannedExternalImports in ESLint config for application-type libraries. Should ban drizzle, database adapters, and infrastructure packages.

Fix: Import only from domain ports. Inject concrete implementations via composition root.


Domain Importing Frameworks

// In domain layer
import { useEffect } from 'react';
import { NextRequest } from 'next/server';

Domain must be framework-agnostic. Zero external dependencies beyond types and pure utilities.

Detection: bannedExternalImports on domain libraries — must include react, next, drizzle, ORM packages, UI frameworks.

Fix: Move framework-dependent code to the appropriate layer (UI, infrastructure, composition).


Import Anti-Patterns

God Barrels

export * from './services';
export * from './adapters';
export * from './models';

Creates barrels that defeat tree-shaking and explode module counts. A single import from a composition library can transitively pull the entire dependency graph into every page.

Detection: Grep for export * excluding entry-points/ directories. ESLint no-restricted-syntax banning ExportAllDeclaration.

Fix: Explicit named exports only. Create subpath entry-points per feature so consumers import only what they need.


Root Barrel When Subpaths Exist

import { Button } from '@myorg/ui';              // pulls everything
import { Button } from '@myorg/ui/atoms'; // pulls only atoms

Single component import pulls entire library into the bundle.

Detection: no-restricted-imports banning root barrel paths from app code.

Fix: Always use the most specific subpath available.


Cross-Boundary Source Imports

import { something } from '../../../libs/application/src/index';
import { something } from '@myorg/application/src/lib/internal';

Bypasses TypeScript project references. Causes TS6305 errors. Breaks the Nx project graph.

Detection: Grep for from '.*libs/.*src/ across apps and libs.

Fix: Import via package alias only. Never reference another library's src/ directory.


TypeScript Config

Disabling Composite

Setting composite: false to make TS errors disappear. Disables project references. Downstream projects lose incremental build benefits. Creates a time bomb that surfaces as TS6305 cascades later.

Detection: Grep for "composite": false in tsconfig.lib.json files.

Fix: Keep composite: true. Fix the actual import or reference issue. See TypeScript for config patterns.


RootDir Misalignment

{
"compilerOptions": { "rootDir": "src" },
"include": ["src/**/*.ts", "../../types/**/*.d.ts"]
}

The include references files outside rootDir. Works locally, fails in CI with TS6059.

Detection: Compare rootDir against include paths in every tsconfig.lib.json.

Fix: Either expand rootDir or move the referenced files under src/.


Global SkipLibCheck

Adding skipLibCheck: true to the root tsconfig. Masks declaration mismatches between libraries. Real type errors go undetected until runtime.

Detection: Check tsconfig.base.json for the flag.

Fix: Fix declaration errors at source. Use skipLibCheck only per-project for a specific third-party library.


Nx Config

Wildcard Constraints

{ "sourceTag": "scope:intelligence", "onlyDependOnLibsWithTags": ["*"] }

Defeats boundary enforcement entirely. That scope can import anything.

Detection: Grep eslint config for "*" in depConstraints.

Fix: Replace with explicit allowed tags. Every constraint should name specific types and scopes.


Missing Library Tags

New library added with no tags in project.json. Unconstrained by default — can import and be imported by anything.

Detection: Find all project.json files missing non-empty tags array.

Fix: Every library declares at minimum type:* and scope:* tags at creation.


Growing Disable Comments

// eslint-disable-next-line @nx/enforce-module-boundaries
import { DatabaseAdapter } from '@myorg/database-adapters';

Each disable is a boundary violation. They accumulate silently.

Detection: Count eslint-disable.*enforce-module-boundaries across the repo. Track per PR.

Fix: Track count as a metric trending to zero. Every new disable requires justification and a ticket to remove it.


Agent-Specific

AI coding agents introduce their own failure modes.

Mass Import Rewriting

Auto-generating scripts to rewrite imports across package boundaries. Doesn't respect architectural boundaries. Can repoint imports to src/ paths.

Fix: Fix imports manually per architecture rules. Use ESLint to prevent regression.

Relaxing Compiler Options

Agent encounters TS errors, disables composite, adds skipLibCheck, loosens strict. Trades compile-time safety for silence.

Fix: CLAUDE.md rule: never modify tsconfig compiler strictness without explicit human approval.

Service Locator Revival

Agent creates ServiceLocator.get(Adapter) pattern. Hides dependencies. Makes the composition root irrelevant.

Fix: All wiring goes through composition root. Grep for service locator patterns in CI.


Quick Audit

Run these periodically. Each reveals a category of debt.

CommandWhat It Finds
Grep for eslint-disable.*enforce-module-boundariesBoundary violations
Grep for from '.*libs/.*src/Cross-boundary imports
Grep for export * excluding entry-pointsGod barrels
Grep for new.*Adapter or new.*Repository in actionsInline construction
Grep for "*" in eslint depConstraintsWildcard constraints
Find project.json missing tagsUnconstrained libraries
Grep for "composite": false in tsconfigsDisabled project references

Context