Leer en español · Lire en français · Auf Deutsch lesen
Chapter 3 of 12 · Intermediate
Design Principles and Fundamentals
Every architecture is built from the same few ideas about structure: how to divide a system into parts, how much those parts should know about each other, which way their dependencies should point and how to keep the whole thing as simple as the problem allows. This chapter teaches those ideas at every scale, from a Python class to a component to a fleet of services: modularity, coupling and cohesion, information hiding, dependency management, SOLID, connascence, Conway's law, simplicity and the metrics that measure structure. It suits developers who want to reason about design precisely, and architects who need a shared vocabulary for reviews.
Video introduction to Chapter 3
6 min · narrated, with captionsListen only (audio)
Read the transcript
Welcome to Chapter 3: Design Principles and Fundamentals. Every architecture is built from the same few ideas about structure. How to divide a system into parts, how much those parts should know about each other, which way their dependencies should point, and how to keep the whole thing as simple as the problem allows. This chapter teaches those ideas.
Chapter 2 described the qualities an architecture has to deliver. Most of them, and modifiability above all, depend less on any particular technology than on structure. A team can choose the most fashionable framework and still end up with a system where every change touches ten places. These principles are old ideas. Information hiding dates from nineteen seventy-two, and Conway's law from nineteen sixty-eight. They have survived because they describe something that does not change: the cost of change depends on how far a change has to travel. What has changed is the scale. The principle that tells you to keep a class's fields private also tells you to keep a service's database private. So every section shows each idea twice: in a small piece of code, and at the scale of components and services.
This chapter suits developers who want to reason about design precisely. It also suits architects who need a shared vocabulary for design reviews. Being able to read short Python examples helps. It builds on Chapter 2's quality attributes, which these principles help to achieve. It is worth having read Section zero point three on organising code, and Section two point six on designing for change. And it leads into Chapter 4, which shows how to describe a structure so that others can understand it and challenge it.
The first four sections lay the foundations. Section three point one asks what a module is and where its boundaries should go. David Parnas showed that dividing a system by processing steps fails, and dividing it by likely change succeeds. Section three point two gives you the vocabulary for judging boundaries: the classic levels of coupling and cohesion, and the kinds of coupling that only appear between services. Section three point three looks at the interface itself: information hiding, contracts, abstractions that leak, and why every visible behaviour of a popular interface ends up being depended on. Section three point four turns to direction: which way dependencies should point, how to measure stability, how to break cycles, and how to check all of this automatically.
This diagram from Section three point two shows the vocabulary at work, in a real refactoring. On the left is the design before: an orders module and a billing module. Orders calls Billing directly, passing a flag that selects one of two behaviours. So Orders must know about that flag, and it cannot accept an order if Billing is slow or failing. Worse, both modules use the same orders table. Any change to its layout, its status codes or its VAT rules has to be made in both modules at once. On the right is the design after. Orders publishes an Order Placed event to a message broker, carrying exactly the facts Billing needs. Billing subscribes, and each service owns its own data. The coupling has not vanished: Billing still depends on the meaning of that event. But it is now explicit, narrow and versioned, which is what loose coupling means in practice.
The second four sections build on those foundations. Section three point five covers the five SOLID principles, each shown in code and between services, together with the criticism they attract when applied mechanically. Section three point six introduces connascence, a finer measure of coupling, with a clear set of rules for making it weaker and more local. Section three point seven widens the view to the organisation: Conway's law, the evidence for it, and the arithmetic of communication paths. Section three point eight closes with simplicity and measurement: the complexity that belongs to the problem and the complexity we add ourselves, the price of keeping options open, and the limits of all metrics.
Here is Conway's law in one picture. On the left is an organisation. A checkout team and a catalogue team must go through a shared team of database administrators for every schema change. On the right is the system they build: a checkout service and a catalogue service that both read and write a shared database. The structure of the system copies the structure of communication. So when you design a system, you are also designing who needs to talk to whom, and the reverse is just as true.
Read together, the eight sections make one argument. Change is the dominant cost of software, so a good structure is one in which likely changes stay local. Every principle in the chapter is a way of achieving that. That is also why the principles sometimes conflict, and why none is absolute. The professional skill is to ask, for a change that is likely in this system, how far it would have to travel, and which principle shortens that distance at the lowest cost.
If you are new to design, take the sections in order. Each opens with a short everyday explanation. The whole chapter takes about three hours to read. If you are an experienced engineer, you may prefer to start with the worked examples, such as the online shop in Section three point one, or the metrics calculation in Section three point four. They are designed to be argued with. Each section ends with a few self-check questions. Answer them in your head before you look. When you are ready, take the optional chapter test: twenty multiple-choice questions and two written answers, in about forty five minutes. The pass mark is eighty per cent.
That is the map of Chapter 3. Start with Section three point one, on modularity, where we see how to divide a system so that the changes you expect stay inside one part. See you there.
At a glance
You will learn to
- Explain Parnas's criterion for decomposing a system into modules and apply it to compare two candidate decompositions.
- Classify dependencies using the classic levels of coupling and cohesion, and identify temporal, deployment and semantic coupling between services.
- Distinguish information hiding from encapsulation, specify an interface as a contract, and design stable APIs that account for leaky abstractions and Hyrum's law.
- Calculate instability, abstractness and distance from the main sequence, and use them with the acyclic, stable-dependencies and stable-abstractions principles to fix dependency structures.
- Apply the SOLID and component cohesion principles at class, component and service scale, and recognise when they are being applied dogmatically.
- Use connascence to describe the strength, locality and degree of coupling and choose refactorings that weaken it.
- Explain Conway's law, the evidence for it and the inverse Conway manoeuvre, and calculate communication paths for a team.
- Separate essential from accidental complexity, compute cyclomatic complexity, and turn structural rules into fitness functions without falling into Goodhart's law.
Before you start
Chapters 0 to 2, especially Section 0.3 (organising code), Section 1.7 (technical debt) and Section 2.6 (modifiability). The ability to read short Python examples helps.
Time
About 3 hours to read; 45 minutes for the test.
Key terms
modularity · information hiding · coupling · cohesion · afferent and efferent coupling · abstraction · design by contract · Hyrum's law · Acyclic Dependencies Principle · instability and abstractness · SOLID · connascence · Conway's law · accidental complexity
Why principles, and why at every scale
Chapter 2 described the qualities an architecture has to deliver. Most of them, and modifiability above all, depend less on any particular technology than on structure: how a system is divided into parts, what each part knows about the others, and which way the dependencies between them run. A team can choose the most fashionable framework and the best cloud platform and still end up with a system where every change touches ten places, because the parts were cut in the wrong places. The principles in this chapter are the accumulated knowledge of more than fifty years about cutting them well.
They are old ideas, and that is a strength. Information hiding dates from 1972, coupling and cohesion from the structured design work of the late 1960s and 1970s, Conway's law from 1968. They have survived because they describe something that does not change: the cost of change depends on how far a change has to travel. What has changed is the scale at which architects apply them. The same principle that tells you to keep a class's fields private tells you to keep a service's database private; the same reasoning that forbids a dependency cycle between two packages forbids two services that can only be released together. Every section therefore shows each idea twice: in a small piece of code, and at the scale of components and services.
How the chapter is organised
The first four sections lay the foundations. Section 3.1 asks what a module is and where its boundaries should go, using David Parnas's KWIC example to show why decomposing by processing steps fails and decomposing by likely change succeeds, and separates logical modules from the physical units that are built and deployed. Section 3.2 gives the vocabulary for judging boundaries: the classic levels of coupling and cohesion, afferent and efferent coupling, and the kinds of coupling that only appear between services. Section 3.3 looks at the interface itself: information hiding versus encapsulation, contracts, leaky abstractions, deep modules and Hyrum's law. Section 3.4 turns to direction: Robert C. Martin's principles for dependencies between components, the instability and abstractness metrics, how to break cycles, layering, and how to check all of this automatically.
The second four sections build on them. Section 3.5 covers the five SOLID principles and the component cohesion principles, each shown both in code and between services, together with the criticism they attract when applied mechanically. Section 3.6 introduces connascence, a finer measure of coupling with a clear set of rules for making it weaker and more local. Section 3.7 widens the view to the organisation: Conway's law, the evidence for it, the arithmetic of communication paths and the inverse Conway manoeuvre. Section 3.8 closes with simplicity and measurement: essential and accidental complexity, KISS, YAGNI and DRY properly understood, the price of keeping options open, structural metrics such as cyclomatic complexity, and the limits of all metrics.
One idea underneath them all
Read together, the eight sections make one argument. Change is the dominant cost of software, so a good structure is one in which likely changes stay local. Every principle in the chapter is a way of achieving that. Parnas says to put each likely change inside one module. Coupling and cohesion say to keep things that change together close and things that change separately apart. Information hiding says to make sure that what may change is invisible from outside. The dependency principles say to point dependencies at the things that change least. Connascence says to keep any strong agreement between two pieces of code as close together as possible. Conway's law adds that people are part of the structure: a change that needs two teams to agree is not local, however the code is arranged.
That argument also explains why the principles sometimes conflict and why none is absolute. Removing duplication can increase coupling; adding interfaces to invert a dependency can make a module shallower; splitting a component to improve cohesion can create a network hop. The professional skill is not to apply every principle everywhere but to ask, for a specific change that is likely in a specific system, how far it would have to travel, and which principle shortens that distance at the lowest cost.
How to use this chapter
Readers new to design can take the sections in order; each opens with a short everyday explanation and links to Section 0.3 where the basics were first introduced. Experienced engineers may want to start with the worked examples: the online-shop decomposition in Section 3.1, the order and billing refactoring in Section 3.2, the payment interface in Section 3.3 and the metrics calculation in Section 3.4 are designed to be argued with. The principles here are used throughout the rest of the course: the architectural styles of Chapters 5 and 6 are largely different answers to the questions of where to put boundaries and which way dependencies point, and Chapter 11 turns the automated checks introduced here into a governance practice through fitness functions.
The 8 sections
Chapter test
Test your understanding of Chapter 3
Optional: 20 multiple-choice questions and 2 short written answers, drawn afresh from a bank of 72 questions. Pass mark 80%. Passing every chapter test, then the final assessment, earns the Be Wise Certificate of Completion.
Take the Chapter 3 test in the appFurther reading
- On the Criteria To Be Used in Decomposing Systems into Modules, David L. Parnas (1972), Communications of the ACM, volume 15, issue 12. Six short pages that introduced information hiding through the KWIC example; still the best argument for decomposing by likely change.
- Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design, Edward Yourdon and Larry L. Constantine (1979), Yourdon Press / Prentice-Hall. The book that made coupling and cohesion standard vocabulary, with the original discussion of their levels.
- How Do Committees Invent?, Melvin E. Conway (1968), Datamation (April 1968). The original statement of Conway's law, freely available on the author's site and more nuanced than the one-line summary.
- A Philosophy of Software Design (2nd edition), John Ousterhout (2021), Yaknyam Press. A short, opinionated book on reducing complexity, the source of deep and shallow modules.
- Clean Architecture: A Craftsman's Guide to Software Structure and Design, Robert C. Martin (2017), Prentice Hall. Collects the SOLID principles, the component cohesion and coupling principles, and the instability and abstractness metrics in one place.
- Balancing Coupling in Software Design, Vlad Khononov (2024), Addison-Wesley. A modern synthesis of structured design, connascence and domain-driven design that judges coupling by strength, distance and volatility together.
- Software Engineering at Google: Lessons Learned from Programming Over Time, Titus Winters, Tom Manshreck and Hyrum Wright (eds) (2020), O'Reilly Media. Free to read online; discusses Hyrum's law and gives an excellent account of keeping interfaces and dependencies healthy at scale.
- Building Evolutionary Architectures (2nd edition), Neal Ford, Rebecca Parsons, Patrick Kua and Pramod Sadalage (2022), O'Reilly Media. Shows how to protect structural decisions, such as dependency rules, with automated fitness functions.