Reading code is bad

5 min read Original article ↗

Reading code is bad.

I'll come back to this.

Meaningful Order

What constitutes meaningful order?

Say you have a bunch of modules you want to start.

Some modules rely on others, so the relied-upon modules must start before the reliant modules.

Common problem.

Colleagues (smart, experienced ones) have argued that writing a list of operations constitutes "explicit order": You just put relied-upon modules before reliant ones:

  1. StartModuleS
  2. StartModuleY
  3. StartModuleE

Both Y and E depend on S. So, start S first, then Y and E. Easy. "Explicit".

Nonsense.

Nothing about the list above indicates the intention of the order of operations. For example it is not clear whether X must precede E (and indeed their order can be swapped).

A seductive but improper analogy

Consider a library. You want to find a book. How can you find it? Because the library places books with an intentional and explicit ordering.

Does every book in the library obey that order? Of course not. Because people shelve books and people make mistakes. I would bet that more than 5% of the books in any well-used library are not in the right order.

Ah, but finding books is not the only order-dependent activity: Somebody must also shelve books (and here's the catch) including NEW BOOKS. How do you know where to put the new books? The answer is not based on their location in the library.

But, compare that to my bookshelves at home. Do my books have an order? According to the logic of my colleagues, yes, yes they do, they are explicitly ordered. How could they be otherwise (unless I interleaved their pages or broke them into piece)?

But I think we can all agree that the ordering my books have is "incidental" -- basically, I could swap the order of any book and nobody but me would know whether it was out-of-order.

The point is that the library has a system, a well-defined and intentional ordering, with a very clear definition of "before" and "after". And that definition has nothing to do with the physical location of books; indeed it is the other way around: The ordering dictates the book locations.

The dependency management system I mentioned also defines "before" and "after". And if the provided dependencies happen not to obey that, it is (a) easy to fix and (b) explicitly out-of-order. And again, the order in which modules happen to start up does not define their ordering; their ordering (their membership in various dependency lists) determines the order in which they start.

In sharp contrast, a monolithic list of "StartModuleX" does not distinguish between the intended order and the actual order. Its ordering (in any meaningful, useful sense) is not intentional; it is incidental.

But this analogy falls apart because when starting modules we want to order a single module against a group of other modules. We do not want nor need to specify per-module order; we only want to say that a given module must follow a group of other modules (its dependencies).

(In analogy to C++ terms, there would be no operator<( const Module & ) -- there would only be operator<( const ModuleList & ).)

A better but still-bad solution

Each class could initialize the subsystems it needs, in its constructor.

But that means each object uses a specific implementation of some class. Sometimes the dependent object does not know or care which implementation it uses; just that it has an object that supplies an implementation.

Yet another solution:

  • Explicitly define dependency lists for modules. Each module has an associated list of modules upon which it depends.
  • Write a manager that takes a list of modules, traverses them to build an ordered list of modules, and start them in that order.

We have such a system in place. The total number of explicitly listed dependencies is in the thousands.

Every once in a while, somebody realizes that (due to a code change they want to make) they need to start some module sooner so they add another dependency. Sometimes that reveals the fact that some other dependency which was accidentally satisfied (that is, it wasn't explicitly listed as a dependency); it merely happened to get started in the right order.

This, they claim, is proof(!) that the dependency management system is no better than just listing all 4000 modules in the required order, in a monolithic list.

Even better: Dependecy Injection

The basic idea is to have a higher-level entity create dependent objects and pass them explicitly to modules that need them.

Wikipedia explains dependency injection adequately.  That's not my goal.

My goal is to point out that reading code does not tell you what you want to know. This is a theme in conversations I have with engineers. Many engineers adopt the philosophy that the best way to understand code is to read it.

False.

Engineers understand code to change it. And the code as-is will not tell you its intention or its plan for the future. It only tells you what some particular engineer made time for up to that point. That's usually a far cry from what the architect intended or (more likely) what an architect would intend if the code had an architect.