Book cover

Complexity

Complexity comes from an accumulation of dependencies and obscurities.

Strategic (long-term approach, thinking about design) vs. Tactical programming (short-term approach, ship features as soon as possible).

Modules should be deep

#module #interface #implementation

Module has two parts: interface and implementation.

A module is any unit of code that has an interface and an implementation.

Each class in OOP is a module.

The best modules are those whose interfaces are much simpler than their implementations.

Interface - what other see. Implementation - how it works inside.

Good (deep) module has simple interface, hides complex implementation.

Interface

Contains two kinds of information: formal and informal.

Formal - specified explicitly in the code. Informal - high-level behavior (described by comments).

In general - if a developer needs to know a particular piece of information in order to use a module, then that information is part of the module’s interface.

Well specified interface eliminates the “unknown unknowns” problem of complexity.

Abstractions

Closely related to modular design.

An abstraction is a simplified view of an entity, which omits unimportant details.

Interface serves as module’s abstraction. It present a simplified view of the module’s functionality.

Details of implementation are unimportant from the standpoint of the module’s abstraction.

It takes practice to design correct abstractions.

We use abstraction daily and it makes our lives easier - cars, microwaves (we only see few buttons, we don’t care about implementation).

Best modules are deep - they have a lot of functionality hidden behind a simple interface.

đźš© Red flag - Shallow module: A shallow module is one whose interface is complicated relative to the functionality it provides.

Classes are good - should though large classes be split into many smaller classes? Is more classes always better? Author disagrees.

Lots of small classes each having its own interface make system a lot more complex.

Information Hiding

One of the most important techniques for achieving deep modules is information hiding.

  1. It simplifies the interface to a module,
  2. makes it easier to evolve the system.

Hiding variables and methods in a class by declaring them private isn’t the same thing as information hiding.

Information leakage

When a design decision is reflected in multiple modules.

One of the most important red flags in software design.

đźš© Red flag - Information leakage: Same knowledge is used in multiple places, such as two different classes that both understand the format of a particular type of file.

Information hiding can often be improved by making a class slightly larger.

Generality vs specialization

Specialization leads to complexity.

Over-specialization may be the single greatest cause of complexity in software. Code that is more general-purpose is simpler, cleaner, and easier to understand.

One of the most important elements of software design is determining who needs to know what, and when.

Different Layer, Different Abstraction

Software systems are composed in layers.

Higher layers use the facilities provided by lower levels.

Example:

File System 
	- uppermost layer implements file abstraction (file consists of a variable-length array of bytes; can be updated by reading/writing variable-length byte ranges)
	- Next layer implements cache in memory of fixed-size disk blocks
	- Lowest layer consists of device drivers

If a system contains adjacent layers with similar abstractions, this is a red flag that suggests a problem with the class decomposition.

đźš© Red flag: Pass-Through Method - A pass-through method is one that does nothing except pass its arguments to another method.

Solution - refactor the classes so that each class has a distinct and coherent set of responsibilities.

Pull Complexity Downwards

You have to implement complex functionality.

Should you let users of the module deal with it, or should you handle complexity internally?

Most modules have more users than developers.

As a module developer, you should strive to make life as easy as possible for the users of your module, even if that means extra work for you.

It is more important for a module to have a simple interface than a simple implementation.

Example: Configuration parameters are an example of moving complexity upwards instead of down.

Sometimes it is critical to make some parts of the system configurable. Sometimes though, configuration parameters provide an easy excuse to avoid dealing with important issues and pass them on to someone else.

Example I found very interesting: Network protocol that must deal with lost packets. When response is not received, we could implement parameter for the retry method (upward complexity). Or, we could implement logic to calculate this retry based on previous successful responses (response times). This is pulling complexity downwards, away from the user.

Before adding configuration parameter:

  • “…Will users (or higher-level modules) be able to determine a better value than we can determine here?”
  • When you do provide configurable parameter, see if you can provide reasonable default

As with everything, pulling complexity downwards can also be easily overdone.

Better Together Or Better Apart?

Given two pieces of functionality, should they be implemented together in the same place, or should their implementations be separated?

đźš© Red flag: Repetition - If the same piece of code (or code that is almost the same) appears over and over again, that’s a red flag that you haven’t found the right abstractions.

Should we split methods into multiple smaller methods?

Each method should do one thing and do it completely.

đźš© Red flag: Conjoined Methods: It should be possible to understand each method independently. If you can’t understand the implementation of one method without also understanding the implementation of another, that’s a red flag. This red flag can occur in other contexts as well: if two pieces of code are physically separated, but each can only be understood by looking at the other, that is a red flag.