Refactoring Bad Smells in Code
Code smells aren't bugs. Your program still runs. But something is off. There's friction when you try to understand it, change it, or extend it. The term ...
14 Oct 2023

Code smells aren't bugs. Your program still runs. But something is off. There's friction when you try to understand it, change it, or extend it. The term comes from Kent Beck:
"If it stinks, change it." — Grandma Beck, discussing child-rearing philosophy
A code smell is a surface indicator that usually corresponds to a deeper problem. You can't always point to exactly what's wrong. You just know something feels harder than it should.
Here are the smells I encounter most often — and the ones worth learning to recognize.
Mysterious Name
You open a file. There's a function called proc. A variable called d. A class called Manager.
Good code reads like prose. If you have to puzzle over what a name means, the name is wrong. Rename it. The cost is five seconds. The benefit lasts the lifetime of the codebase.
Duplicated Code
The same logic in two places means bugs get fixed in one and survive in the other. It means changes require finding every copy. It means someone will miss one.
When you see identical code structures in multiple places, unify them. Extract a function, a class, a module. Give it one home.
Long Functions
The best functions are short. Not because short is inherently virtuous, but because short functions tend to do one thing. And functions that do one thing are easy to name, easy to test, and easy to reuse.
If your function scrolls for multiple pages, it's doing too much. Split it.
Long Parameter List
In the early days, we passed everything as parameters to avoid globals. That was the right instinct. But a function with eight parameters is its own kind of pain.
Group related parameters into objects. Use configuration types. The function signature should be readable at a glance.
Global Data
Globals can be modified from anywhere. You can't trace who changed them or when. They create invisible dependencies between distant parts of your code.
Minimize global state. Encapsulate it. Control access through well-defined interfaces.
Mutable Data
You update a value here. Code over there expected the old value. Now you have a bug that only manifests under specific timing conditions.
Favor immutability. When data must change, make the change explicit and visible. Use new objects instead of modifying existing ones.
Divergent Change
When one module changes for multiple unrelated reasons, it's doing too many things. A UserService that changes when the database schema changes AND when the email format changes has two responsibilities. Split them.
Smelling is the first step
Recognizing smells is a skill. Fixing them is refactoring. Neither guarantees perfect code. But code that smells less is code that's easier to change — and that's the whole point.
Don't try to fix every smell at once. Fix the one you're touching right now. Leave the code a little better than you found it. Over time, the compound effect is dramatic.