From "The Pragmatic Programmer"
🎧 Listen to Summary
Free 10-min PreviewDecoupling and Flexible Code Design
Key Insight
Software must be flexible and adaptable to keep pace with rapid change, otherwise code risks becoming outdated or brittle. Coupling, defined as dependencies between code components, is the primary enemy of change because it links elements that must evolve in parallel, making modifications difficult. Its transitive nature means if A is coupled to B and C, and B to M and N, then A is effectively coupled to B, C, M, N. Symptoms of tight coupling include unexpected dependencies, changes propagating through unrelated modules, developer reluctance to modify code due to uncertainty of impact, and meetings requiring broad attendance for changes.
Specific forms of coupling include 'train wrecks,' which are chains of method calls like `customer.orders.find(order_id).getTotals()`. These expose extensive implicit knowledge about an object's internal structure and create rigid dependencies, making future changes (e.g., a 40% discount limit) difficult to enforce consistently. The 'Tell, Don't Ask' principle remedies this by having objects perform actions on themselves rather than external code querying their state and then acting. This delegates responsibility and preserves encapsulation, for instance, by having the totals object apply its own discount. The Law of Demeter is simplified to 'Don't Chain Method Calls,' advising against more than one dot in access paths, with a pragmatic exception for highly stable language-level library chains, such as `people.sort_by { |person| person.age }.first(10).map { |person| person.name }`.
Another insidious source of coupling is 'globalization,' encompassing globally accessible data such as global variables, singletons with exposed instance data, or mutable external resources like databases. Global data acts as an implicit parameter to every method, making it hard to trace impacts of changes and hindering code reuse and unit testing. The principle 'Avoid Global Data' is crucial. If certain data is important enough to be global, it should be 'Wrapped in An API' to control access, insulate calling code from implementation changes, and prevent direct manipulation of underlying resources, thereby reducing system-wide dependencies.
📚 Continue Your Learning Journey — No Payment Required
Access the complete The Pragmatic Programmer summary with audio narration, key takeaways, and actionable insights from Andrew Hunt, David Thomas.