What is a "code smell" in the Fowler/Beck sense, and how would you describe the three classic size-related smells: Duplicated Code, Long Method, and Long Parameter List?
answer
- surface symptom, not a defect
- heuristic, no magic numbers
- Extract Function is the workhorse
- comment explaining a block = extract me
- wrong abstraction costs more than duplication
basics
~20 sA code smell is a surface hint that code may need restructuring — it is not a bug. Duplicated Code = the same logic in several places. Long Method = one function doing too much. Long Parameter List = a call taking too many arguments.
solid answer
~50 sA code smell, popularized by Kent Beck and Martin Fowler in *Refactoring*, is a quickly-spotted structural symptom that usually signals a deeper design problem. Smells are heuristics, not rules: they say "look here", not "this is broken". Smelly code still compiles and passes tests. Duplicated Code — the same or near-same logic in multiple places, so every change must be repeated and one copy always gets missed. Fix with Extract Function, Pull Up Method, or by unifying variants behind one parameterised function. Long Method — a function doing many things at mixed abstraction levels; hard to name, test, and reuse. Fix by Extract Function until the body reads as a summary of intent; any comment explaining a block is an extraction candidate. Long Parameter List — many arguments, especially boolean flags, make call sites unreadable and easy to transpose silently. Fix with Introduce Parameter Object, Preserve Whole Object, Replace Parameter with Query, or Combine Functions into Class.
code
pseudocode · 7 lines// Long Parameter List + flag argument
renderReport(user, from, to, true, false, "pdf")
// After: Parameter Object + Remove Flag Argument
range = DateRange(from, to)
options = ReportOptions(includeTotals = true, format = PDF)
renderDetailedReport(user, range, options)go deeper
Define a smell as a hint that code needs restructuring, name the three smells, and give one refactoring each (Extract Function, Extract Function, Introduce Parameter Object).
Explain the concrete maintenance cost of each smell and name several refactorings per smell, including Preserve Whole Object and Remove Flag Argument.
Add the counter-cases: coincidental duplication, the wrong-abstraction cost, and how Preserve Whole Object trades readability for coupling. Frame smells as heuristics with no thresholds.
Frame smells economically — they are leading indicators of future change cost — and discuss how to encode them in tooling as conversation triggers rather than gates, plus how one smell fixed reveals the next.
## What a code smell is The term comes from Kent Beck and was popularized by Martin Fowler's book *Refactoring: Improving the Design of Existing Code* (1999, 2nd ed. 2018). A **code smell** is a *surface indication* — something you can notice quickly, often without deep domain knowledge — that *usually* corresponds to a deeper problem in the design. Three properties matter: 1. **A smell is not a defect.** Smelly code can be perfectly correct. Smells predict *future* cost: harder change, more places to touch, more chances to get it wrong. 2. **A smell is a heuristic, not a rule.** Fowler deliberately refused to give numeric thresholds ("no method over 20 lines"). The question is always "does this structure make the next change harder?" 3. **Each smell maps to candidate refactorings** — mechanical, behaviour-preserving transformations you apply under the protection of tests. **Refactoring** itself means changing internal structure without changing observable behaviour. Tests are the safety net that lets you do it confidently. ## Duplicated Code The same or nearly-same expression appears in two or more places. - *Why it hurts:* a change to the rule must be applied N times. In practice one copy is forgotten, and the copies silently drift apart — the two branches of the system now disagree. - *Variants:* identical code in the same class (Extract Function); identical code in sibling subclasses (Pull Up Method, or Form Template Method when the surrounding steps differ); similar-but-not-identical code (extract the common part, parameterise the difference). - *Caveat:* **coincidental duplication** — two snippets look alike but express different rules that will evolve independently (e.g. tax rounding and shipping rounding). Merging them creates a coupling that must later be torn apart. "Duplication is far cheaper than the wrong abstraction" (Sandi Metz) captures this: prefer waiting for the third occurrence before unifying. ## Long Method A function that does many things, typically mixing abstraction levels — high-level orchestration next to low-level string manipulation. - *Why it hurts:* you must read all of it to understand any of it; you cannot reuse a part; you cannot unit-test a part; merge conflicts concentrate there. - *Detection cues:* internal blank-line-separated "paragraphs", comments that say *what the next block does*, deep nesting, many local variables, a name containing "and"/"process"/"handle". - *Fixes:* **Extract Function** is the workhorse. Also Replace Temp with Query, Introduce Parameter Object, Decompose Conditional (extract each branch into a named function), Replace Conditional with Polymorphism for type-switch chains. - *Guideline, not law:* extract when the extracted piece can be given a good intention-revealing name. If you cannot name it, the boundary is wrong. ## Long Parameter List A function takes many arguments. - *Why it hurts:* call sites become unreadable; same-typed adjacent parameters can be transposed and still compile; adding a parameter forces edits at every call site; flag parameters (`doSomething(x, true, false)`) hide two or three different behaviours behind one name. - *Fixes:* - **Introduce Parameter Object** — bundle parameters that always travel together into a named type (this also cures the Data Clumps smell). - **Preserve Whole Object** — pass the object you already have instead of pulling three fields out of it. - **Replace Parameter with Query** — if the callee can derive the value itself, do not pass it. - **Combine Functions into Class** — if the same arguments recur across several functions, they are really the state of an object. - **Remove Flag Argument** — split into two explicitly named functions. - *Counter-pressure:* Preserve Whole Object and Combine Functions into Class both *increase coupling* — the callee now depends on the whole object's type. If the callee only needs a scalar, keeping the scalar parameter preserves a narrower, more testable interface. Judgement, not reflex. ## How they interact These three co-occur. A Long Method usually needs many parameters or many locals; extracting it produces functions that each want the same three values, which reveals a Data Clump, which becomes a Parameter Object, which often turns out to be a missing domain concept. Smell-driven refactoring is iterative: fixing one exposes the next.
- When would you deliberately leave duplicated code in place?When the duplication is coincidental — the snippets look alike but encode rules that will evolve for different reasons. Unifying them couples two independent change axes, and the eventual split is more expensive than the duplication. A common discipline is to wait for the third occurrence, and to check whether the copies share a *reason to change*, not just a shape.
- Is there a line-count threshold that makes a method "too long"?No. Fowler explicitly avoids numbers; the real test is whether the body mixes abstraction levels and whether each extracted piece can be given an intention-revealing name. Teams may adopt a lint threshold as a *conversation trigger*, but the threshold is a proxy, not the definition.
A smell is like a smell in a kitchen: it does not prove food is spoiled, but it tells you exactly which cupboard to open first.
saying these in an interview costs you the question
- Calling a smell a bug, or claiming smelly code must be broken
- Quoting hard limits ("never more than 20 lines", "max 3 parameters") as if they were the definition
- De-duplicating identical-looking code without checking whether the two copies change for the same reason
- Fixing Long Method by adding comments or region markers instead of extracting named functions
- Replacing a long parameter list with a generic map/dictionary bag, which hides the contract instead of naming it