Copy-Paste Programming
Duplicating code or rules instead of giving shared knowledge one explicit home.
Also known as: Cut-and-Paste Programming, Duplicated Code, Code Cloning
Understand This First
- DRY — the principle this antipattern violates.
- Source of Truth — the architectural fix for facts that must stay consistent.
- Refactor — the disciplined way to extract duplication without changing behavior.
Symptoms
- The same validation rule, query, permission check, error message, or mapping appears in several files with small local edits.
- A bug fix lands in one copy, but search finds two more copies with the old behavior.
- An agent adds a helper, then inlines a slightly different version of the same helper elsewhere.
- Tests pass for one path while another path with the same business rule drifts silently.
- Reviewers need to ask, “Did you update all the other places too?”
- The copied blocks are similar enough to share intent but different enough that nobody is confident they can be merged.
Why It Happens
Copy-paste programming is tempting because it works immediately. You have a working example. You need the same shape somewhere else. Copying the block is faster than finding the right abstraction, naming it, testing it, and wiring callers through it.
Sometimes copying is also how people learn. A new developer copies a known-good handler to understand the framework. A team copies one migration to write the next. An agent copies a nearby resolver because local code is the strongest signal in its context window. None of that is automatically wrong. The trap begins when the copy becomes production design and nobody records the relationship between the copies.
Agents make the trap easier to scale. A human copies one block and edits it. An agent can copy the pattern across twenty endpoints in a minute. It may change names and types just enough that a text search no longer catches every clone, while still preserving the same hidden rule in every place. The code looks locally reasonable. The system now has twenty places to fix the same mistake.
The deepest cause is often uncertainty about ownership. If there is no obvious module for “the upload limit,” “the discount rule,” or “the user-visible error shape,” copying feels safer than inventing one. The team avoids a design decision by scattering the decision across the codebase.
The Harm
Copy-paste programming turns one future change into a hunt. Every duplicated rule becomes a small fork of the truth. If the tax calculation exists in three services, changing the tax rule means finding all three, proving they still mean the same thing, and updating them without missing an edge case.
The copies also start to diverge. One keeps the old null handling. One catches a broader exception. One includes the February business-rule patch and the other doesn’t. After enough drift, the team can’t tell whether the differences are intentional. Refactoring gets riskier because deleting a copy might delete a real requirement that was never named.
In agentic coding, the harm shows up as false velocity. The agent finishes the change quickly because it didn’t create the shared home the change deserved. The cost moves to review, debugging, and the next feature. Worse, future agents learn from the copied code. They infer that scattering the rule is the local convention and keep doing it.
The Way Out
Give shared knowledge one deliberate home, but don’t abstract on sight. The right move depends on whether the copies truly mean the same thing.
Use three checks:
Name the knowledge. If you can state the duplicated idea in one sentence, it probably wants a home. “The upload limit is 25 MB” belongs in configuration. “Only account owners can rotate API keys” belongs in an authorization policy. “This branch renders the empty state” may be local UI structure and not worth extracting.
Check the change axis. If every copy must change together, extract it. If the copies will change for different reasons, keep them separate and make the difference explicit. Bad abstractions are another way to lose truth: they force unrelated cases to pretend they share one reason to change.
Make the relationship searchable. If duplication is intentional, say so. A generated file header, a shared test fixture, a comment that points to the template, or a code generator can preserve the link between copies. The antipattern is not every repeated line. It is untracked duplication that must stay consistent.
When reviewing an agent’s patch, search for the new rule, literal, and helper name before you approve. If the same knowledge appears in more than one place, ask the agent to either extract the shared home or explain why the copies are allowed to diverge.
How It Plays Out
A team asks an agent to add a new “team admin” permission. The agent updates the settings page, the billing page, and the invitation API by copying the same role check into each file. Everything passes. Two weeks later, support adds a “billing admin” role that should affect only the billing page. The copied checks now look almost the same, but each one means something different. The team extracts a central can_manage_billing policy and a separate can_manage_team_settings policy, then updates the pages to call the named rules instead of carrying local copies.
A migration script converts old status strings into a new enum. A developer copies the mapping into a backfill job, a reporting query, and a test helper. One copy maps "paused" to inactive; another maps it to suspended. The discrepancy doesn’t fail tests because each path has its own expected output. The fix is to move the mapping into one versioned module, add tests around that module, and make every caller use it.
An agent is asked to add validation to five similar form components. It copies the first component’s regex into the other four and tweaks labels by hand. The fifth form has a different allowed character set, but the copied regex hides the difference. A reviewer catches it by asking the agent to list every new validation rule and its source. Four forms should call the same shared validator. The fifth should carry a named exception with its own test.
Related Patterns
Sources
- William J. Brown, Raphael C. Malveau, Thomas J. Mowbray, and Hays W. “Skip” McCormick III’s AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis (Wiley, 1998) established the antipattern form this article follows and is the classic source family for cut-and-paste programming.
- Andy Hunt and Dave Thomas’s The Pragmatic Programmer gives the DRY formulation this antipattern violates: every piece of knowledge should have one authoritative representation.
- Martin Fowler and Kent Beck’s Refactoring treats duplicated code as a primary smell and gives the behavior-preserving extraction discipline for removing it safely.
- Steve McConnell’s “Why You Should Use Routines, Routinely” names duplicate-code avoidance as a practical reason to create routines and quotes David Parnas’s warning that paste-driven coding often signals a design error.
- Cory Kapser and Michael W. Godfrey’s “Cloning Considered Harmful” Considered Harmful is the useful corrective: some cloning is intentional, but each clone family needs an explicit maintenance strategy.