Anti-Patterns to Avoid
Learn about common anti-patterns in software development and migration. Understand risks, warning signs, and better alternatives.
Big Bang Migration
Attempting to migrate an entire system at once instead of incrementally
Distributed Monolith
Splitting a monolith into microservices that are still tightly coupled and must be deployed together
Premature Optimization
Optimizing code or architecture before understanding actual performance requirements
Not Invented Here (NIH)
Rejecting perfectly good external solutions in favor of building custom ones
Golden Hammer
Using a familiar technology for every problem regardless of fit
Cargo Cult Programming
Using patterns or practices without understanding why they work
Lava Flow
Dead code that no one dares to remove because they don't understand it
Shotgun Surgery
Making a single change requires modifications across many different classes or modules
Shared Database Integration
Multiple services directly sharing the same database tables
Migration Feature Creep
Adding new features or improvements during a migration instead of focusing on parity
God Object
A single class or module that knows or does too much, concentrating most of the system's responsibilities in one place and becoming a maintenance bottleneck.
Big Ball of Mud
A system with no discernible architecture, where code is haphazardly structured, tangled, and duct-taped together, making every change risky and slow.
Accidental Complexity
Complexity introduced by the solution rather than the problem — overbuilt tooling, layers, and abstractions that obscure logic that is actually simple.
Vendor Lock-In
Designing a system so deeply around one provider's proprietary services that switching becomes prohibitively expensive, eroding negotiating power and portability.
Inner-Platform Effect
Building a configurable system so general it becomes a poor reimplementation of the platform it runs on, reinventing a language, database, or framework badly.
Stovepipe System
Independently built, siloed systems that duplicate capabilities and cannot interoperate because each was designed in isolation without shared standards.
Swiss Army Knife
An interface or component with so many options and overloads that it tries to cover every use case, becoming hard to learn, misuse-prone, and impossible to evolve.
Magic Pushbutton
Putting business logic directly in UI event handlers, so a single button click handler holds validation, rules, and persistence with no separation of concerns.
Reinventing the Wheel
Building from scratch a solved, well-supported capability — like crypto, date handling, or an ORM — instead of using a proven, maintained library or standard.
Boat Anchor
Keeping a piece of obsolete software, hardware, or a dependency that no longer serves a purpose but is retained and maintained out of inertia or sunk-cost thinking.
Dependency Hell
A tangle of conflicting, version-pinned, or transitive dependencies that makes upgrading or even installing software fragile, slow, and unpredictable.
Circular Dependency
Two or more modules that depend on each other directly or transitively, forming a cycle that prevents independent building, testing, and reasoning.
Leaky Abstraction
An abstraction that fails to fully hide its underlying implementation, forcing callers to understand and depend on the details it was meant to encapsulate.
Anemic Domain Model
Domain objects that hold data but no behavior, with all logic pushed into separate service classes, draining the object model of its purpose.
Fat Controller
Web or API controllers that accumulate business logic, validation, and data access instead of delegating, becoming bloated and impossible to test or reuse.
Smart UI
Concentrating business logic, data access, and rules inside the presentation layer, fusing UI and domain so neither can change or be tested independently.
Over-Engineering
Building more generality, flexibility, or sophistication than the problem requires, adding cost and complexity for capabilities that are never actually needed.
Premature Abstraction
Extracting abstractions before enough concrete cases exist to know what they should be, locking in the wrong shape and adding indirection that obstructs change.
Nanoservices
Splitting a system into services so small that coordination, network, and operational overhead vastly exceed the value of each tiny service.
Entity Service
Designing microservices around data entities rather than business capabilities, forcing every workflow to orchestrate chatty calls across CRUD-only services.
Spaghetti Code
Code with tangled, unstructured control flow and no clear modularity, where execution jumps unpredictably and dependencies are impossible to follow.
Boolean Trap
Function parameters that take a bare boolean force readers to decode opaque true/false call sites, hiding intent and inviting wrong arguments.
Stringly Typed Code
Using strings to represent data that has real structure or a fixed set of values, discarding type safety and pushing errors to runtime.
Primitive Obsession
Modeling domain concepts with raw primitives like int and string instead of dedicated types, scattering validation and inviting invalid data.
Magic Numbers
Unexplained numeric literals embedded in code, hiding their meaning and duplicating values that must change together.
Magic Strings
Hardcoded string literals that act as keys, flags, or identifiers, with no central definition, inviting typos and silent failures.
Long Method
A single function that does too much and runs for hundreds of lines, mixing many concerns and resisting comprehension, testing, and reuse.
Long Parameter List
A function signature with too many parameters, making calls error-prone, hard to read, and a sign of poorly grouped or missing abstractions.
Data Clumps
The same group of fields or parameters traveling together everywhere, signaling a missing abstraction that should be a single object.
Feature Envy
A method that is more interested in another class's data than its own, repeatedly reaching into that class instead of letting it own the behavior.
Exception Swallowing
Catching exceptions and then ignoring them, hiding failures so that errors pass silently and bugs become nearly impossible to diagnose.
Null Checking Everywhere
Defensive null checks scattered through the codebase to guard against nulls that could be designed away, cluttering logic and still missing cases.
Refused Bequest
A subclass that inherits methods or data it does not want or use, often overriding them to do nothing, signaling a wrong inheritance relationship.
Yo-Yo Problem
An inheritance hierarchy so deep that understanding behavior forces constant scrolling up and down between many classes to trace a single call.
Call Super
A framework requiring subclass overrides to call the parent method, a fragile contract that breaks silently whenever a developer forgets the call.
Temporal Coupling
Methods that must be called in a specific hidden order, where calling them out of sequence silently breaks state with no compiler protection.
Switch Statement Smell
Repeated switch or if-else chains branching on a type code, duplicated across the codebase, that should be replaced by polymorphism.
Poltergeist
A short-lived, do-nothing class that only passes data or calls to other objects, adding indirection and noise without real responsibility.
Sequential Coupling
A class designed so its methods must be invoked in a rigid sequence, with the ordering enforced only by convention rather than by the API itself.
Copy-Paste Programming
Duplicating blocks of code instead of factoring out shared logic, so every fix and change must be repeated across each copy, and some are missed.
Hardcoding
Embedding values that should be configurable, such as URLs, paths, credentials, and limits, directly in source, forcing code changes to adapt.
Redundant Comments
Comments that merely restate what the code already says, adding noise, drifting out of date, and masking the absence of self-explanatory code.
N+1 Query Problem
Loading a list, then firing one extra query per row to fetch related data, turning a single page load into hundreds of round-trips.
God Table
A single table accumulating dozens or hundreds of unrelated columns for many concepts, becoming a contention and maintenance bottleneck.
Entity-Attribute-Value (EAV) Abuse
Storing arbitrary attributes as rows of name/value pairs to fake a schemaless model, sacrificing type safety, integrity, and query performance.
One True Lookup Table (OTLT)
Cramming every reference list into one generic lookup table keyed by category, defeating foreign keys and mixing unrelated domains.
SELECT * Everywhere
Querying all columns by default instead of naming the ones you need, wasting I/O and creating brittle coupling to schema order and shape.
Implicit Columns in INSERT
Writing INSERT statements without naming columns, relying on positional order so a schema change silently misaligns or corrupts data.
Premature Denormalization
Duplicating data across tables for speed before any measured need, creating update anomalies and integrity bugs to solve a problem you may not have.
Over-Normalization
Splitting data into so many tables that every read requires a sprawl of joins, hurting performance and readability with no integrity benefit.
Missing Indexes
Querying large tables with no supporting index, forcing full scans that work in testing and collapse under production data volume.
Index Overuse
Adding an index for every column just in case, bloating storage and slowing every write while most indexes are never used by the planner.
Storing Everything as JSON Blobs
Dumping structured data into opaque JSON text columns by default, abandoning constraints, indexing, and queryability the database would provide.
Natural Primary Keys Misuse
Using mutable, business-meaningful values like email or SSN as primary keys, so a real-world change cascades breakage through every referencing row.
Soft Delete Everywhere
Adding an is_deleted flag to every table by default, so all queries must filter it, integrity decays, and tables bloat with dead rows.
Polling the Database
Repeatedly querying a table on a tight loop to detect changes instead of using events or notifications, wasting resources and adding latency.
Chatty Data Access
Making many fine-grained round-trips to the database for one logical operation, so network latency dominates and throughput collapses under load.
Dual Write
Writing the same change to two systems in sequence without a single transaction or log, so a failure between them leaves the stores inconsistent.
No Connection Pooling
Opening a fresh database connection per request or query and closing it after, paying high handshake cost and exhausting connection limits under load.
Unbounded Result Sets
Querying without a LIMIT and loading entire growing tables into memory, so a query that was fine at launch crashes the app as data accumulates.
Timestamp Without Time Zone
Storing instants in local time without zone information, so values are ambiguous across regions and DST shifts, corrupting ordering and reporting.
Floating-Point for Money
Storing monetary amounts in binary floating point, so rounding errors accumulate and totals fail to reconcile to the cent.
Schemaless Sprawl
Treating a schemaless store as license to skip data design, so documents drift into inconsistent shapes that no consumer can reliably read.
Busy-Waiting (Spin-Waiting)
Repeatedly polling a condition in a tight loop instead of blocking, burning CPU cycles while waiting for an event that the scheduler could deliver for free.
Lock Contention
Many threads competing for the same lock, serializing work that should run in parallel and turning a multicore machine into an expensive single-core one.
Deadlock-Prone Locking
Acquiring multiple locks in inconsistent orders so two threads can each hold one lock while waiting for the other, freezing both forever.
Broken Double-Checked Locking
A lazy-initialization idiom that checks a field outside a lock, locks, then checks again — but without proper memory barriers it returns partially constructed objects.
Thread-Per-Request Overload
Spawning a dedicated OS thread for every incoming request, so concurrency is capped by thread count and memory, collapsing under load instead of degrading gracefully.
Blocking the Event Loop
Running CPU-heavy or synchronous work on a single-threaded event loop, stalling every other in-flight request until it finishes.
Synchronous-Over-Async (sync-over-async)
Blocking a thread to wait on an asynchronous operation's result, combining the costs of both models and risking thread-pool starvation or deadlock.
Chatty I/O
Making many small, fine-grained remote or storage calls where a few coarse-grained calls would do, multiplying latency and overhead per operation.
N+1 Network Calls
Fetching a list, then making one additional remote call per item to enrich it, so a single logical operation fans out into N+1 dependency calls.
Memory Leak
Allocating memory that is never released because references are unintentionally retained, so usage grows without bound until the process slows, thrashes, or crashes.
Unbounded Cache
A cache with no size limit, eviction, or expiry that grows until it consumes all memory and turns a performance optimization into an out-of-memory failure.
Cache Stampede (Dogpile Effect)
When a hot cache entry expires, many concurrent requests all miss and recompute it at once, hammering the backing store and amplifying load instead of absorbing it.
Retry Storm
Aggressive, uncoordinated retries during a partial outage that multiply traffic against an already-struggling dependency and turn a blip into a full collapse.
Thundering Herd
Many clients or threads waking and acting at the same instant — on a recovery, a timer, or a wakeup — creating a synchronized load spike that overwhelms the resource they target.
Head-of-Line Blocking
A single slow or stuck item at the front of a strictly ordered queue holds up everything behind it, even when the later items are unrelated and ready to proceed.
Hot Partition (Hot Shard)
A partitioning key that sends a disproportionate share of traffic to one shard, overloading it while the rest sit idle and capping the system at one node's throughput.
False Sharing
Independent variables that happen to live on the same CPU cache line, so updates by different cores invalidate each other's caches and silently destroy multicore performance.
Missing Back-Pressure
A producer that pushes work faster than the consumer can handle, with no mechanism to slow it down, so queues grow unbounded until memory or the downstream collapses.
Resource Leak
Acquiring file handles, sockets, connections, or threads without reliably releasing them, so a finite pool is exhausted and the application stops being able to do work.
Over-Caching
Adding caches everywhere to chase speed, multiplying staleness, invalidation bugs, and operational complexity for marginal gains that profiling never justified.
Premature Scaling
Building for massive scale before there is load to justify it, paying the cost and complexity of distributed systems to solve problems the product does not yet have.
Snowflake Server
A server hand-configured over time into a unique, irreproducible state that no one can recreate, document, or safely replace.
Configuration Drift
Environments that should be identical gradually diverge as undocumented manual changes accumulate, breaking reproducibility and causing inconsistent behavior.
Manual Deployment
Releasing software through hand-run steps and checklists instead of automation, producing slow, error-prone, irreproducible deploys that depend on individuals.
Works on My Machine
Code that runs only in a developer's local setup because of undeclared dependencies and environment assumptions, then fails everywhere else.
Environment Parity Gap
Development, staging, and production environments differ enough that testing in one gives little confidence about behavior in another.
ClickOps
Provisioning and changing cloud infrastructure by hand through web consoles, producing undocumented, irreproducible, and unauditable configuration.
No Infrastructure as Code
Running infrastructure without any code-based definition, so it cannot be versioned, reviewed, reproduced, or recovered systematically.
Pets vs Cattle (Pet Servers)
Treating individual servers as irreplaceable pets that are named, nurtured, and manually healed, instead of disposable cattle that are replaced on failure.
Monolithic Pipeline
A single, all-or-nothing CI/CD pipeline that builds, tests, and deploys everything together, making it slow, fragile, and hard to change safely.
Flaky Pipeline
A CI/CD pipeline that fails intermittently for reasons unrelated to the code change, eroding trust and training teams to ignore red builds.
No Rollback Plan
Deploying with no tested, fast way to revert, so a bad release means scrambling under pressure while the outage drags on.
Deploy and Pray
Pushing releases to production with no automated verification, monitoring, or rollback, then hoping nothing breaks instead of knowing it works.
Over-Provisioning
Allocating far more compute, memory, or capacity than workloads need, wasting money for headroom that is rarely used.
Under-Provisioning
Allocating too little capacity to save money, leaving workloads starved so they slow down, fail, or fall over under load.
Cloud Bill Shock
An unexpectedly huge cloud invoice arriving because spend was never tracked, attributed, or governed until the bill landed.
Lift and Shift Without Optimization
Moving applications to the cloud unchanged and stopping there, inheriting on-prem inefficiencies while paying cloud prices and gaining none of the benefits.
Single Region, No Disaster Recovery
Running an entire system in one region or data center with no disaster-recovery plan, so a regional failure takes everything down with no recovery path.
No Monitoring (Flying Blind)
Running production systems with no metrics, logs, or alerts, so problems are invisible until users complain and incidents cannot be diagnosed.
Alert Fatigue
So many low-value or noisy alerts fire that responders become desensitized and start ignoring them, including the ones that actually matter.
Log Everything (Logging Noise)
Logging indiscriminately at high verbosity, burying useful signal in a flood of low-value messages while driving up storage cost and slowing search.
Latest Tag in Production
Deploying container images by the mutable :latest tag, so production runs an unknown, changing version that cannot be reliably reproduced or rolled back.
Hardcoded Secrets
Embedding API keys, passwords, or tokens directly in source code, where they leak through version control, logs, and shared binaries.
Security Through Obscurity
Relying on secrecy of design or implementation as the primary defense, rather than on sound, reviewable security controls.
Rolling Your Own Crypto
Designing or implementing custom cryptographic algorithms or protocols instead of using vetted, standard libraries and primitives.
Plaintext Password Storage
Storing user passwords as readable text or with reversible/fast encoding, so a single database breach exposes every credential.
SQL Injection via String Concatenation
Building SQL queries by concatenating untrusted input into the query string, letting attackers alter query logic and access or destroy data.
Overly Permissive CORS
Configuring Cross-Origin Resource Sharing to allow any origin (or reflecting any origin with credentials), exposing authenticated APIs to malicious sites.
Wildcard IAM Permissions
Granting broad cloud permissions with wildcards like Action:* or Resource:*, violating least privilege and widening the blast radius of any compromise.
Shared Admin Accounts
Multiple people using one privileged login (root, admin, a shared service account), destroying accountability and making credential rotation and offboarding impossible.
Missing Input Validation
Accepting and processing external input without checking its type, range, format, or size, opening the door to injection, corruption, and crashes.
Trusting Client-Side Validation
Relying on browser or app-side checks as the security boundary, when any client can be bypassed and send arbitrary requests directly to the server.
Verbose Error Leakage
Returning stack traces, SQL errors, internal paths, or version details to clients, handing attackers a map of the system to exploit.
Default Credentials
Shipping or deploying systems with vendor default usernames and passwords left unchanged, an instantly exploitable and heavily automated attack vector.
Long-Lived Static Credentials
Using permanent API keys and access tokens that never expire and are rarely rotated, maximizing the value and lifespan of any leak.
No MFA on Privileged Access
Protecting administrator, root, and high-value accounts with a single password, so one phishing or credential leak yields full takeover.
Publicly Exposed Storage Buckets
Leaving cloud object storage open to anonymous read or write, a leading cause of large-scale data leaks from simple misconfiguration.
Disabled TLS Certificate Verification
Turning off certificate validation in HTTPS clients to silence errors, removing the protection TLS provides against man-in-the-middle attacks.
JWT none Algorithm Acceptance
Accepting JSON Web Tokens with alg:none or trusting the token's own algorithm header, letting attackers forge tokens with no valid signature.
Mass Assignment
Binding incoming request data directly onto domain objects, letting attackers set fields like isAdmin or accountBalance that were never meant to be writable.
Insecure Deserialization
Deserializing untrusted data with formats or libraries that can instantiate arbitrary types, enabling remote code execution and other attacks.
Logging Sensitive Data
Writing passwords, tokens, PII, or payment data into logs, where it spreads to aggregators and backups far beyond its intended access controls.
Scope Creep
Uncontrolled expansion of project scope after work begins, where requirements grow without matching adjustments to time, budget, or staffing.
Analysis Paralysis
Overanalyzing a decision or design to the point that no decision is made and no progress occurs, trading action for endless deliberation.
Bikeshedding (Law of Triviality)
Spending disproportionate time debating trivial, easy-to-understand details while important, complex decisions receive little scrutiny.
Death March
A project doomed by impossible deadlines or scope, pushed forward through sustained overtime and unsustainable pressure rather than realistic planning.
Hero Culture
A team that depends on a few individuals heroically saving the day, rewarding firefighting over the boring, systemic work that prevents fires.
Bus Factor of One
Critical knowledge or capability concentrated in a single person, so the project halts if that person becomes unavailable.
Knowledge Silos
Information and expertise trapped within individuals or teams, blocking collaboration and forcing others to rediscover what is already known.
Water-Scrum-Fall
A hybrid where agile ceremonies are bolted onto a waterfall lifecycle, with up-front planning and big-bang release bookending a thin layer of Scrum.
Story Point Inflation
Estimates in story points drift upward over time so velocity rises without more real work, turning a planning aid into a gamed vanity number.
Vanity Metrics
Tracking impressive-looking numbers that do not inform decisions or correlate with real outcomes, creating an illusion of progress.
Feature Factory
An organization that measures success by the volume of features shipped rather than the outcomes they produce, optimizing output over impact.
Big Design Up Front (BDUF)
Specifying a complete, detailed design before any implementation begins, betting that requirements are fully known and will not change.
Gold Plating
Adding features, polish, or sophistication beyond what was requested or needed, spending effort on value no stakeholder asked for.
Technical Debt Denial
Refusing to acknowledge or pay down accumulated technical debt, treating short-term delivery speed as if it carried no compounding cost.
Rubber-Stamp Code Reviews
Approving pull requests without meaningful inspection, performing the ceremony of code review while providing none of its protective value.
Meeting Overload
Filling calendars with so many meetings that there is little uninterrupted time left for the focused work the meetings are meant to coordinate.
HiPPO Decision-Making
Decisions driven by the Highest Paid Person's Opinion rather than data, evidence, or the expertise of those closest to the problem.
Blame Culture
An environment that responds to failures by finding someone to punish rather than understanding causes, driving problems underground.
Resume-Driven Development
Choosing technologies for their appeal on a resume or their hype rather than their fit for the problem, optimizing careers over systems.
Documentation Rot
Documentation that drifts out of sync with the system it describes, becoming stale and misleading until teams learn to distrust it entirely.
Ice-Cream Cone (Inverted Test Pyramid)
A test suite dominated by slow manual and end-to-end tests with few unit tests, making feedback slow, brittle, and expensive to maintain.
Flaky Tests
Tests that pass and fail non-deterministically without code changes, eroding trust in the suite and masking real regressions.
Testing Implementation Details
Tests coupled to private internals rather than observable behavior, so harmless refactors break them and real bugs slip through.
Assertion Roulette
A test with many unlabeled assertions, so when one fails it is unclear which condition broke or why, slowing diagnosis.
Mystery Guest
A test that depends on external data or resources not visible in the test itself, making it opaque, fragile, and non-reproducible.
Happy-Path-Only Testing
Tests that exercise only the expected, valid flow and ignore errors, edge cases, and failures — leaving real-world conditions untested.
Excessive Mocking (Mockery)
Replacing nearly every collaborator with mocks so tests verify interactions instead of behavior, becoming brittle and detached from reality.
Slow Test Suite
A test suite so slow that developers stop running it locally and feedback arrives too late, encouraging skipped tests and large risky batches.
Chatty API
An API design that forces clients to make many small round trips to complete one task, harming latency, scalability, and battery life.
Breaking Changes Without Versioning
Changing an API's contract in place without versioning or deprecation, silently breaking existing clients and eroding trust.
Inconsistent API Naming and Conventions
An API where naming, casing, pluralization, error formats, and conventions vary across endpoints, raising the learning curve and integration errors.
Overfetching and Underfetching
Endpoints that return too much data or too little, forcing clients to waste bandwidth or make extra calls to assemble what they need.
Ignoring Idempotency
Designing write operations that cause duplicate effects when retried, so network blips and client retries create double charges or duplicate records.
Missing Pagination (Unbounded Result Sets)
Collection endpoints that return all records at once with no pagination, causing huge payloads, slow queries, and out-of-memory failures as data grows.
Leaky API Abstraction
An API that exposes internal database schemas, implementation details, or storage structures, coupling clients to internals and blocking safe evolution.
Nanoservices (Overly Fine-Grained Services)
Splitting a system into so many trivially small services that coordination, network, and operational overhead dwarf any benefit of separation.
Synchronous Call Chains
Deep chains of blocking request-response calls between services, so latency compounds and one slow or failed service cascades into widespread failure.
Entity Services
Decomposing microservices around data entities (CRUD wrappers per table) rather than business capabilities, creating chatty, anemic, tightly coupled services.
Death Star (Distributed Big Ball of Mud)
A microservice estate where every service calls nearly every other with no clear boundaries, producing a tangled mesh impossible to change or reason about.
Coverage-Driven Testing (Coverage as a Target)
Chasing a code-coverage percentage as the goal, producing tests that execute code without meaningfully asserting behavior — high numbers, low confidence.