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.
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).
| Split | Calls crossing a module line (coupling) | Calls inside a module (cohesion) |
|---|---|---|
| by kind of code (left) | 11 of 12 | 1 of 12 |
| by feature (right) | 4 of 12 | 8 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 callerschargeCard()andrefund()change too. Left: Calculations, Integrations and Controllers, 3 modules. Right: only Payment. - Demo: express shipping:
shippingCost()andshipOrder()get a speed, and the ripple reachesplaceOrder(). 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.
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 on | Example |
|---|---|---|
| content | the other module's insides | reading another class's private field by reflection |
| common | shared global data | two modules writing one global config object |
| control | a flag that steers its logic | sendEmail(order, isRefund) |
| stamp | a whole record when it needs a part | passing Order to get the weight |
| data | only the values it needs | shippingCost(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).