Chapter 8: Pull Complexity Downwards
Core Idea
Section titled “Core Idea”When you meet unavoidable complexity related to a module’s functionality, handle it inside the module rather than pushing it to its users. “It is more important for a module to have a simple interface than a simple implementation.”
Frameworks Introduced
Section titled “Frameworks Introduced”- Pull complexity downwards: “Most modules have more users than developers, so it is better for the developers to suffer than the users.”
- When to use: whenever you’re tempted to punt a hard problem upward, by throwing an exception you’re unsure how to handle or by adding configuration parameters for a policy you can’t decide.
- Why it works: pushing complexity up amplifies it. Every caller handles the exception, and every administrator in every installation learns the parameters. Handled once inside the module, one person deals with it.
- The configuration-parameter test: before exporting a parameter, ask “will users (or higher-level modules) be able to determine a better value than we can determine here?”
- If no: compute the value internally.
- If yes (for example, a user knows which requests are more time-critical and can set priorities): export the parameter, but compute a reasonable default automatically, so users supply values only in exceptional conditions.
- When pulling down makes sense (all three should hold):
- The complexity is closely related to the class’s existing functionality.
- Pulling it down will simplify many places elsewhere in the application.
- Pulling it down simplifies the class’s interface.
Key Concepts
Section titled “Key Concepts”- Moving complexity upward: exporting decisions to callers or administrators through exceptions or configuration parameters.
- Configuration parameter: a value users set to control behavior, such as cache size or retry count. Some systems have hundreds.
- Incomplete solution: “Ideally, each module should solve a problem completely; configuration parameters result in an incomplete solution.”
- Dynamic computation: a value computed at runtime adapts when conditions change, while configuration values go out of date.
Mental Models
Section titled “Mental Models”- Think of yourself as serving your module’s users: “take a little bit of extra suffering upon yourself in order to reduce the suffering of your users.”
- Treat configuration parameters as “an easy excuse to avoid dealing with important issues.” Users often can’t find the right values, and often the module could have computed them with a little extra work.
- Pulling complexity down is a way to make modules deeper (ch04): the interface shrinks while the implementation absorbs the work.
Anti-patterns
Section titled “Anti-patterns”- Solving the easy problems and punting the hard ones: throwing exceptions or adding knobs because it is easier for you now.
- Taking it too far: pulling the whole application into one class. Also, pulling unrelated knowledge down, such as a
backspacemethod in the text class (ch06). That doesn’t simplify higher-level code much and leaks UI knowledge into the text class.
Worked Example
Section titled “Worked Example”1. The editor’s text class. Many students gave the text class a line-oriented interface (read, insert, and delete whole lines). That made the text class simple but the UI complex: keystrokes insert characters within a line, and deleting a selection touches parts of several lines, so UI code had to split and join lines. A character-oriented interface (insert a string at any position, delete any range) pulls the splitting and merging into the text class. The text class becomes more complex if it stores lines internally, but the whole system becomes simpler.
2. A retry interval for lost packets. A network protocol resends a request if it gets no response in time.
- Pushing complexity up: add a configuration parameter for the retry interval, which users must guess and which goes stale.
- Pulling complexity down: measure the response time of requests that succeed, and use a multiple of it as the retry interval. Users never see the setting, and it adjusts automatically when conditions change.
- Check against the three conditions: the interval is closely related to the protocol’s job, computing it removes a decision from every user, and it removes a parameter from the interface.
Key Takeaways
Section titled “Key Takeaways”- Prefer a simple interface over a simple implementation.
- Before throwing an exception or adding a parameter, ask whether the module could handle it internally.
- Export a configuration parameter only when users can choose a better value than the module, and give it an automatic default.
- Pull down only complexity that is related to the module, simplifies many callers, and shrinks the interface.
- The goal is minimizing overall system complexity, not making one class do everything.
Connects To
Section titled “Connects To”- Ch 4: pulling complexity down deepens modules.
- Ch 5: the “Taking it too far” case for information hiding, where users need a parameter.
- Ch 6: general-purpose versus UI-specific methods in the text class.
- Ch 7: the character-oriented interface over line storage.
- Ch 10: exceptions are the most common way of pushing complexity upward.