Smell (Code Smell)
“A code smell is a surface indication that usually corresponds to a deeper problem in the system.” — Martin Fowler
Context
At the heuristic level, a code smell is a recognizable pattern in source code that suggests (but doesn’t prove) a design problem. Kent Beck and Martin Fowler popularized the term in the context of refactoring. Smells aren’t bugs. The code works. But something about its structure makes it harder to understand, change, or extend than it should be.
Code smells matter in agentic coding because AI agents generate code prolifically, and not all of it is well-structured. A human reviewing agent output needs a vocabulary for identifying structural issues quickly. Recognizing smells lets you say “this function is too long” or “these classes are too tightly coupled” and direct the agent to refactor, without needing to articulate a full design critique.
Problem
How do you identify design problems before they become bugs or maintenance crises?
Design problems rarely announce themselves. A function that’s slightly too long works fine today. A class with one too many responsibilities passes all its tests. The damage is cumulative: each small compromise makes the next change slightly harder until the codebase becomes resistant to modification. By the time someone says “we need to rewrite this,” the cost is enormous. Smells are the early warning system.
Forces
- Working code resists criticism (“if it works, why change it?”).
- Subjectivity makes smell detection feel like opinion rather than analysis.
- Volume of agent-generated code can overwhelm a reviewer’s ability to notice structural issues.
- Refactoring cost discourages addressing smells before they cause pain.
Solution
Learn the common smells and develop the habit of noticing them during code review, whether you’re reviewing human or agent work. The most widely recognized smells include:
Long Method / Long Function. A function that does so many things you can’t hold it in your head. Break it into smaller, named pieces.
Feature Envy. A method that uses more data from another class than from its own. It probably belongs in the other class.
Shotgun Surgery. A single change requires edits in many files. The related logic is scattered and should be consolidated.
Primitive Obsession. Using raw strings, integers, or booleans where a domain type would be clearer. See Make Illegal States Unrepresentable.
Duplicated Code. The same logic in two or more places. When one copy gets fixed, the others don’t.
God Class / God Object. A single class that knows too much and does too much. It violates Separation of Concerns.
Smells are heuristics, not rules. A long function that reads clearly and does one conceptual thing may not need refactoring. A small amount of duplication may be preferable to a bad abstraction. The smell tells you where to look; your judgment decides what to do.
When reviewing agent-generated code, check for these common smells: overly complex class hierarchies (the agent defaulted to enterprise patterns), duplicated validation logic (the agent didn’t extract a shared function), and primitive obsession (strings used where enums would be safer). Agents rarely produce god classes on their own, but they frequently produce long methods and feature envy.
How It Plays Out
A developer reviews an agent’s pull request and notices a 200-line function. The function works (all tests pass) but the developer recognizes the Long Method smell and asks the agent to refactor it into smaller functions with descriptive names. The refactored version is easier to test, easier to read, and reveals a subtle boundary between two responsibilities that the long version had blurred.
A team notices that every time they add a new payment type, they must change code in seven files. They recognize the Shotgun Surgery smell and consolidate the payment logic into a single module with a clear extension point. Future payment types require changes in one place.
“This function is 200 lines long. Refactor it into smaller functions with descriptive names. Each function should do one thing. Run the tests after each extraction to make sure nothing breaks.”
Consequences
A shared vocabulary of smells makes code reviews faster and more productive. Instead of vague discomfort (“something feels off”), you can name the issue and point to a known remedy. Smells caught early are cheap to fix; smells ignored compound over time.
The risk is smell-driven refactoring without purpose. Not every smell needs fixing. Refactoring code that’s stable, rarely changed, and well-tested may not be worth the effort. Use smells to prioritize: focus on smelly code that’s also frequently modified. That’s where the return on refactoring is highest.
Related Patterns
- Refined by: Smell (AI Smell) — AI smells are a newer category of smell specific to model-generated output.
- Uses: KISS — many smells are symptoms of unnecessary complexity.
- Uses: Separation of Concerns — concern violations produce many common smells.
- Enables: Make Illegal States Unrepresentable — primitive obsession is a smell that this pattern resolves.
- Enables: Local Reasoning — fixing smells often restores the ability to reason locally.
- Related: Premature Optimization – optimized code nobody understands is a smell.
- Related: Technical Debt – smells are visible indicators of underlying debt.
Sources
- Kent Beck coined the term “code smell” in the late 1990s while collaborating with Martin Fowler on Ward Cunningham’s wiki. The metaphor — something that doesn’t look wrong but smells wrong — gave developers a shared vocabulary for structural intuition.
- Martin Fowler and Kent Beck catalogued twenty-two code smells and their remedies in Refactoring: Improving the Design of Existing Code (1999, 2nd ed. 2018). Chapter 3, “Bad Smells in Code,” remains the canonical reference for the smell concept.
- Arthur Riel formalized the “God Class” anti-pattern in Object-Oriented Design Heuristics (1996), identifying the tendency of procedural-minded developers to concentrate behavior in a single controller class.
Further Reading
- Sandi Metz, 99 Bottles of OOP (2018) — a practical demonstration of identifying and addressing smells through incremental refactoring.