Skip to content

Sources

The practitioners, works, and concepts this skill draws from. Each entry names the concept as the fix files use it, links to the original work, and offers a short definition.

These are people whose work I’ve learned from and whose framings I keep returning to.

George Miller — “The Magical Number Seven, Plus or Minus Two” (1956).

The cognitive strategy of grouping related pieces of information into a single unit (“chunk”) so they occupy one slot in working memory instead of many. Miller’s research established that working memory holds roughly 4±1 chunks at a time. In code, a well-named function, a cohesive type, or a clear module boundary acts as a chunk — it lets the reader treat a complex idea as a single thing.

David Parnas — “On the Criteria To Be Used in Decomposing Systems into Modules” (1972).

Each module should be defined not by the step it performs in a flowchart, but by the design decision it conceals from other modules. Parnas’s paper showed that information-hiding decompositions are more flexible and comprehensible than process-step decompositions. The right question is not “what does this module do?” but “what does it hide?”

Fred Brooks — The Mythical Man-Month (1975).

The quality of a system that feels as though it was designed by one mind. Brooks argues this is the most important consideration in system design: “It is better to have a system omit certain anomalous features and improvements, but to reflect one set of design ideas, than to have one that contains many good but independent and uncoordinated ideas.”

Doug McIlroy, Ken Thompson, and the Bell Labs Unix tradition — articulated by McIlroy (~1978) and elaborated in The Unix Programming Environment (Kernighan & Pike, 1984).

“Make each program do one thing well. Expect the output of every program to become the input to another, as yet unknown, program.” Programs as filters: each takes data in, transforms it, and passes data out; the pipe makes the data flow visible. The principle scales beyond shell pipelines — it underlies functional pipelines, dataflow architectures, and any system where intermediate values are named and inspectable.

Larry Constantine & Edward Yourdon — Structured Design (1979).

Cohesion measures how strongly the elements within a module belong together. Coupling measures how much one module depends on the internals of another. The goal: high cohesion within modules, low coupling between them. The two are joint criteria — improving one without the other usually trades one form of tangle for another.

Gerald Jay Sussman & Hal Abelson — Structure and Interpretation of Computer Programs (1985).

Every powerful design system provides three things: a set of primitive elements, means of combination (ways to build compound things from simpler things), and means of abstraction (ways to name compound things and treat them as primitives). This is the generative grammar of software design — the same triad applies whether you’re designing a language, a library, or a domain model.

Fred Brooks — “No Silver Bullet” (1986).

Brooks distinguishes essential complexity (intrinsic to the problem itself; cannot be eliminated, only managed) from accidental complexity (introduced by tools, processes, and decompositions; can be removed). Most pain in legibility work is accidental. The point of structural diagnostics is to surface accidental complexity so it can be removed, leaving the reader to grapple only with what’s essential.

John Sweller — Cognitive Load Theory (1988).

The amount of information a reader must hold in working memory to understand a piece of code. Sweller’s theory identifies three types: intrinsic (inherent complexity of the problem), extraneous (complexity introduced by poor presentation), and germane (effort spent building useful mental models). Good code minimizes extraneous load and channels effort toward germane load.

The “Gang of Four” — Design Patterns (1994). Reframed in the FP tradition as the Free Monad and developed by the Haskell community (Wadler, Kmett, and others through the 2000s–2010s).

Instead of executing effects directly, build a description of what should happen — an AST, a recipe, a plan — and have a separate, mechanical interpreter execute it. The plan is pure data, composable, inspectable, and testable; the interpreter is so simple it barely needs attention. In everyday code: “return a plan, then run the plan.”

Guy Steele — “Growing a Language” (OOPSLA, 1998).

A good language — or domain model — is one that can be grown from small pieces. Start with a few well-chosen primitives. Compose them into bigger pieces. The bigger pieces should be indistinguishable from primitives — they compose the same way, are invoked the same way. The test: “new words defined by a library should look just like the primitives of the language.” The result is a layered vocabulary that mirrors the domain.

Andrew Hunt & Dave Thomas — The Pragmatic Programmer (1999).

“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” Often misread as “don’t write similar-looking code” — the point is about knowledge, not characters. Two pieces of code that happen to look alike but encode different facts should not be merged; two pieces of code that encode the same fact in different places must be consolidated. F10 (Single Point of Knowledge) is this principle stated structurally.

