Simple Design: The Four Rules
What This Concept Is
Kent Beck's four rules of simple design, in priority order:
- Passes all the tests. The code has to work, and that has to be demonstrable.
- Reveals intent. Names, structure, and shape tell a reader what is going on.
- No duplication. The same idea does not live in two places under two names.
- Fewest elements. No class, method, or module that is not pulling its weight.
They are ordered on purpose. If rules 2 and 3 disagree with rule 1, rule 1 wins. If 4 disagrees with 2, intent wins over minimalism. Minimalism is the tiebreaker, not the goal.
Why It Matters Here
Most day-to-day design judgment is not "do I use Strategy or Observer?" It is: do I make this class, extract this method, inline this helper, split this module? The four rules are the scoring function for those micro-decisions.
They also give a shared vocabulary in review. Instead of "this feels messy," you say "the intent is hard to read -- that processHelper name tells me nothing" or "there is duplication between Order.calculateTotal and Invoice.calculateTotal; pull it into a shared concept."
Concrete Example
Before:
def x(lst):
r = 0
for i in lst:
if i["s"] == 1:
r += i["p"] * i["q"]
return r
Applying rule 1: add a test. The function calculates the total of paid items.
Applying rule 2 (reveal intent):
PAID = 1
def total_of_paid_items(line_items):
total = 0
for item in line_items:
if item["status"] == PAID:
total += item["price"] * item["quantity"]
return total
Applying rule 3 (no duplication): we notice another function total_of_shipped_items with the same shape. Extract:
def total_where(line_items, predicate):
return sum(item["price"] * item["quantity"] for item in line_items if predicate(item))
def total_of_paid_items(line_items):
return total_where(line_items, lambda i: i["status"] == PAID)
Applying rule 4 (fewest elements): is total_of_paid_items still pulling its weight? If callers always want "paid" and no other status, yes, because the name carries intent. If callers already pass arbitrary predicates, inline it. Intent beats minimalism.
Common Confusion / Misconception
"Fewest elements" does not mean "smallest file." The rule is "fewest elements that still reveal intent and avoid duplication." A perfectly tiny file with one cryptic function violates the rule because intent has been lost.
"No duplication" does not mean "every repeated token is a bug." Three lines that look alike but mean different things are not duplication. True duplication is the same idea expressed twice. Extract only when the concept is the same, not when the text rhymes.
Order matters. A common beginner mistake is optimizing minimalism first, collapsing named helpers into inline code and losing intent. The rules are a priority list.
How To Use It
In review, walk the rubric against the PR in order:
- Does it pass tests? If no, ask for tests before discussing design.
- Does each name / structure reveal intent? Call out specific names or structures that hide it.
- Is there duplication? Is the duplicated idea the same concept or just similar text?
- Are there elements (classes, methods, modules, interfaces) that add nothing?
When writing code, use the same order, but in reverse for the refactor loop: write it, make it work, then climb 1 -> 2 -> 3 -> 4.
Check Yourself
- Why is "reveals intent" ranked above "no duplication"?
- What kind of duplication is not worth removing?
- Why is minimalism the tiebreaker and not the target?
Mini Drill or Application
For each snippet, identify which rule is most violated and the smallest improvement:
- a 5-line method named
doStuffthat contains a well-written algorithm for computing tax - two modules with near-identical functions that round currency slightly differently on purpose
- a class with one method and one field whose only purpose is to hold a single
intthe caller already had - a function that works for three known cases but has no tests