Skip to content

Reading Code Smells

You now know how to refactor safely. The missing piece is knowing what to refactor and when. The answer is code smells: surface signs that something underneath may be poorly structured. The word smell is deliberate — a smell is a hint that invites a closer look, not a rule that demands action. Sometimes the smell is fine in context. The skill is learning to notice the smell and then decide.

flowchart LR
  S["Code smell<br/>(a hint, not a rule)"] --> J{"Worth it<br/>right now?"}
  J -->|yes| R["Pick a matching<br/>refactoring"]
  J -->|no| N["Note it,<br/>move on"]
  R --> T["Small step → test → commit"]
A smell is a hint: notice it, judge whether it is worth fixing now, then take small steps

Here are the smells you will meet most often, each paired with the refactorings this course teaches to address it. The names come from the catalogue in Fowler’s Refactoring (2nd ed., 2018), chapter 3, “Bad Smells in Code”, written with Kent Beck. Treat this as your map: when something feels wrong, name the smell, then look up the move.

SmellWhat you noticeWhere this course addresses it
Long FunctionA function that scrolls off the screen and does many thingsComposing Methods — Extract Function
Duplicated CodeThe same fragment appears in two or more placesExtract Function and Moving Features
Mysterious NameA variable, function or field whose name hides its purposeRename Function/Variable, Rename Field, Extract Variable
Large ClassA class hoarding too many fields and responsibilitiesMoving Features
Long Parameter ListA call with so many arguments you lose trackSimplifying APIs
Feature EnvyA method more interested in another object’s data than its ownMoving Features
Primitive ObsessionBare strings and numbers standing in for real conceptsOrganizing Data
Shotgun SurgeryOne small change forces edits scattered across many filesMoving Features
Repeated SwitchesThe same switch or nested branching repeated until it hides the logicSimplifying Conditionals
Refused Bequest / Temporary FieldA subclass that disowns what it inherits, or state used only sometimesGeneralization & Inheritance

Here is Primitive Obsession: a raw number is passed around to mean “money,” but nothing stops a caller from mixing it up with a quantity or a temperature. The smell is the bare primitive standing in for a domain concept. The cure — wrapping it in a small type — lives in Organizing Data, but you should learn to smell it now.

// Smell: a bare number means "money" — easy to misuse
function total(price: number, shipping: number): number {
return price + shipping;
}
// Hint of the cure: give money its own type
type Money = { cents: number };
function totalMoney(price: Money, shipping: Money): Money {
return { cents: price.cents + shipping.cents };
}

The wrapped version makes it impossible to accidentally add money to a quantity — the compiler or the type now carries the meaning that the bare number left to chance.

What is a code smell?
Which refactoring most directly addresses a Long Function?
Which smell is shown in the example?
Why name the smell before choosing a refactoring?