04 · Foundations
Bad Practices & Code Smells
A smell is a surface symptom that something deeper is wrong. Smells don't prove a bug — they prove a maintainability problem. Refactor when the cost of the next change outweighs the cost of the cleanup.
The catalog you should know
- Duplicated Code — the same logic in two places will drift. Pull it into a function or a parent class.
- Long Method — if you need to scroll, the method is doing too much. Extract sub-methods until each one fits on a screen.
- Long Parameter List — >3 parameters is a warning, >5 is a rewrite. Group related params into an object.
- Large Class / God Object — a class with 30 methods runs the whole domain. Split by responsibility (S in SOLID).
- Magic Numbers / Strings —
if status == 7tells you nothing.if status == Status.ARCHIVEDtells you everything. - Primitive Obsession — passing a
string emaileverywhere; introduce anEmailtype that validates itself once. - Feature Envy — a method on class A spends its time poking class B's fields. The method belongs on B.
- Data Clumps — the same three params travel together (start,
end, timezone). They want to be a
TimeRangeobject. - Shotgun Surgery — one change requires edits in ten files. The concept is leaking across modules; consolidate it.
- Switch Statements on Type — replace with polymorphism (Strategy or State).
- Comments explaining what code does — rename the function and delete the comment. Comments should explain why.
- Dead Code — code that no path executes. Git remembers; delete it.
SOLID
The principles you refactor towards:
- S — Single Responsibility: a class has exactly one reason to change.
- O — Open / Closed: open for extension, closed for modification. Add a new payment provider = add a class, not edit ten.
- L — Liskov Substitution: a subclass must honour the contract of
its parent. If
Square extends Rectanglebreaks code that worked with rectangles, the hierarchy is wrong. - I — Interface Segregation: many small interfaces beat one fat interface. Clients shouldn't depend on methods they don't use.
- D — Dependency Inversion: depend on abstractions, not concretions. Inject the database, don't import it.
When (and when not) to refactor
The trigger is friction, not aesthetics. Refactor when:
- You're about to add a feature and the existing code makes it hard.
- A bug fix requires touching the same area for the third time.
- A new teammate consistently asks "why is this here?"
Don't refactor:
- Code you're about to delete.
- Code you have no tests for. Add tests first.
- Right before a release.
Make the change easy, then make the easy change. — Kent Beck.
What to remember at exam time
- Recognise the named smells (the dozen above) and pair each with its refactoring.
- SOLID — each letter, each one-liner.
- Refactoring needs a green test suite. Always.
- A smell is a hypothesis, not a verdict. Sometimes the design that smells is actually correct for the constraint.