Lesson 0003 · Core Designer · Module 1
The Law of Demeter
A method should only talk to its immediate neighbors. When code walks through strangers, it learns their geography — and every geography change breaks it.
Mission tie-in: chain-walking is one of the most common structural defects in AI-generated code. Master the tell, and you can spot it in any output in seconds.
Knowledge: no talking to strangers
The Law of Demeter limits a method's acquaintances to: itself, its own fields, its arguments, objects it creates, and the direct members of its own collections. Everything else is a stranger.
This is the failure it prevents — a "train wreck":
def is_free_shipping(order: Order) -> bool:
city = order.customer.address.city
return city in FREE_SHIPPING_CITIES
is_free_shipping reaches through order into customer, then address, then city. The caller now knows the internal geography of three objects it doesn't own. Change customer.address to customer.delivery_address and this breaks even though nothing about shipping changed.
The fix is tell, don't ask: move the behavior to the object that owns the data.
def is_free_shipping(order: Order) -> bool:
return order.has_free_shipping_to(FREE_SHIPPING_CITIES)
# inside Order:
def has_free_shipping_to(self, cities: set[str]) -> bool:
return self.customer.delivery_city() in cities
The caller talks to one neighbor (order). The chain still exists — but it lives inside the object that owns the data, where geography changes are contained. The Law of Demeter is not "no chains ever"; it is "nobody walks through strangers."
| You see this | What it costs | The move |
|---|---|---|
a.b.c.d | The caller depends on the shape of three objects it does not own. | Ask the owner one question: a.answer(). |
get_x().get_y().get_z() | The same wreck in getter clothing — chained accessors still walk strangers. | Move the question into the first object. |
| a mock returning a mock returning a mock | The mock chain mirrors the call chain: proof of a train wreck. | Flatten the interface first, then rewrite the test. |
| self.customer.address inside Order | Legal: customer is Order’s own field, a direct neighbour. | Leave it — the law limits callers, not owners. |
Skill: spot the stranger
Which snippet walks through a stranger?
Why does a train wreck hurt maintainability?
The best fix for order.customer.address.city is to:
Practice on your own code
Search a file you own (or an AI-generated one) for chains: patterns like a.b.c.d or repeated get_x().get_y(). Pick one, find the object that owns the final value, and move the question to it.
Reveal: a worked example
# before
if booking.flight.airline.contact_email:
notify(booking.flight.airline.contact_email)
# after — the airline answers; Booking has no opinion on airline internals
if booking.flight.has_contact_email():
notify(booking.flight.contact_email())
Your win
You can spot a train wreck at a glance and know the one fix that works: hand the question to the owner of the data. Add this to your code-review checklist and your AI-review checklist.
Read and watch deeper
- The Pragmatic Programmer, Hunt & Thomas — the "Law of Demeter for Functions" discussion: the original popular treatment.
- "Tell, Don't Ask", Martin Fowler — the companion principle that makes the law workable.
- Code Complete, McConnell — ch. 5: where coupling levels (including "content coupling" — reaching into internals) are ranked.
- Watch: ArjanCodes YouTube — search "Law of Demeter".
- Reference: Glossary — Law of Demeter, Tell, don't ask, train wreck.
Unsure whether a chain is a violation? Ask your agent-teacher to judge a snippet with you — the boundary cases are where the law becomes judgment.