Lesson 0001 · Core Designer · Module 1
Seeing Coupling & Cohesion
Before you can fix structure, you have to see it. This lesson trains your eye to measure the two forces every structure is made of.
Mission tie-in: code that "works" but isn't maintainable is usually failing on exactly these two forces. Every later lesson — patterns, ports, domain protection — is a tool for steering them.
Knowledge: the two forces
Coupling measures how much one module depends on another. Low coupling means a module can change, be tested, or be replaced without its neighbors noticing. Cohesion measures how strongly the elements inside a module belong together — whether it has one purpose.
Look at this function. It works. It also does three different jobs:
def process_order(order_id: str, db: Database) -> None:
order = db.fetch_order(order_id)
total = 0.0
for line in order["lines"]:
total += line["price"] * line["qty"]
order["total"] = round(total, 2)
db.save_order(order)
EmailClient().send_receipt(order_id, order["total"])
logger.info("Order %s processed", order_id)
Ask two questions. Cohesion: how many jobs is this one function doing? It computes a business total, persists it, sends email, and logs — four. Coupling: what does the order flow depend on? It is now fused to the email client and the logging framework; change either and order processing breaks.
Notice what you didn't need: a diagram, a framework, or a tool. You saw the problem by asking how many reasons does this have to change, and who is it joined to. That is the skill of this module: not applying rules, but seeing.
† A function named with "and" ("process and notify", "validate and send") is a confession of low cohesion written into the name.Cohesion and coupling trade against each other across a boundary: to lower coupling between two modules you often raise the cohesion of each — you pull each job into its own module and leave each module with one job.
| You see this | What it costs | The move |
|---|---|---|
def process_and_save(...) | The "and" in a name is two change sources admitted out loud. | Split at the “and”; each half gets one job. |
| import smtplib in a pricing module | Pricing redeploys when the mail vendor changes. | Inject a Notifier port; keep the vendor at the edge. |
| 4 mocks to test 1 assertion | Mock count tracks neighbour count — the unit has too many. | Pass collaborators in; delete the ones the job never uses. |
| one file in 8 of your last 10 PRs | It is the junction where unrelated changes meet. | List its jobs; give each one its own module. |
Skill: point at the problem
Read each snippet below and name the force that is failing. Answer before checking — the act of deciding is the practice.
The function above computes totals, sends email, and logs. The primary problem is:
Which change best raises cohesion without breaking the order flow?
Two modules are tightly coupled when:
Practice on your own code
Take the messiest function in a file you own. Write down (in your head or a note): its jobs, and every neighbor it touches. If the list has more than one job or more than two neighbors, you have found this lesson's win.
Reveal: the refactored version of the example
class Order:
def __init__(self, order_id: str, lines: list[Line]) -> None:
self.order_id = order_id
self.lines = lines
def total(self, pricing: Pricing) -> Money:
return pricing.total(self.lines) # domain knows money, not email
# one job: order flow
def process_order(order_id: str, orders: OrderRepository, pricing: Pricing) -> Money:
order = orders.get(order_id)
return order.total(pricing) # persistence, email, logging live elsewhere
Order flow now depends on a repository and a pricing strategy — not on email or logging. Those became separate modules with one job each.
Your win
You can now look at any function and name which force is failing: too many jobs inside (cohesion) or too many neighbors outside (coupling). That single judgment is the basis of every other lesson in this course.
Read and watch deeper
- A Philosophy of Software Design, Ousterhout — ch. 4 "Modules Should Be Deep": why module shape, not size, determines whether code survives.
- Code Complete, McConnell — ch. 5 "Design in Construction": the deepest treatment of cohesion levels and coupling types.
- Watch: search "coupling" and "cohesion" on ArjanCodes YouTube — the channel's videos on these are the primary source for this lesson.
- Reference: Design Principles — and the glossary on coupling, cohesion, and change amplification.
Anything unclear? Ask your agent-teacher — re-explain any part of this lesson, or bring your own code and have it pointed at together.