Rich Hickey — “Simple Made Easy” (Strange Loop, 2011).

Hickey distinguishes simple from easy. Simple is an objective, structural property: one role, one task, one concept, one dimension — not interleaved with anything else. Its opposite is complex, from Latin complecti, “to braid together.” To complect is to weave concerns into a single strand so they can no longer be reasoned about, tested, or changed independently. Easy, by contrast, is subjective: near at hand, familiar, requiring little effort right now — and easy things are often deeply complected underneath. Most legibility failures are not failures of effort or skill; they are places where independent concerns have been braided together. The remedy is structural un-braiding: separate computation from I/O, policy from mechanism, identity from state, what from how. Several diagnostics in this skill are specific complecting patterns — D3 (Tangled Computation and I/O), D6 (Wrong Module Seams), D7 (Premature Splitting or Over-Bundling) — and the un-braided/complected vocabulary used in SKILL.md and the fix files traces directly here.

Robert Harper — “Boolean Blindness” (2011); popularized for working programmers by Alexis King in “Parse, Don’t Validate” (2019).

A boolean discards the evidence that produced it. A function that returns true/false tells the caller the answer but nothing about what was verified, leaving downstream code to trust on faith — or to recheck. The remedy is to return a richer type (or a more specific value) that carries the evidence, so the type system enforces what was learned.

Yaron Minsky and the Jane Street OCaml tradition — articulated in the “Effective ML” talks and posts (~2011) and in Real World OCaml (Minsky, Madhavapeddy, Hickey, 2013).

Design types so that invalid combinations of values cannot be constructed. A discriminated union over status ensures expires_at exists exactly when status is active. A DateRange smart constructor refuses start > end. This is the structural antidote to defensive checks: if the state can’t exist, you don’t have to handle it. Minsky’s aphorism is the operating principle behind F4 (Parse, Don’t Validate) and F11 (Lifted Invariants).

Type-Centric Modularization (OCaml-Style Modules)

Section titled “Type-Centric Modularization (OCaml-Style Modules)”

Yaron Minsky and the Jane Street tradition; the broader OCaml/ML community. See Real World OCaml (2013) and the standard ML library convention.

Every domain concept gets its own module, named after the type it defines. The module file contains the type (conventionally t) along with every function that creates, transforms, validates, or inspects values of that type. The type is the organizing principle: the module boundary follows from the type, not from a process step or a feature grouping. The pattern translates to TypeScript and other structurally typed languages without OCaml’s nominal opacity — the value is in the organizational discipline, not in access control.

Gary Bernhardt — “Boundaries” (RubyConf, 2012).

All decisions, validations, and transformations happen in a pure core — functions that take data in and return data out, with no side effects. The shell is a thin layer that gathers input from the world, passes it to the core, and mechanically applies the core’s output back to the world. The core is trivially testable; the shell is so simple it barely needs tests. The boundary between core and shell is the most important seam in the system.

Casey Muratori — “Semantic Compression” (2014).

Treat programming like dictionary compression: when you see the same pattern twice, extract it and name it. The result is code where the vocabulary mirrors the problem domain and frequently-expressed ideas have their own names. Critically: don’t compress prematurely. Muratori’s rule — “make your code usable before you try to make it reusable” — guards against the abstraction-too-early failure mode.

Scott Wlaschin — Domain Modeling Made Functional (2018) and the F# for Fun and Profit essays.

A practical synthesis of type-driven design for working programmers: discriminated unions for domain choices, smart constructors for invariants, total functions, and “let the types make illegal states unrepresentable.” Wlaschin’s articulation made these techniques accessible outside the ML/Haskell hardcore — much of the working idiom behind F4 (Parse, Don’t Validate) and F11 (Lifted Invariants) traces here.

John Ousterhout — A Philosophy of Software Design (2018).

A deep module provides powerful functionality behind a simple interface. A shallow module has a complex interface relative to the functionality it provides. Shallow modules push complexity onto their callers; deep modules absorb it. The diagnostic: if the interface is as complex as the implementation, the module is too shallow — consider merging it with an adjacent module to create a deeper one.

Alexis King — “Parse, Don’t Validate” (2019).

Validation checks that data is well-formed but throws away the evidence. Parsing converts data into a richer type where invalid states are unrepresentable. After parsing, downstream code can’t encounter surprises — the type system (or the structure of the data) guarantees what was verified. The shell does the parsing; the core only ever sees the parsed form.