The idea: a change should stay in one place

Programs are split into modules: packages, classes, files, services. Two numbers say whether the split is good. Coupling is how much one module depends on others. Cohesion is how much the parts inside one module belong together. Aim for low coupling and high cohesion: then a change to one feature edits one module, and the rest of the program does not notice.

Two by two grid. Low cohesion and high coupling: a big ball of mud, every change spreads. High cohesion and high coupling: tidy modules that still call into each other's insides. Low cohesion and low coupling: random drawers that hardly talk, code is hard to find. High cohesion and low coupling: the goal, one change edits one module
The goal is the corner with high cohesion and low coupling: each module owns one job and talks to the others through a narrow door.

The canvas shows one small online shop twice. Both panels hold exactly the same 12 functions and the same 12 calls between them. Only the module lines differ. On the left the code is grouped by kind: Controllers, Calculations, Integrations, Helpers. On the right it is grouped by feature: Order, Payment, Shipping, Email. The coloured tag on each function shows which feature it belongs to. Red arrows cross a module line; green arrows stay inside one.

Counting coupling and cohesion

The page uses the simplest honest count: look at every call. A call between two modules adds to coupling. A call inside one module adds to cohesion. Press Count Calls to walk the 12 calls one by one (Demo: count coupling and cohesion).

SplitCalls crossing a module line (coupling)Calls inside a module (cohesion)
by kind of code (left)11 of 121 of 12
by feature (right)4 of 128 of 12

The code did not get better. The lines moved. On the left, orderTotal(), calcTax() and shippingCost() sit together because they all compute a number, yet none of them calls another. On the right, the four calls that still cross all start at placeOrder() or trackParcel() and go to a public entry function of another module. Real tools count the same idea at class level: CBO (coupling between objects) counts the classes a class uses, and LCOM (lack of cohesion of methods) counts method pairs that share no fields.

The ripple of a change

The numbers matter because of change. Edit only the body of a function and nothing else has to know. Change its signature (add a parameter, rename it) and every caller must change too. Each module that holds an edited function must then be edited, rebuilt, reviewed and retested, often by a different team. The page counts those modules.

  • Demo: tax gets a region: calcTax() gains a region parameter, so its callers chargeCard() and refund() change too. Left: Calculations, Integrations and Controllers, 3 modules. Right: only Payment.
  • Demo: express shipping: shippingCost() and shipOrder() get a speed, and the ripple reaches placeOrder(). Left: 3 modules. Right: Shipping and Order, 2. A real feature that crosses two features still crosses two modules.
  • Demo: new mail API stays hidden: only the body of smtpSend() changes. One module on both sides. This is information hiding: a change behind an unchanged signature does not ripple, whatever the split.

Kinds of cohesion

Stevens, Myers and Constantine named seven kinds of cohesion in 1974, from worst to best. The left panel is logical cohesion: things are together because they look alike. The right is functional cohesion: everything in the module serves one job.

A ladder of seven kinds of cohesion from weak to strong: coincidental (a Utils file), logical (all calculations), temporal (everything run at startup), procedural (steps of one procedure), communicational (works on the same data), sequential (output of one is input of the next), functional (one job, like Payment)
Cohesion from weakest to strongest: the left panel is logical cohesion, the right panel functional.

Kinds of coupling

Not every dependency is equally bad. What matters is how much one module must know about the other.

Kind (worst first)What the caller depends onExample
contentthe other module's insidesreading another class's private field by reflection
commonshared global datatwo modules writing one global config object
controla flag that steers its logicsendEmail(order, isRefund)
stampa whole record when it needs a partpassing Order to get the weight
dataonly the values it needsshippingCost(weight)

Group by what changes together

Cohesion is not a property of the code alone; it depends on which changes happen. Demo: retry outside calls asks for a retry around every call to an outside system. Those three functions all live in Integrations on the left, so the left needs 1 module and the right needs 3. If that were the common kind of change, grouping by kind would be the right choice. In most products, changes come from features (tax rules, shipping options), so the feature cut wins: over all five changes (Demo: all five changes) the left touches 10 modules and the right 8, and the right never needs more modules for a feature change. This is the Common Closure Principle: put together the things that change for the same reason, the package-level form of single responsibility.

See also SOLID Principles (single responsibility and dependency inversion are cohesion and coupling rules), Hexagonal Architecture (ports keep the outside systems behind narrow doors), Monolith vs Microservices (bad service lines make a distributed ball of mud) and Inheritance vs Composition (inheritance is the tightest coupling).

See also Domain Logic Patterns, which counts the places a transaction script, a table module and a domain model must edit for the same new business rule.

What the page leaves out

Data coupling through a shared database or global state (only in the table); weighted metrics such as CBO, LCOM, afferent and efferent coupling and instability; changes that break callers only at run time; the cost of too many tiny modules; and how teams map to modules (Conway's law).

On the mind map

This page on the Software Design mind map · 28 pages in 7 branches · open it to see the whole map and test yourself in recall mode.