Write Code Like a Human Will Maintain It – Risks of AI‑Generated Duplication
Write Code Like a Human Will Maintain It – Risks of AI‑Generated Duplication
TL;DR
Generating code with LLMs without enforcing DRY or other best‑practice patterns creates duplicated conditionals that become the new "style" of the repository; the model then reproduces those patterns, making future maintenance harder. Adding explicit review commands, prompts, and static analysis can keep the codebase clean.
The Problem Illustrated by the Author
The author describes a workflow where an LLM is asked repeatedly to add the same access‑check logic in different places (route handler, background job, API endpoint, webhook). Each generated snippet looks like:
if (user.isActive && user.hasPermission('read') &&
!user.isSuspended && account.status === 'open') {
// do a thing
}
Instead of extracting a shared helper, the author merges each copy as‑is because the code works and the tests pass. The short‑term convenience masks a long‑term risk: every new request for a similar endpoint receives another copy of the same conditional, reinforcing the duplicated pattern. When a refactor is later requested, the LLM simply preserves all five copies because that is what the repository now looks like.
Key takeaway: each shortcut merged into the codebase becomes a signal that the model learns to repeat, turning a one‑off smell into a pervasive style.
Why LLMs Amplify Bad Patterns
- Context‑driven generation – LLMs read the files you have open and the recent commits. They treat existing code as a template for future suggestions.
- Pattern reinforcement – The more duplicated snippets exist, the stronger the bias toward reproducing them.
- No intrinsic notion of maintainability – The model optimizes for "code that works" given the prompt, not for long‑term readability or abstraction.
"LLMs are sponges that soak up everything you do and repeat it back to you. So make sure it's good." – Original post
Community‑Suggested Mitigations
1. Add a Review Command with a Checklist
- Create a markdown file (e.g.,
.claude/commands/review.md). - List concrete checks such as "ensure new code does not duplicate existing logic".
- Invoke the command (
/review) before merging; the agent will generate a plan and flag violations.
"Agents don’t care that they just got a wall of generic feedback, they happily look into all the bullet points. I added ‘ensure the new things aren’t duplicating code that already exists elsewhere’ and it gave me such a surprise – it really truly started planning cleanups!" – cadamsdotcom
2. Prompt for a Final Code‑Quality Check
After a larger change, ask the model to verify:
- Separation of concerns
- Absence of leftover experimental code
- Consistency with documentation
"Now do a final code check. Is everything tidy and do the components adhere to the principle of separations‑of‑concerns?" – planb
3. Use Static Analysis as a Baseline
Integrate linters or code‑smell detectors (e.g., Credo, Rubocop, ESLint) into the CI pipeline. The LLM can then be asked to fix the specific issues reported, providing a deterministic safety net.
"Better yet, add tooling like static code analyzers … Verify against a mechanical baseline, with LLM judgement layered on top as‑needed." – chickensong
4. Run Periodic Model‑Based Smell Detection
Some developers run the codebase through a separate LLM (or a dedicated smell‑detector model) on a schedule to surface repeated patterns.
"I continually run codebases through different models to have them look for bad code smells like repeated code. That’s been pretty effective." – carimura
Counterpoints and Nuanced Views
LLMs can improve maintainability – One commenter notes that AI has nudged their code toward better separation of concerns and testability.
"AI has made my code more human‑maintainable. They’ve been complaining about obsolete comments, separation of concerns, testability…" – andai
Not all duplication is bad – Early in a project, premature abstraction can be harmful. Duplication may be acceptable until a pattern stabilizes.
"When you start a project not everything is DRY, and you don’t start pulling out shared helpers until they’re called for." – chickensong
LLMs may ignore existing abstractions – Some users observe that even when a clean abstraction exists, the model often re‑implements logic from scratch.
"The frontier LLM does neither, it just steams ahead re‑implementing things from scratch…" – davnicwil
Human discipline remains essential – Relying on a team of engineers to consistently apply review prompts is optimistic; many fear the collective discipline may erode.
"The idea that an LLM‑fueled group will collectively have this discipline is… bemusing and saddening." – MattyRad
Practical Checklist for AI‑Assisted Development
- Define a review checklist (duplicate detection, DRY, naming conventions). Store it where the agent can read it.
- Run
/reviewbefore every merge; treat the output as a mandatory code‑review item. - Add a static‑analysis step to CI; fail the build on new smells.
- Schedule periodic LLM smell scans to catch drift.
- Prompt explicitly for abstraction when adding similar logic:
- "Create a shared helper
hasReadAccess(user, account)and use it in all new endpoints."
- "Create a shared helper
- Document the intent in code comments so the model sees the rationale, not just the implementation.
Conclusion
LLMs excel at producing working code quickly, but without disciplined checks they will cement duplicated, hard‑to‑maintain patterns. By treating the model as a teammate that needs explicit guidance—review commands, quality prompts, and static analysis—you can keep the codebase clean and prevent the AI from learning bad habits.