Design-First Software

By Nate Abele


What do we mean when we talk about design?

"Design is the rendering of intent." — Jared M. Spool

When we talk about software design, we don't talk about the pixels that appear on the screen, but of the system behind the pixels, that brings them into being: how data flows through it, and how its components fit together.

...

Axioms

Axioms are fundamental, self-evident truths. These truths will serve as the bedrock on which we can build a philosophy of, and approach to, software design.

Software design is about optimizing for two dramatically different audiences, with complementary capabilities but diametrically opposing interests: computers and brains — specifically, human brains. What follows are the capabilities of each of the two audiences: what each is good at (and, conversely, what the other is terrible at).

Brains

  • Ambiguity
  • Inference
  • Intuition

Computers

  • Complexity
  • Lots of data
  • Dynamism — many things changing at once, continuously
  • Precision
  • Repetition

...

[Main book part goes here-ish...]

...

But can't we just write tests?

When dealing with state and side effects

Simple, well-defined units with clean boundaries are easy to reason about independently, but combine even a few of these units, and the number of possible interactions between them quickly grows by orders of magnitude (see also: the halting problem). @TODO: Find a reference that better explains the combinatorially-explosive growth of possible machine states relative to a linear increase in program size, and how it relates to the halting problem.

Unknown unknowns — Unexpected failure modes are, by definition, unexpected. We can only write tests for failure we can anticipate.

Integration complexity: one service invokes another using a callback that invokes a third, which, due to an unanticipated set of conditions, unexpectedly re-invokes the first, resulting in indirect mutual recursion.

Instead of a type system

From the prior two points (unexpected failures & integration complexity), we can correctly infer that it is unreasonable to expect that (a) all units will always be called with expected values, or (b) that all units will correctly handle (and be test-covered for) all values that will be passed to it in production.

The state of the art: necessary, but insufficient

Even in a strongly-typed, pure functional language, automated testing only provides a few programmer-predicted snapshots of behavior within a universe of possible outcomes. Long-term, the only real solution for guaranteed software reliability is something like total functional programming, or other sub-Turing-complexity techniques that facilitate verification analogous to mathematical proofs; see also: Epigram, Coq.

Resources

An aggregated collection of resources which may or may not be referenced in the book itself.

Front-end

Concepts

Abstractions

Theory

Quotes

“Extracting patterns from today's programming practices ennobles them in a way they don't deserve.” — Alan Kay