Memra

Modularity, information hiding, and functional independence

◈ 6 cards

The module-count cost curve and its minimum M, Parnas’s decomposition criterion, and why functional independence is assessed by exactly two qualitative criteria.

Modularity, and the one claim made for it

Modularity is separation of concerns made concrete, and it is by far the commonest form the principle takes. Instead of one undivided program you have a set of parts — modules — each with a name you can refer to it by and a boundary you can reason inside, wired back together to meet the requirements. The claim made for it is stronger than "it is tidier". The classic argument, put by Glenford Myers in the 1970s and never really overturned since, is that modularity is the one property that keeps a program inside the reach of a single human mind: it is what makes a program intellectually manageable at all. A monolithic program — one large module — defeats understanding not because it is long but because of the number of control paths through it, the span of reference between its parts, and the number of variables in scope at once. No amount of discipline makes that graspable; decomposition does.

You modularise so that development can be planned, so that increments can be defined and delivered, so that change can be accommodated, so that testing and debugging can be conducted efficiently, and so that long-term maintenance can happen without unpleasant side effects. Those five motivations are worth memorising as a list, because a definition answer that gives them reads as understanding rather than recitation.

The cost curve, and why "more modules" is not the answer

If subdivision always helped, you could subdivide indefinitely and drive development effort to nearly zero. Obviously you cannot, and the reason is the shape of two competing curves. The effort to build an individual module falls as the number of modules rises, because each one gets smaller. But the effort to integrate modules rises as their number grows, because there are more interfaces to define, agree, implement and test. Add the two and total effort is a U: it falls, bottoms out at some number of modules M, and rises again.

The honest part of the argument — and the part that earns marks — is that M cannot be predicted with confidence. There is no formula. What the curve gives you is qualitative guidance: modularise, but stay in the vicinity of M, and avoid both extremes. Too few modules and nobody can reason about any of them; too many and the logic disappears into the wiring between them, where it is harder to find than it was in the monolith. Knowing where the vicinity of M is requires the concepts in the rest of this lesson.

Information hiding — the criterion for how to decompose

Modularity tells you to decompose; it does not tell you where to cut. Information hiding is the criterion, and it comes from Parnas: modules should be characterised by the design decisions that each one hides from all the others. Put operationally, a module is specified and designed so that the information inside it — its algorithms and its local data — is inaccessible to modules that have no need of it. Modules then communicate only the information required to achieve the software’s function, and nothing else.

The payoff is specific and it is worth naming precisely, because this is the beat candidates omit: the benefit is realised during change and during maintenance. Because procedural detail and local data are hidden from the rest of the software, errors introduced by a modification are much less likely to propagate beyond the module that was modified. Hiding is not primarily about secrecy or safety at run time; it is about containing the blast radius of tomorrow’s edit.

Functional independence, and its two measures

Functional independence is what you get when separation of concerns, modularity, abstraction and information hiding are all applied together: modules that each do one job, and that reach outside themselves no more than that job actually requires. Each module addresses a specific subset of the requirements and presents a simple interface to the rest of the program. Independent modules are easier to develop, because function can be compartmentalised and handed to different people; easier to test and maintain, because side effects of a change are limited; and more likely to be reusable.

Independence is assessed using two qualitative criteria, and only two: cohesion, the relative functional strength of a module — how tightly everything inside it serves one job — and coupling, the relative interdependence between modules — how entangled it is externally. You want cohesion high and coupling low. Both are qualitative: you argue about them, you do not compute them. Module 8 defines each properly and gives their ladders; here the point is only that a definition of functional independence that does not name both of them is incomplete, and the exam knows it.

Worked example — where to cut BorrowBox’s late-fee logic

The first BorrowBox design put every fee-related decision in one place: a LateFeeService that read the return timestamp, applied the grace period, looked up the daily rate by tool category, capped the total at the tool’s replacement value, called the payment provider, formatted a receipt and emailed it. One module, so integration effort is zero — and it is unreviewable. Nobody can tell you what it does without reading all of it, and every change to the fee rules risks the email.

Apply information hiding and ask what decision each module should hide. The fee schedule — grace period, daily rate, category multipliers, cap — is one decision, and it changes on the community board’s timetable rather than ours; hide it in FeePolicy, which exposes exactly one operation: given a return event, how much is owed? How money actually moves is a second decision, owned by the payment provider and changing on their timetable; hide it in PaymentGateway. How a member is told is a third; hide it in NoticeSender. That is three modules, each highly cohesive, each hiding one volatile decision, with narrow interfaces between them — and when the grace period changes from 24 to 48 hours, exactly one module changes and no caller is retested. Split it further — a module per rate category, say — and you would be past M: the fee logic would now live in the wiring, and reading it would mean chasing six files.

ModulesBuild effortIntegration effortTotal19609635466064014541233306324306292Total effort has a minimum M — but M cannot be predicted.
Illustrative person-days for the BorrowBox fee subsystem. Build effort falls with more modules, integration effort rises, and total effort bottoms out — here at six modules. The numbers are invented; the U-shape and the fact that M cannot be computed in advance are the point.
NORMAL ~/memra/learn/comp-410/modularity-and-information-hiding utf-8 LF