"Simple Made Easy" - Rich Hickey (2011)

"Simple Made Easy" - Rich Hickey (2011) — Strange Loop Conference

Strange Loop Conference

AI-assisted summary of "Simple Made Easy" - Rich Hickey (2011) — original source. Check the original for full context.

Source published:

Key ideas

  • Rich Hickey defines simplicity as an objective property meaning unbraided or untangled (from the Latin sim-plex), contrasting it with ease, which merely reflects subjective familiarity, physical availability, or proximity to current cognitive habits. (1:24)
  • Every bug discovered in production has already passed both the type checker and the automated test suite, exposing the limits of 'guard rail programming' when developers fail to logically reason about structural architecture. (14:04)
  • Mutable state is fundamentally complex because it complects value with time, meaning encapsulation inside classes or methods fails to prevent state from leaking like poison across an application. (35:39)
  • Software maintainability depends on evaluating the long-term production artifact rather than developer authoring convenience, requiring engineers to choose simple constructs like immutable data and queues over complex object-relational mapping and stateful classes. (9:07)

Detailed notes

## Background This keynote address was given by Rich Hickey, author of Clojure and designer of Datomic, at the Strange Loop 2011 conference and re-published on YouTube by the Strange Loop Conference channel on September 2, 2021. The talk, originally recorded in partnership with InfoQ, is presented in a remastered HD version and focuses on software design principles, dissecting how the industry conflates ease with simplicity and offering tools to fight systemic complexity.

## TL;DR Rich Hickey dismantles the common confusion between 'easy' (familiar, near at hand, and ergonomic to author) and 'simple' (unbraided, untangled, and conceptually independent). He critiques mainstream software culture for prioritizing developer convenience, superficial syntax, and 'guard rail programming' with automated tests over rigorous architectural reasoning. By examining how mutable state, inheritance, and object-relational mapping complect orthogonal concerns, Hickey demonstrates how structural entanglement limits human cognitive capacity. Ultimately, he advocates for adopting plain data structures, pure functions, open polymorphism, and declarative boundaries to build systems that remain reliable, understandable, and sustainable to change over time.

## Atmosphere & style The lecture carries an authoritative, wry, and intellectually grounded tone, characterized by Hickey's dry, pragmatic humor and deadpan delivery. Instead of evangelizing dogmatically, he speaks with the quiet confidence of a veteran engineer who has survived decades in the trenches of complex C++ and enterprise systems, gently puncturing industry hype and conference pieties. Listening to the talk feels like an incisive philosophical and etymological intervention, shifting the audience from the comfortable ease of habitual coding habits into a heightened, rigorous awareness of architectural entanglement.

## Bulletin - [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) Rich Hickey defines simplicity as an objective property meaning unbraided or untangled (from the Latin sim-plex), contrasting it with ease, which merely reflects subjective familiarity, physical availability, or proximity to current cognitive habits. - [14:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=844s) Every bug discovered in production has already passed both the type checker and the automated test suite, exposing the limits of 'guard rail programming' when developers fail to logically reason about structural architecture. - [35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) Mutable state is fundamentally complex because it complects value with time, meaning encapsulation inside classes or methods fails to prevent state from leaking like poison across an application. - [09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) Software maintainability depends on evaluating the long-term production artifact rather than developer authoring convenience, requiring engineers to choose simple constructs like immutable data and queues over complex object-relational mapping and stateful classes.

## Key takeaways - [00:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=4s) Edsger W. Dijkstra's maxim that simplicity is a prerequisite for reliability serves as the foundation for high-quality software engineering. - [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) Simplicity is an objective attribute denoting the absence of interleaving or braiding, rather than a count of operations or object cardinality. - [05:16](https://www.youtube.com/watch?v=SxdOUGdseq4&t=316s) Ease is purely relative, rooted in physical proximity, mental familiarity, and cognitive capacity limits that developers rarely acknowledge due to pride. - [09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) Conflating authoring constructs with long-term production artifacts incentivizes lightweight typing ergonomics over software reliability and maintainability. - [12:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=739s) Human working memory is biologically constrained, making the combinatorial mental burden of intertwined components impossible to comprehend in isolation. - [14:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=844s) Agile refactoring and automated tests act only as guard rails; they cannot guide system architecture or replace informal logical deduction about program behavior. - [17:14](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1034s) Prioritizing ease over simplicity delivers rapid initial velocity but inevitably leads to systemic slowdowns where teams spend sprints redoing past work. - [19:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1175s) Incidental complexity arises from tool choices rather than problem-domain requirements, producing tangled 'knitted castles' that cannot be easily modified. - [24:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1461s) Lisp parentheses highlight the confusion between unfamiliarity in tooling and architectural complexity, which Clojure untangled by adding dedicated data structures. - [31:08](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1868s) The term 'complect' describes the destructive act of braiding together orthogonal concerns such as state and identity, syntax and order, or work and worker. - [35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) Mutable state is inherently complex because it couples value to time, preventing developers from retrieving stable point-in-time values even in single-threaded programs. - [36:28](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2188s) True modularity requires components to communicate strictly through abstract boundaries and values, rather than hiding mutable state behind deceptive class encapsulations. - [39:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2361s) Everyday constructs like ORMs, imperative loops, pattern matching, and stateful objects introduce incidental complexity that can be replaced with declarative data and polymorphism à la carte. - [49:18](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2958s) Abstraction literally means drawing away from physical realization, not merely hiding information behind an opaque wall. - [52:41](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3161s) Simple architectures disentangle the dimensions of what, who, when, where, and why by specifying small interfaces, injecting arguments, using queues, and externalizing business rules. - [56:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3367s) Information is inherently simple and is ruined when wrapped in domain-specific classes; plain maps and sets should be used directly to leverage generic operations. - [58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) Engineers must develop an 'entanglement radar' to spot complected dependencies rather than debating superficial concerns like syntax and formatting. - [59:56](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3596s) Simplicity is a conscious design choice that often increases the number of distinct components while keeping them untangled and freely adaptable.

## Detailed walkthrough ### [00:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=4s) Introduction, the Audience, and an Appeal to Authority

Rich Hickey opens his talk with a dry joke aimed at the conference crowd: “All right, who's ready for some more category theory? You're all in the wrong room.” He acknowledges that the Strange Loop audience is cutting-edge, packed with developers who are already adopting functional programming and new technologies. While many in the room might find themselves nodding along to concepts that feel familiar, Hickey explains that his deeper goal is not merely preaching to the choir, but giving attendees conceptual tools they can carry back to their workplaces to persuade others to do the right thing.

To ground the thesis of the talk, Hickey leads with an appeal to authority, quoting computer scientist Edsger W. Dijkstra: “Simplicity is a prerequisite for reliability.” While admitting he does not agree with everything Dijkstra said—noting specifically that Dijkstra may have been mistaken regarding formal program proofs—Hickey insists that Dijkstra hit the mark on simplicity. If we want to build reliable, high-quality software systems, simplicity is non-negotiable, yet the software industry rarely focuses on it with any real rigor.

[01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) The Etymology and True Meaning of "Simple"

Hickey explains his fondness for word origins, pointing out that over time, words drift and come to mean whatever sloppy, colloquial consensus decides they mean. He wants to resurrect the precise etymological meanings of two core words that software developers routinely conflate: *simple* and *easy*.

```mermaid flowchart TD accTitle: Etymology of Simple and Complex accDescr: Diagram illustrating the root meaning of simple as one fold versus complex as braided together. subgraph S["Root: sim-plex"] A["One fold / braid / twist"] --> B["Unbraided / Untangled"] B --> C["Objective quality: no interleaving"] end subgraph C2["Root: com-plex"] D["Braided / folded together"] --> E["Interleaved dimensions"] end ```

The word *simple* stems from the roots *sim* (one) and *plex* (fold, braid, or twist)—literally meaning having one fold or twist, which in practical terms means no twists at all. Its direct opposite is *complex*, meaning braided or folded together. The central point of Hickey's talk is learning to look at software strictly through this lens: is it braided together, or is it distinct? When applying simplicity to software, developers should seek components that fulfill one task, have one role, address one concept (such as security), or handle a single dimension of a problem.

Crucially, Hickey warns against becoming fixated on the number one: - Simplicity does *not* mean there is only one instance of an object (cardinality). - Simplicity does *not* mean an interface with only one method or operation. - Simplicity is about the absence of interleaving, not counting things. - Because interleaving can be inspected and verified—one can look at a design and see whether two concerns twist together—simplicity is an *objective* property of a system.

[05:16](https://www.youtube.com/watch?v=SxdOUGdseq4&t=316s) Deconstructing "Easy": The Three Faces of Nearness

The word *easy*, by contrast, derives from a French lineage tracing back to the Latin root shared with *adjacent*, meaning "to lie near" or "to be nearby" (its opposite, *hard*, derives from roots meaning strong or tortuously so). Hickey breaks down "nearness" into three distinct dimensions:

1. **Physical Nearness:** Being physically at hand. In modern programming, this manifests as software that is already on our hard drives, pre-packaged in our tools, or obtained instantly via an installer or package manager. 2. **Mental Nearness (Familiarity):** Being near to our current understanding or existing skillset—not in terms of raw intellectual capability, but literally being near what we already know. Hickey argues the industry is destructively obsessed with these first two forms of easiness: developers demand things they can download and run within five seconds, regardless of whether the software is a tangled "giant hairball." Similarly, programmers reject new ideas by complaining that code is unreadable simply because it is unfamiliar: “I can't read German. Does that mean German is unreadable? No, I don't know German.” Rejecting the unfamiliar ensures developers will never learn anything substantially new. 3. **Capability Nearness:** Being near to our cognitive limits. Hickey notes that talking about cognitive capacity makes developers deeply uncomfortable. While nobody feels their ego bruised by admitting they cannot play the violin, software is pure conceptual work; admitting something exceeds our mental capacity directly threatens our pride. Consequently, “due to a combination of hubris and insecurity we never really talk about whether or not something is outside of our capabilities.”

Unlike simplicity, which can be objectively measured by checking for braids, *easy* is purely relative: “Playing the violin and reading German are really hard for me. They're easy for other people, certain other people.” Hickey cautions that casually conflating these terms—claiming a technology is "simple" when one actually means it is "familiar"—degrades technical discourse and prevents teams from reasoning objectively about the qualities that matter.

[09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) Constructs vs. Artifacts: Programmer Convenience and Replaceability

Hickey exposes a critical blind spot in how software is created and evaluated: the conflation of the *construct* (the source code, programming language syntax, and libraries we use to write programs) with the *artifact* (the running software that executes over years in production).

| Focus | Construct | Artifact | |:--- |:--- |:--- | | **Perspective** | The developer's typing experience; ergonomics; syntax. | The running software; production behavior; maintenance. | | **Priorities** | Short-term convenience ("Look, I only had to type 16 characters," missing semicolons). | Reliability, long-term performance, correctness, fixability, ease of change. | | **Driver** | Developer self-involvement; management wanting interchangeable labor. | End users who run the application over years. |

Developers are infatuated with programmer convenience, obsessing over whether an authoring experience feels lightweight. Employers share this obsession for a more cynical reason: if code relies purely on familiar, off-the-shelf tools that are physically near and mentally familiar, developers become instantly replaceable. A business owner can swap one developer for another who can sit in the seat and immediately type familiar constructs, completely ignoring whether anyone can actually understand the resulting system.

Hickey emphasizes that end users do not read source code to admire its syntax: “We don't ship source code and the user doesn't look at our source code and say, okay, oh, that's so pleasant, right? No, they run our software and they run it for a long period of time.” Long-term software health depends entirely on the artifact—whether it works, whether it can be fixed when broken, and whether it can adapt to new requirements. He insists that engineers must evaluate their programming constructs through the lens of the artifacts they produce rather than the transient convenience of typing them in.

[12:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=739s) The Limits of Human Understanding and the Trap of Intertwining

Rich Hickey begins by joking that his slides are designed to lull the audience into a state where everything he says seems true, since he cannot rely on monads to achieve that effect. Turning to basic logic, he asks how developers can possibly expect to build reliable software when they do not understand it. He references Professor Gerald Sussman, who noted that as systems are made more flexible, extensible, and dynamic, engineers inevitably trade off their ability to understand system behavior and ensure correctness. For any system where correctness and comprehension matter, humans hit hard cognitive limits.

```mermaid flowchart TD accTitle: The Combinatorial Cost of Complexity accDescr: Intertwining components forces multiple concepts into a developer's limited working memory simultaneously. subgraph Mind["Limited Working Memory"] A["Concept A"] B["Concept B"] C["Concept C"] end subgraph Codebase["Braided / Intertwined Code"] D["Component 1"] --- E["Component 2"] E --- F["Component 3"] F --- D end D -.->|"Must pull in together"| Mind ```

Hickey points out that human working memory is fundamentally constrained: people can only juggle a few concepts at once. When components are intertwined, developers lose the ability to consider them in isolation. Every time a developer examines one piece of software, they are forced to load every attached piece into their head as well, creating a combinatorial mental burden. As Rich Hickey argues: “So fundamentally this complexity and by complexity, I mean this braiding together of things is going to limit our ability to understand our software.”

[14:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=844s) “Guard Rail Programming” and Informal Reasoning

Addressing contemporary software methodologies, Hickey expresses deep skepticism toward claims made by Agile and Extreme Programming (XP) advocates who assert that refactoring and automated tests allow code to be changed with zero risk. Calling such claims “phooey,” he argues that modifying software fundamentally requires analyzing what it currently does, determining the impact of a proposed change, and deciding what it ought to do. No methodology can bypass the need to reason about a program. He clarifies that reasoning does not mean mathematical formal proofs or category theory, which he rejects as unrealistic goals for everyday work; rather, it refers to ordinary, informal logic.

```mermaid flowchart LR accTitle: Guard Rail Programming Fallacy accDescr: Bumping into tests like guard rails does not steer code toward correct architecture or destination. A["Write Code"] --> B["Crash into Guard Rail (Failing Tests)"] B --> C["Bounce Off (Fix Test)"] C --> D["Bug in Field (Passed Tests & Types)"] ```

Hickey asks the audience what is true of every bug found in production. While someone shouts that “it got written,” Hickey highlights a far more critical fact: every production bug passed the type checker and passed all the automated tests. Relying purely on tests leads to what he calls “guard rail programming,” where developers boast they can make changes recklessly simply because safety nets exist. He compares this to a motorist careening down a highway by constantly bouncing off the guard rails: guard rails might keep you on the road surface, but they do not guide the vehicle to its destination. When systems fail in production and guard rails collapse, developers must rely on informal logic to isolate the root cause, which is impossible if the codebase is an entangled mess.

- Every bug discovered in the field has already passed both the type checker and the test suite. - “Who drives their car around banging against the door? I'm glad I've got these guard rails because I'd never make it to the show on time.” — Rich Hickey - Reasoning about code does not require category theory or formal proofs, but standard logical deduction about program parts.

[17:14](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1034s) The Velocity Fallacy: Sprints vs. Sustainable Speed

Hickey addresses the common objection that adopting easy tooling yields immense development speed. He asks what kind of runner sprints at absolute maximum speed right from the starting line: only someone running a very short race. Programmers, thinking they are smarter than runners, attempt to solve the endurance problem with process: “We just fire the starting pistol every 100 meters and call it a new sprint.”

| Development Focus | Initial Velocity | Long-Term Trajectory | Primary Sprint Activity Over Time | |:--- |:--- |:--- |:--- | | **Ease First** (Ignoring Simplicity) | Maximum speed immediately | Drastic slowdown | Redoing past work, fighting bugs | | **Simplicity First** (Upfront Design) | Slower start (ramp-up) | Consistent, sustainable speed | Moving forward, adding features |

Presenting an experiential, deliberately unnumbered graph, Hickey illustrates that prioritizing ease while ignoring simplicity delivers rapid velocity initially, but accumulated complexity inevitably causes a fatal slowdown over the long haul. Subsequent sprints accomplish less and less, devolving into cycles of endlessly redoing past work. Conversely, prioritizing simplicity requires an initial ramp-up because developers must think and do design work before coding, but it protects velocity over time.

[19:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1175s) The Loom, the Knitted Castle, and Incidental Complexity

Hickey warns that many familiar, widely available constructs in object-oriented programming are easy to pick up and succinctly described, yet yield complex, tangled artifacts. End users do not care how much fun developers had writing the code; they only care about whether the software works reliably, which depends entirely on whether the resulting system is simple.

```mermaid flowchart TD accTitle: Knitted Castle vs. Lego Castle accDescr: Knitted software braids concerns together, making changes impossible without unraveling the whole system. subgraph KnittedCastle["Knitted Castle (Complex & Intertwined)"] K1["Thread A"] --- K2["Thread B"] K2 --- K3["Thread C"] K3 --- K1 end subgraph LegoCastle["Lego Castle (Simple & Modular)"] L1["Block A"] -.-> L2["Block B"] L2 -.-> L3["Block C"] end ```

When chosen tools introduce complexity that is not inherent to the problem domain, it becomes incidental complexity. Hickey jokes that while he omitted the textbook definition on the slide, “incidental is Latin for 'your fault'.” He likens working with complex tools to running a textile loom: the developer happily throws the shuttle back and forth, only to produce a knotted mess—a “knitted castle.” Simplicity provides ease of understanding, ease of change, simpler debugging, and architectural flexibility, such as decoupling location choices from performance choices. Hickey challenges the audience directly on agile refactoring: “And I really do want to ask the question: is having a test suite and refactoring tools going to make changing the knitted castle faster than changing the Lego castle? No way, completely unrelated.”

[22:34](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1354s) Making Things Easy: Mental Limits and the Juggling Analogy

To make software easier without sacrificing simplicity, Hickey breaks down the three constituents of ease: 1. **Location (at hand):** Installing the tool into a toolkit or obtaining organizational approval to use it. 2. **Familiarity (mental grasp):** Reading a book, following a tutorial, having someone explain it, and practicing it. 3. **Mental capability:** The biological constraints of human cognition.

While developers can install tools and study new concepts, human mental hardware cannot be upgraded. Hickey stresses: “We actually can't get much smarter. We're not going to move our brain closer to the complexity. We have to make things near by simplifying them.” Debunking the myth that elite software engineers possess superhuman brains capable of managing unbounded complexity, he returns to the juggling metaphor. An average juggler handles three balls; the best juggler in the world might handle nine or twelve, but no human can juggle 20 or 100 balls. Compared to the massive complexity software systems can generate, all humans sit at virtually the same modest cognitive threshold.

[24:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1461s) Incidental Complexity and the Case of Parentheses

Rich Hickey warns that human cognitive capacity is strictly bounded: developers can only juggle so many mental balls at once, forcing a critical architectural choice about how many of those balls should be incidental complexity versus fundamental problem-solving. When teams continually bring in tools that throw extra balls into the air, cognitive overload is the inevitable outcome.

```mermaid flowchart TD accTitle: The Anatomy of the Parens Complaint accDescr: Diagram illustrating how the complaint about Lisp parentheses splits into user-fixable ease issues and language-level complexity.

subgraph UserIssues["Easy / Familiar (User's Responsibility)"] direction TB E1["Tooling: No structural editing or paren matching"] E2["Familiarity: Parens placed on the left of the verb"] end

subgraph DesignerIssues["Simple / Complected (Designer's Fault)"] direction TB D1["Overloaded Parentheses"] D2["Wraps function calls, grouping, and data structures"] end

UserIssues -->|"Solved by learning tooling"| Fix["User Education"] DesignerIssues -->|"Solved by adding distinct data structures"| Clojure["Simple Construct (e.g. Clojure vectors & maps)"] ```

To illustrate how easy and simple are frequently conflated, Hickey analyzes the classic developer complaint: "parens are hard." On the surface, this complaint has nothing to do with software operation and everything to do with programmer experience: - First, parentheses are not "at hand" for newcomers who lack an editor configured with structural editing modes (like Paredit) or paren-matching capabilities. - Second, parentheses are unfamiliar: developers have seen parentheses before, but placing them on the left-hand side of the method or verb feels jarring.

Yet beneath those user-remediable familiarity problems lies a legitimate critique of *simplicity*. In traditional languages like Common Lisp and Scheme, parentheses are not simple because their role is heavily complected: they are overloaded to simultaneously represent function calls, syntactic grouping, and core data structures. Hickey explains how Clojure fixed this: by introducing additional distinct data structures—such as square brackets for vectors and curly braces for maps—Clojure decoupled those concepts. As Hickey puts it:

> “It doesn't make lists, not Lisp to have more data structures, right.”

By untangling that overloaded syntax at the design level, the construct becomes genuinely simple, pushing the remaining learning curve back onto the programmer to master the editing tools and syntax.

---

[27:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1655s) Benefits vs. Byproducts and the Complexity Toolkit

Hickey invokes an old critique aimed at early Lisp programmers—that they knew the value of everything and the cost of nothing—likely pointing to how older Lisp runtimes dynamically consed up vast amounts of memory and evaluated code in ways that ran like complete "pigs" relative to the hardware of their era. Hickey repurposes that phrase to describe modern software culture: developers eagerly scour sites like Hacker News, jumping on libraries or paradigms because of advertised benefits ("Look, this is shorter!"), while remaining blind to their byproducts.

> “Never nothing. It's just like, we look all for benefits, right? So as programmers now, I think we're, we're looking all for benefits and we're not looking carefully enough at the byproducts.”

To counter this tendency, Hickey presents a side-by-side comparison contrasting common complex programming constructs with their simpler alternatives. He stresses that "simple" does not mean something developers already know; it strictly means unentangled and unbraided:

| Complex Construct | Simpler Alternative | Key Reason for Complexity / Simplification | |:--- |:--- |:--- | | **State & Objects** | Values | Objects entangle identity, state, and behavior; values are immutable and unbraided. | | **Methods** | Functions & Namespaces | Methods couple behavior to types, turning classes into poor namespaces. | | **Vars & Variables** | Managed References | Raw mutable variables allow uncontrolled modification; managed references separate mutation semantics. | | **Inheritance, Switch Statements, Pattern Matching** | Polymorphism à la Carte | Deep hierarchies and giant switches tie cases together; open polymorphism decouples them. | | **Syntax** | Data | Syntax introduces bespoke parsing rules; raw data remains universally manipulable. | | **Imperative Loops & Folds** | Set Functions | Even folds imply sequential ties; set functions declare relationships without order entanglement. | | **Actors** | Queues | Actors complect identity with message queues; isolated queues decouple transport. | | **ORM** | Declarative Data Manipulation | Object-relational mapping forces tabular data into classes; declarative queries separate shape from access. | | **Conditionals** | Rules | Nested `if`/`else` trees tangle disparate branching paths; rule engines evaluate facts independently. | | **Inconsistency (Eventual Consistency)** | Consistency | Inconsistency forces developers to juggle systems that "stand apart" simultaneously. |

---

[31:08](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1868s) The Perils of "Complecting" and Modular Illusions

Hickey reintroduces the archaic word **complect**, meaning to join by weaving, entwine, or braid together. He chooses it deliberately because benign terms like "braiding" lack the inherently negative moral weight required to describe architectural destruction.

> “So what do you know about 'complect'? It's bad. Don't do it.”

To illustrate complecting, Hickey contrasts visual representations of strips of paper: in an uncomplected state, several parallel strands hang neatly straight down; when complected, those identical strands form an impenetrable knot. While compose means to simply "place together" without altering the constituent parts, complecting physically intertwines them.

```mermaid flowchart LR accTitle: Modular Architecture Comparison accDescr: Contrast between pseudo-modular complected components and components isolated by abstract boundaries.

subgraph PseudoModular["Complected Modularity (The Illusion)"] direction TB M1["Module A"] <--->|"Hidden assumptions, tight coupling"| M2["Module B"] end

subgraph TrueModular["Simple Modularity"] direction TB A["Blue Component"] -->|"Speaks to"| I["Abstract Boundary (Interface/Protocol)"] B["Yellow Component"] -->|"Speaks to"| I end ```

Developers often fall into the trap of believing that modularity alone guarantees simplicity. Hickey sarcastically jokes that if making systems modular were enough, his presentation would be finished halfway through. In reality, developers constantly build modular code where distinct modules remain deeply entwined through hidden assumptions:

> “Then you get out in the field and you're like, oh my God, this thing presumes, that, that thing never returns the number 17. What is that?”

True simplicity cannot be achieved merely through physical code organization, partitioning, or vertical stratification into layers. Instead, components must interact strictly through narrow abstractions (metaphorically represented by the dashed connection nubs on a LEGO brick), preventing the components from needing to know anything about each other's inner workings.

---

[35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) The Fundamental Flaw of Stateful Programming

Hickey addresses mutable state not from the vantage point of an ivory-tower functional purist, but from his own extensive career writing production systems:

> “I did years and years, C++ you know, He-Man stateful programming. It's, it's really not fun. It's not good. It's, it's never simple.”

State is inherently complex because it enacts a fundamental complecting of **value** and **time**. When a construct holds mutable state, a developer cannot retrieve a value that is independent of the exact instant of observation, frequently making it impossible to obtain an accurate, stable value at all. Although mutable state feels natural and convenient because it is familiar to programmers trained in imperative languages, its fundamental coupling makes reasoning about software over time exceptionally difficult.

[36:28](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2188s) State as Poison: Why Modularity Cannot Hide State

Programmers often seek refuge in standard software engineering abstractions like modularity, convincing themselves that encapsulation solves the problem of mutable state. But wrapping an assignment statement inside a method does nothing to eliminate its underlying complexity. If calling a method with identical arguments yields different results at different times, the complexity has not been contained; it has simply escaped into the surrounding program. It does not matter that the internal variable is hidden from external view if the containing construct behaves statefully. As Rich Hickey puts it, state spreads throughout a system like a contaminant: “You have this complexity and it's like poison and it's like dropping, you know, some, some dark liquid into this, to a vase. It's just going to end up all over the place.” The only way to stop that spread is to enclose mutable operations inside a boundary that exposes a strictly pure functional interface—one where the exact same inputs guaranteed produce the exact same outputs.

Critically, this critique of state has nothing to do with multithreading or concurrency bugs. Even in a strictly single-threaded application, mutable state destroys a developer's ability to reason about program execution. When a production system fails despite passing all unit tests and satisfying the type checker, diagnosing the root cause requires reconstructing the exact, volatile state of the client at the precise moment things went wrong. In an application saturated with mutable variables, reproducing that transient historical condition is rarely easy or even possible.

```mermaid flowchart TD accTitle: How Mutable State Leaks Past Encapsulation accDescr: Diagram illustrating how mutable state inside a method pollutes callers unless bounded by a pure functional interface.

subgraph BrokenEncapsulation["Stateful Method Wrapper"] Var["Mutable Variable"] --> Method["Method Logic"] end

Method -->|"Different answer for same input"| Caller["Caller / Rest of System (Poisoned)"]

subgraph PureBoundary["True Functional Boundary"] Internal["Internal Computation"] --> PureInterface["Pure Interface: Same Input = Same Output"] end PureInterface -->|"Deterministic Value"| SafeCaller["Safe Caller / Composable System"] ```

[38:02](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2282s) References and De-complecting Value from Time

Modern languages often introduce keywords like `var` or provide reference constructs, but Hickey emphasizes that none of these constructs inherently make state simple—not even the constructs in Clojure. However, languages that do not default to mutability dramatically change developer habits: when engineers must deliberately opt into state, they discover that their programs need orders of magnitude less state than they previously assumed, simply because they never needed all those mutable locations in the first place.

Where constructs do diverge significantly is in how they model time. Hickey singles out Clojure and Haskell references as vastly superior because they explicitly separate time from value. Rather than entangling identity and transient contents into a single opaque handle, these reference constructs decouple the flow of time from the extraction of an immutable value. Once a developer dereferences a construct to extract a point-in-time value, that value can be freely passed across the rest of the application without dragging temporal variability along with it. As Hickey warns, failing to make this separation infects the entire architecture: “I mean, if I have a way to get out of this thing and get a value out, I can continue with my program after I've passed that variable to somebody else or a reference to something that's going to find the variable every time through the varying thing, I'm poisoning the rest of my system.”

[39:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2361s) How Common Constructs Complect: A Catalog of Complexity

To demonstrate how widespread complexity is in standard tools, Hickey enumerates everyday programming constructs and exposes the hidden concepts they interweave ("complect"):

| Construct | Complected Concerns | Mechanism of Entanglement | |:--- |:--- |:--- | | **State** | Everything it touches | Binds program logic to transient execution history. | | **Objects** | State, Identity, and Value | Inextricably bundles who an entity is with what its current value happens to be. | | **Methods** | Function, State, and Namespaces | Binds behavior to object state; in languages like Java, inheriting identical method names from two sources creates namespace collisions. | | **Syntax** | Meaning and Order | Dictates semantics through rigid, unidirectional textual order rather than pure data. | | **Inheritance** | Distinct Types | Explicitly ties parent and child type definitions together by definition. | | **Pattern Matching / Switches** | Handler Identity and Action | Fuses multiple pairs of "who handles this" and "what to do" in a closed, centralized block. | | **Vars / Variables** | Value and Time | Fuses the memory location with time. Machines can only dereference scalar words; they cannot retrieve a snapshot of a composite mutable object in one dereference. | | **Loops & Folds** | Computation and Execution Order | Loops entangle what is computed with step-by-step control flow; folds impose an implicit directional (left-to-right) order. | | **Actors** | Work and Worker | Complects what work must be done with the specific agent or mailbox executing it. | | **Object-Relational Mapping (ORM)** | Objects, Relational Models, and State | An egregious collision of state, identity, mapping code, and consistency boundaries. | | **Conditionals** | Business Rules and Code Structure | Scatters decision logic across the flow and organizational hierarchy of the application. |

Highlighting a point made by Professor Gerald Jay Sussman on data versus syntax, Hickey stresses that programmatic data structures are categorically superior to specialized linguistic syntax: “I don't care how much you really love the syntax of your favorite language. It's inferior to data in every way.” He also mocks the idea of an idealized object-dereferencing architecture: having watched a keynote showing memory dereferencing entire objects, he jokes that he called Apple to order one of those computers, only to be told they do not exist—real physical machines only ever read scalar words at a given memory address, making instantaneous composite snapshots of mutable objects impossible. Addressing ORMs, Hickey remarks on the theoretical absurdity of their abstractions, joking that if an immutable value has a dual called a "co-value," that co-value is simply an inconsistency that no one actually wants.

[42:54](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2574s) Choosing Simpler Tools: Polymorphism à la Carte and Just Plain Data

The central takeaway of the talk is that developers can build systems just as sophisticated as those built in C++, Java, or C#, but using drastically simpler constructs. Hickey insists that decades of experience in those heavy object-oriented languages taught him that all the ambient incidental complexity is unnecessary. Programmers should spend their energy solving problem-domain requirements rather than wrestling with the semantic debris generated by their tools.

To achieve this, Hickey outlines direct, simpler alternatives to complex standard features: - **Values over State:** Use immutable primitives and persistent data collections (via libraries or functional defaults) instead of mutable variables. - **Functions over Methods:** Rely on pure, stateless functions rather than methods bound to stateful instances. - **Data over Synthetic Objects:** Stop wrapping application domain entities in heavy custom classes. Almost all processing needs reduce to maps, sets, and sequential vectors. Hickey laments that while programmers supposedly write data processing programs, many codebases contain almost no genuine data—only endless artificial wrappers. - **Direct Data Protocols over String-Parsing Streams:** Avoid the classic Unix-style command-line pattern of piping arbitrary character strings that require custom parsers on each end. - **Polymorphism à la Carte:** Decouple data structures, function signatures, and concrete implementations into independent operations using constructs like Clojure protocols or Haskell type classes, avoiding rigid class hierarchies. - **Declarative Operations over Hardcoded Branching:** Replace hand-rolled loops and tangled conditional logic with declarative facilities like SQL, LINQ, Datalog, and rule-based inference engines (such as Prolog systems).

Of these alternatives, Hickey views decoupled dispatch as the most liberating: “The biggest thing, I think the most desirable thing, the most esoteric, this is tough to get, but boy, when you have it, your life is completely, totally different thing is polymorphism ala carte, right?”

[47:16](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2836s) Environmental Complexity: Contention and Inherent Limits

Even when a developer eliminates self-inflicted structural complexity, they must still confront environmental complexity. Software does not run in a vacuum; it executes on physical hardware alongside other programs, operating system daemons, and competing threads within the same runtime. These processes inevitably fight over shared physical resources, including memory caches, bus bandwidth, and CPU cycles.

This resource contention is an inherent reality of the physical implementation space rather than an artifact of poor software design. Unlike self-imposed architectural complexity, this environment cannot simply be designed away through clever language features. Poking fun at the jargon of software engineering, Hickey quipped: “Inherent is Latin for 'not your fault' in the implementation space.” While it is not part of the problem domain itself, developers must account for it as an inescapable condition of running code on concrete hardware.

[47:56](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2876s) The Problem with Composing System Policies

Rich Hickey addresses the practical realities developers face when building systems, noting that you cannot go to a customer and argue that a requested feature is unviable simply because of garbage collection issues. While problems like GC pauses and resource contention inevitably occur, software lacks great architectural solutions for them. Segmentation—allocating isolated chunks of memory and CPU to distinct processes—leads to massive inefficiency because pre-allocating static resources strips out dynamism and leaves unused headroom stranded.

The core dilemma Hickey identifies is that individual operational policies fail to compose across a larger application. When independent libraries or components all decide to optimize locally by sizing their internal thread pools to match the number of machine cores, running several of them concurrently overwhelms the hardware. Splitting policies into isolated, localized decisions does not simplify a system; it breeds complexity because those resource-allocation choices belong to an entity with broader contextual information, which modern runtime architectures fail to centralize. Showing a slide with an extensive quote, Hickey distills the message down to a fundamental premise: programming is not about typing; it is about thinking.

- Pre-allocating CPU and memory via strict segmentation introduces tremendous waste and eliminates the dynamic behavior systems need. - “If everybody says I'll just size my thread pool to be the number of cores, well, how many times can you do that in one program?” — Rich Hickey - Isolated technical decisions made without system-wide context complect the runtime and demand centralized coordination mechanisms that are currently lacking in modern software tools.

---

[49:18](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2958s) Designing for Simplicity: What Abstraction Really Means

Hickey shifts focus to how developers can design their own simple constructs rather than merely selecting simple baseline tools. Designing simple software requires understanding abstraction by its literal definition: to draw away from the physical nature of something. Hickey forcefully rejects the common, sloppy conflation of abstraction with mere encapsulation or information hiding. Hiding internal implementation details behind an opaque wall does not constitute abstraction and will not help reduce complexity.

Instead, abstracting for simplicity involves dissecting a problem using the classic framework of questions: who, what, when, where, and why. Hickey emphasizes cultivating an architectural stance of radical indifference to incidental details: "I don't know, I don't want to know." To illustrate, he recalls an anecdote from a C++ course he once taught: he repeated the phrase so frequently that a student made him a custom t-shirt featuring a Booch diagram where every single relationship line read "I don't know, I don't want to know."

```mermaid flowchart TD accTitle: Deconstructing Problems for Simplicity accDescr: Mapping the Who, What, When, Where, and Why dimensions of software design into simple constructs. subgraph Dimensions["Deconstruction Framework"] direction TB W1["What: Operations & Interfaces"] W2["Who: Entities & Data"] W3["When/Where: Time & Flow"] W4["Why: Policies & Business Rules"] end W1 -->|"Declare with"| O1["Small interfaces / protocols"] W2 -->|"Connect via"| O2["Argument injection & generic data"] W3 -->|"Decouple via"| O3["Queues"] W4 -->|"Isolate into"| O4["Declarative / rule systems"] ```

- “And abstract again, here's an actual definition, not made up one, means to draw something away.” — Rich Hickey - Abstraction is not "hiding stuff"; drawing away from physical realization clarifies conceptual design boundaries. - Adopting an "I don't know, I don't want to know" posture keeps abstractions free of low-level assumptions and implementation dependencies.

---

[50:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3039s) Disentangling What from How

Hickey examines the "what" dimension of system design: specifying operations and objectives by naming sets of functions. Whether a language uses interfaces, protocols, or type classes, these constructs serve as specifications rather than implementations. Hickey stresses that these interface sets must be radically smaller than standard industry practice. Languages like Java foster bloated interfaces because they lack union types, making it clumsy to express that a parameter fulfills multiple discrete capabilities without bundling those capabilities into a single monolithic interface.

Crucially, an abstraction specification should deal strictly in values and other abstractions. Hickey warns against complecting the "what" with the "how"—either crudely by binding an interface directly to a concrete class or function, or subtly by allowing semantic assumptions to mandate execution order (citing `fold` as an operation that complects what is computed with how the collection is traversed). When the separation between what and how is kept pure, the execution burden can be pawned off entirely to external mechanisms, such as query engines or logic solvers.

- Abstraction specifications must take and return only pure values and other abstract interfaces. - “Strictly separating what from how is the key to making how somebody else's problem.” — Rich Hickey - Giant interfaces make code significantly harder to decompose; language limitations (like lack of union types) should not excuse bloated API surfaces. - Specifying declarative goals allows runtime execution ("how") to be delegated to specialized engines like database planners or logic solvers.

---

[52:41](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3161s) Breaking Down Who, How, When, Where, and Why

Hickey walks through the remaining dimensions required to maintain simple architectures:

- **Who (Entities and Data):** Systems should be composed out of numerous sub-components assembled through direct argument injection rather than hardwired dependencies. Hickey advises developers not to feel guilty about ending up with five components where they once had none; dividing responsibilities into smaller sub-components and delegating policy grants massive architectural wins, provided components avoid hidden circular assumptions. - **How (Implementations):** Implementation details must be decoupled using open polymorphism (such as protocols, type classes, or runtime-open polymorphic dispatch) rather than closed switch statements or monolithic pattern matching. Implementations should exist as isolated islands, shielded from dictating how down-the-line consumers execute their work. - **When and Where (Time and Space):** Directly coupled method invocations (such as Component A explicitly calling Component B) severely complect time and location: Component A must know where Component B lives, and Component B is forced to execute whenever Component A dictates. Hickey urges developers to decouple these boundaries immediately by using queues. - **Why (Policy and Business Rules):** System rules and policies are routinely scattered throughout application code, making it impossible to audit logic alongside business stakeholders. Hickey dismisses "pretend testing systems that lets you write English strings" as a silly workaround, arguing that real business policies belong in clean, external declarative or rule-based engines.

- Open polymorphism constructs provide superior flexibility over switch statements by keeping implementation details completely separate and extensible. - Directly connected object-to-object invocations complect execution timing and physical location. - “Stick a queue in there, right? Queues are the way to just get rid of this problem.” — Rich Hickey - Domain logic must be separated from general orchestration code so that business rules can be read, understood, and audited declaratively.

---

[56:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3367s) Stop Ruining Information: Data as Data

Turning to information representation, Hickey argues that information is inherently simple, and that traditional object-oriented practices actively ruin it. Object-oriented encapsulation originally evolved to model hardware devices and physical peripherals—such as a screen or a mouse that cannot be touched directly. Applying those encapsulation patterns to pure information was a profound historical mistake that complects domain facts with rigid class hierarchies and representational mechanics.

Wrapping information inside custom classes or Object-Relational Mapping (ORM) frameworks destroys the developer's ability to reuse generic data manipulation tools. By contrast, representing data directly as plain maps and sets allows developers to write generic algorithms once, verify their correctness, and reuse them everywhere across an entire codebase. Hickey implores programmers to stop writing brand-new classes just to hold a new shape of incoming information.

- Objects were designed to mediate interactions with physical I/O devices, not to wrap pure domain data. - “The only thing you can possibly do with information is ruin it.” — Rich Hickey - Wrapping information inside custom classes and ORMs intertwines operational logic with storage representations. - “Please start using maps and sets directly. Don't feel like I have to write a class now because I have a new piece of information. That's just silly.” — Rich Hickey

---

[57:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3441s) Disentangling Complexity and the Choice of Simplicity

Closing the discussion on writing and refining systems, Hickey touches on the necessity of simplifying existing artifacts—whether that means untangling an inherited codebase or simplifying an inherently messy problem space. While acknowledging that a comprehensive breakdown of simplification techniques warrants an entirely separate talk, he describes the foundational task as an exercise in disentangling. Developers must trace the intertwined threads of execution, track where data moves, and label every distinct responsibility before breaking them apart into independent pieces.

Ultimately, Hickey reminds the audience that architectural simplicity is never an accident of tooling or circumstance. Achieving a simple system demands deliberate discipline, continuous critical thought, and an active refusal to accept entangled constructs.

- Simplifying existing software requires carefully tracing execution paths, discovering hidden bindings, and systematically labeling domain concepts to disentangle them. - “The bottom line is simplicity is a choice. It's your fault if you don't have a simple system.” — Rich Hickey

[58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) Breaking the Culture of Complexity and Developing Entanglement Radar

Rich Hickey warns that the software industry is trapped in a self-reinforcing "culture of complexity" because developers continually rely on tools that generate fundamentally complex outputs. To the extent that the community continues using these tools, everyone remains stuck in a rut. For those in the room who already subscribe to this philosophy and use simple constructs, Hickey explains that the true goal of this talk is to arm them with the vocabulary and framework needed to debate colleagues who do not yet believe. Choosing simplicity is an active, demanding discipline; automated tools and guard rails cannot substitute for human design choices because they simply do not yield simplicity.

True simplicity requires honing one's sensibilities and exercising deliberate care. Developers must abandon the intuitive assumption that simplicity is synonymous with ease of use. As Hickey bluntly puts it: “Your sensibilities about simplicity being equal to ease of use are wrong.” While ease describes familiarity and proximity to our existing capabilities, simplicity describes the objective absence of interleaving. Instead of focusing superficial attention on code aesthetics—such as naming preferences, the visual formatting of braces, or whether a semicolon is present—engineers need to cultivate what Hickey calls an "entanglement radar." The real power lies in spotting complected systems and identifying interconnections between components that could otherwise exist independently. Without that focus, testing frameworks and type systems remain merely secondary safety nets that fail to touch the actual heart of software problems.

```mermaid flowchart TD accTitle: Simplicity vs Secondary Guard Rails accDescr: Diagram contrasting core design simplicity against secondary safety nets and tooling guard rails.

subgraph CoreDesign["Core Design Sensibility"] ER["Entanglement Radar"] --> Disentangle["Untangled, Independent Constructs"] Disentangle --> Simplicity["Simple Artifacts"] end

subgraph SecondaryTools["Secondary Mechanisms"] Safety["Testing / Type Systems / Guard Rails"] Ease["Familiar Authoring / Ease of Use"] end

Safety -.->|"Safety nets only, cannot create"| Simplicity Ease -.->|"Often conflated with, but distinct from"| Simplicity ```

- Simplicity requires conscious, persistent vigilance because automated guard rails and testing harnesses do not produce simplicity on their own. - Hickey urges developers to prioritize structural complecting over surface-level syntax disputes: “All the reliability tools you have, right, since they're not about simplicity, they're all secondary.” - Guard rails function strictly as safety nets; they never resolve the underlying structural entanglement of poorly separated software.

---

[59:56](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3596s) Choosing Simple Artifacts and the Power of Untangled Software

Addressing how to make simplicity easy in practice, Hickey emphasizes that developers must judge tools by the runtime and operational characteristics of their resulting artifacts rather than the comfort of authoring them. Programmers frequently get caught up arguing about how convenient code is to type into an editor, but the shape of the source text at authoring time is completely detached from the simplicity of the underlying software artifact that must be maintained and lived with over years. Simplicity must be established at the foundation through careful up-front design, abstracting away interconnected dependencies before the first line of production code is written.

Crucially, designing for simplicity does not mean minimizing the raw number of components. When a tangled problem is broken apart into orthogonal concerns, the total count of distinct artifacts often increases. Hickey stresses that counting elements is a false metric: “Simplicity is not about counting, right? I'd have rather have more things hanging nice, straight down, not twisted together then just a couple of things tied in a knot.” Having more independent pieces hanging straight down dramatically increases one's flexibility and freedom to alter the system over time, which is where the deepest benefits of software engineering lie. Hickey concludes by expressing his hope that the audience will apply these ideas directly or use them to persuade their peers, finishing with a playful parting shot at the audience: “This is what you say when somebody tries to sell you a sophisticated type system. Thank you.”

- The long-term maintainability of a system is dictated by its runtime and conceptual artifacts, not the ergonomics or keystrokes of the authoring experience. - Simplicity often yields a higher quantity of smaller, dedicated parts, but keeping them untangled guarantees genuine modifiability. - Separating concerns into straight, independent threads provides developers with the leverage to adapt and evolve software without unintended cascade failures across the codebase.

## Notable quotes > [50:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3039s) Strictly separating what from how is the key to making how somebody else's problem. — Rich Hickey

> [58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) Your sensibilities about simplicity being equal to ease of use are wrong. — Rich Hickey

> [39:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2361s) I don't care how much you really love the syntax of your favorite language. It's inferior to data in every way. — Rich Hickey

> [57:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3441s) The bottom line is simplicity is a choice. It's your fault if you don't have a simple system. — Rich Hickey

> [56:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3367s) The only thing you can possibly do with information is ruin it. — Rich Hickey

> [22:34](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1354s) We actually can't get much smarter. We're not going to move our brain closer to the complexity. We have to make things near by simplifying them. — Rich Hickey

## Mentioned Rich Hickey, Edsger W. Dijkstra, Gerald Jay Sussman, Clojure, Datomic, Strange Loop, InfoQ, Common Lisp, Scheme, Haskell, Java, C++, Paredit, SQL, LINQ, Datalog, Prolog, Apple

## Video description Rich Hickey, the author of Clojure and designer of Datomic, is a software developer with over 30 years of experience in various domains. Rich has worked on scheduling systems, broadcast automation, audio analysis and fingerprinting, database design, yield management, exit poll systems, and machine listening, in a variety of languages.

This keynote was given at Strange Loop 2011, and is perhaps the best known and most highly regarded of Rich's many excellent talks, ushering in a new way to think about the problems of software design and the constant fight against complexity.

The video was recorded at Strange Loop in partnership with InfoQ, who have hosted it on their site since 2011. This version (released 10 years later) is a new edit made from the original HD video and slides, restoring the slide transitions and animations as it was given in 2011.

[Source](https://www.youtube.com/watch?v=SxdOUGdseq4)

中文简报

“简单成就容易”——里奇·希基(2011年) 里奇·希基剖析了人们常将“容易”(易于上手、触手可及且编写顺手)与“简单”(不交织、不纠缠且概念独立)混为一谈的现象。他批评主流软件文化过于看重开发者便利性、表面语法以及依赖自动化测试的“护栏式编程”,而忽视了严谨的架构推理。通过审视可变状态、继承和对象关系映射如何将本不相干的关注点交织在一起,希基阐明了结构上的纠缠如何受限于人类的认知能力。最终,他主张采用纯数据结构、纯函数、开放多态和声明式边界,以构建随时间推移仍能保持可靠、易懂且可持续变更的系统。 里奇·希基将简单定义为一种客观属性,意为不交织或不纠缠(源自拉丁语 sim-plex),这与仅仅反映主观熟悉度、实际可及性或贴近当下认知习惯的“容易”形成了鲜明对比。 (1:24) 线上生产环境中发现的每一个缺陷,都早已通过了类型检查器和自动化测试套件,这揭示了当开发者未能对结构架构进行逻辑推理时,“护栏式编程”所存在的局限性。 (14:04) 可变状态本质上是复杂的,因为它将值与时间交织在一起,这意味着将其封装在类或方法内部,并不能阻止状态像毒药一样在整个应用程序中蔓延渗透。 (35:39) 软件的可维护性取决于对长期生产产物的评估,而非开发者的编写便利性,这要求工程师选择不可变数据和队列等简单构件,而非复杂的对象关系映射与有状态类。 (9:07)

Reading summary

Rich Hickey contends that reliable software hinges on simplicity—defined strictly as an objective absence of interleaving—which developers routinely compromise by pursuing 'easiness,' a subjective measure of familiarity and authoring convenience. He argues that sustainable engineering requires designing unbraided systems rather than relying on automated testing guard rails to compensate for structural entanglement. Simplicity versus Familiarity and Authoring Convenience (1:24) Hickey traces the word 'simple' to its Latin root simplex, meaning a single fold or twist, and contrasts it with 'complex,' which denotes things braided or folded together. Simplicity is an objective structural property: a component is simple when it addresses a single concept or dimension without interleaving other concerns. By contrast, 'easy' originates from roots meaning 'to lie near.' Hickey segments nearness into physical proximity (tools readily at hand), mental familiarity (concepts near our existing knowledge), and cognitive limits. Because familiarity is purely relative, developers frequently dismiss simple techniques as difficult simply because they are unaccustomed to them. This confusion is amplified by conflating programming constructs—the ergonomics and syntax used while typing—with the resulting software artifact running in production over years. Hickey asserts that the industry's fixation on concise syntax or off-the-shelf tools prioritizes transient developer convenience and management's desire for interchangeable labor over the enduring operational requirements of end users, who require systems that are reliable, debuggable, and adaptable. Cognitive Constraints and the Guard Rail Fallacy (14:04) Human working memory is strictly bounded. Using a juggling analogy, Hickey notes that while an elite juggler might manage nine to twelve balls compared to an average person's three, no human can juggle dozens. When software components are braided together, engineers are forced to load every tangled dependency into memory simultaneously, exceeding biological cognitive capacity. Because human mental hardware cannot be upgraded, software must be made cognitively accessible through simplification rather than through attempts to manage compounding complexity. Hickey rejects claims from Agile and Extreme Programming that test suites and automated refactoring eliminate the risk of modifying complex code. Labeling this practice 'guard rail programming,' he points out that every bug discovered in production has already passed both the type checker and the automated tests. While testing guard rails keep code from careening completely off the road, they do not steer the vehicle or guide system architecture. When software fails, developers must rely on ordinary logical deduction, which becomes impossible if the codebase is fundamentally entangled. Architectural Toxicity of Complecting and Mutable State (36:28) To describe structural entanglement, Hickey emphasizes the archaic term 'complect'—meaning to weave or braid together. Modularity alone does not guarantee simplicity, as modular components often remain deeply complected through hidden assumptions and tight coupling. The most damaging form of complecting is mutable state, which intertwines the concept of an immutable value with the passage of time. When state is mutable, retrieving an accurate, point-in-time value independent of the exact moment of observation becomes exceedingly difficult. Hickey warns that encapsulating state within object-oriented methods fails to neutralize its complexity. Like a drop of ink in a vase of water, state spreads through a codebase whenever identical method inputs yield divergent outputs depending on execution history. Even in single-threaded environments, mutable variables obstruct debugging by erasing the transient historical states that triggered failure. True containment requires enclosing mutation inside pure functional interfaces or employing reference models that explicitly decouple stable snapshot values from time. Deconstructing Architectures with Plain Data and Abstraction (49:18) Hickey defines true abstraction by its literal meaning—drawing away from physical implementation—rather than merely hiding code behind classes. Simple system design requires independently addressing five core dimensions: what operations are performed, who executes them, when and where they run, and why business rules apply. Hickey advocates specifying 'what' using minimal interfaces that deal strictly in values, delegating 'how' to declarative engines, decoupling 'when and where' through message queues, and isolating 'why' into separate rule systems. Crucially, Hickey argues that traditional object-oriented practices ruin domain information. Objects were historically conceived to mediate physical hardware interfaces, not to encapsulate pure data. Wrapping domain facts in custom classes and Object-Relational Mappings prevents engineers from leveraging generic data manipulation operations. Hickey urges developers to represent domain facts directly using persistent, generic maps and sets, combining them with decoupled polymorphic dispatch rather than monolithic class hierarchies or bespoke data wrappers. Hickey concludes that architectural simplicity is an active, demanding choice rather than an accident of tooling. While disentangling a problem into orthogonal, simple constructs often increases the total number of distinct components, keeping those elements unbraided preserves long-term velocity and provides the architectural leverage necessary to modify systems safely over time.

精炼阅读摘要

Rich Hickey 在 2011 年的演讲中主张,软件可靠性的基石在于“简单”(即关注点未被编织或纠缠的客观属性),而非“容易”(即贴近开发者既有习惯与即时便利的主观属性);软件行业沉迷于语法便利与自动化测试等次要护栏,掩盖了因状态纠缠与过度封装而导致系统长期衰退的核心问题。 概念辨析:客观的“简单”与相对的“容易” (1:24) Hickey 追溯词源指出,“简单”(simple)源于拉丁语词根 sim-plex,意为“单层折叠或未编织”,其反义词“复杂”(complex)则代表“编织或折叠在一起”。简单关注的是系统组件是否发生交织(interleaving),要求一个部件仅承担单一角色、概念或维度。简单不是数量上的唯一(如单例对象或单方法接口),而是能够通过设计审查进行客观验证的系统属性。相对地,“容易”(easy)源于与“邻近”(adjacent)相同的词根,意指“在身旁”或“易于接近”,具体表现为物理上的唾手可得(预装工具、快速下载)、思维上的熟悉度(贴合现有知识结构)以及心智能力的极限适应。Hickey 强调容易是一种纯粹相对的主观度量,开发者常误将“熟悉”称为“简单”,因排斥不熟悉的概念而固步自封。 软件行业长期混淆了编写代码的“构造物”(constructs,如编程语言语法与按键便利)与长期运行的“工件”(artifacts,生产环境下的可维护性与正确性)。开发者过度关注编码体验是否轻巧,管理层则倾向追求使用现成流行工具以实现开发人员的可替换性,两者均忽视了终端用户依赖的是系统工件的长期稳健。Hickey 指出,终端用户从不阅读代码语法,评估工具的标准必须立足于其产生的工件质量,而非键盘输入的瞬时愉悦感。 认知瓶颈与“护栏编程”的局限 (14:04) 人类的工作记忆存在硬性生物约束,无法同时处理过多概念。当代码组件互相编织纠缠时,开发者无法对其进行独立分析,被迫在头脑中加载所有关联部件,引发组合爆炸。Hickey 借杂耍抛球比喻指出,顶尖专家与普通人在大脑生理机能上并无质的差距,没有人能够同时抛接几十甚至上百个球;寄希望于人类智力去抗衡失控的偶发复杂性是行不通的。 针对敏捷(Agile)与极限编程(XP)关于重构与自动化测试能消除修改风险的论断,Hickey 持强烈怀疑态度。他指出,所有在生产环境中爆发的故障在事前均已通过了类型检查和自动化测试套件。单纯依赖测试修改系统如同靠撞击道路护栏来驾驶的“护栏编程”,护栏或许能防止车辆冲出道路,却无法指引车辆驶向目的地;一旦生产系统崩溃,开发者必须依赖非形式化的逻辑推理来排查根因,而纠缠的代码会剥夺这种理解能力。以即时便利为导向的开发模式通常在初始阶段速度极快,但随之而来的复杂性积累会导致长期推进速度断崖式下跌,使后续迭代沦为无休止的返工与补漏。 纠缠的代价与可变状态的扩散 (36:28) Hickey 引入词汇“complect”(意为交织或编织在一起),指责其对架构造成的结构性破坏,并将高度纠缠的系统比作无法单独拆改任何一根线的“编织城堡”(knitted castle)。业界常误以为模块化即可自动保证简单,但若模块之间暗含隐式假设或紧密耦合,物理划分只是掩耳盗铃。更为致命的是对可变状态的处理:可变状态在根本上将“值”(value)与“时间”(time)紧密纠缠,导致开发者无法脱离具体的观察时刻获取确定性的值。 面向对象的封装无法消除状态的复杂性。Hickey 将可变状态比作滴入水瓶的墨水,即便用方法将其包装,只要相同输入在不同时间返回不同结果,状态毒性就会向调用者扩散。这一困境在单线程环境下同样会摧毁开发者对执行流程的推理能力。Hickey 剖析了常见编程构造的纠缠本质:对象将身份、状态与值纠缠,方法将行为与特定状态及命名空间绑定,循环与折叠将计算目标与执行顺序捆绑,而对象关系映射(ORM)则将关系模型、类结构与状态一致性边界强行混杂在一起。 简约架构的解构维度与纯数据范式 (49:18) 在架构设计上,Hickey 主张回归抽象的本义——“抽离”(draw away),而非单纯的信息隐藏。开发者应确立“我不知道也不想知道”的设计立场,从操作(What)、实体(Who)、时空(When/Where)与业务规则(Why)四个维度对系统进行解构。“What”应由极小的接口或协议进行规范,将其与具体的执行机制(How)彻底分离;“Who”通过参数注入组装细粒度子组件;“When 和 Where”通过队列解耦组件调用的时间与空间强制绑定;“Why”则应脱离散落的代码逻辑,沉淀于独立的声明式规则引擎中。 在实现工具层面,Hickey 提倡以不可变“值”替代可变状态,以纯函数替代实例方法,以独立的多态机制(如 Clojure 协议或 Haskell 类型类)替代层级继承体系。他批评了将纯信息包装进专用类或 ORM 的习惯做法,指出对象模型最初是为模拟外部硬件设备而设计,将其滥用于数据处理破坏了通用算法的复用能力。系统应当直接使用基础的 Map 和 Set 表达信息。他呼吁开发者培养辨识结构交织的“纠缠雷达”(entanglement radar),认识到简单并非追求组件数量极少,将系统分解为更多互不纠缠的独立部件,反而能赋予系统长期的适应与重组能力。 Hickey 总结认为,简单绝非顺应直觉与便利的自然产物,而是一项需要自觉克制与严密思考的工程抉择。依赖自动化防护栏与语法糖无法代偿严谨的架构设计,软件系统的长期生命力最终取决于开发者能否抵御即时便利的诱惑,主动剔除偶发复杂性,并将对工件本质的思考置于编码击键体验之上。

Detailed notes — zh

## Background 该视频由 Strange Loop Conference 频道于 2021 年 9 月 2 日发布在 YouTube 平台上,是 Clojure 作者 Rich Hickey 在 Strange Loop 2011 大会上极具影响力的开场主题演讲的高清重制版。视频简介提到,拥有 30 余年跨领域系统开发经验的 Rich Hickey 在本场演讲中系统性阐述了软件设计中对抗复杂性的全新思考方式,该演讲被公认为其影响深远的技术代表作之一。 ## TL;DR Rich Hickey 深入剖析了软件工程中常被混淆的两个核心概念:“简单”(Simple,客观上没有交织缠绕)与“容易”(Easy,主观上触手可及与熟悉好用)。他尖锐地批判了敏捷重构神话、过度依赖测试与类型系统的“护栏式编程”,以及对象、状态和语法对事物本质概念的强制交织。演讲指出人类工作记忆容量极其有限,面对软件长跑,唯有克制开发时的局部即时快感,用不可变值、纯函数和队列等非编织的简单构件去替代紧耦合模型,才能保障系统运行工件的长期韧性与推理能力。 ## Atmosphere & style 整场演讲呈现出一种冷峻深刻又极具冷幽默感的大师气场,Rich Hickey 毫无居高临下的布道说教,而是用三十年被复杂性痛击的实战教训与听众真诚对话。他的叙事从严谨的拉丁词源学探究自然切入,行文中穿插着对“He-Man 式状态编程”与“马路撞护栏”的辛辣嘲讽,引发全场频繁的会心大笑。整场演说逻辑如手术刀般精确犀利,听众能深刻感受到一种拨云见日般的思想震撼与反思共鸣。 ## Bulletin - [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) Rich Hickey 严格区分了 Simple(简单:客观概念,指单股未编织缠绕)与 Easy(容易:主观概念,指邻近、现成或熟悉),指出开发者常将对现有知识的“熟悉”误认为系统的“简单”。 - [15:33](https://www.youtube.com/watch?v=SxdOUGdseq4&t=933s) 敏捷开发、自动化测试和类型系统充其量只是“护栏与安全网”,无法替代对系统因果逻辑的日常推理,生产环境中爆发的所有 Bug 均已全绿通过了单元测试与类型检查器。 - [35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) 可变状态将“值”与“时间”不可逆地编织缠绕在一起,像墨水滴入花瓶般持续渗透毒害架构,而单线程程序同样会因无法还原瞬时隐式状态而陷入调试灾难。 - [09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) 软件设计应以“运行产物”(Artifacts)而非“编码打字体验”(Authoring)为首要评价基准,通过纯数据、纯函数、队列与按需多态来取代充血对象模型与 ORM 等高编织度构件。 ## Key takeaways - [00:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=4s) Edsger W. Dijkstra 的名言“简单是可靠性的先决条件”切中要害,构建优秀软件系统的唯一基石是构建简单的系统。 - [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) “Simple”词源为单重折叠与无交织(No Interleaving),它是一个与基数数量多少无关、可客观判断的系统属性。 - [05:16](https://www.youtube.com/watch?v=SxdOUGdseq4&t=316s) “Easy”源自拉丁语的“邻近”,具有物理可及、认知熟悉和心智边界三重维度,属于完全主观且相对的概念。 - [09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) 过度追求打字手感与语法糖等构造体验(Constructs),会导致开发者忽视软件长期运行产物(Artifacts)的架构韧性与维护成本。 - [12:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=739s) 人类大脑同时思考多个交织概念的工作记忆容量极小,系统组件的交织编织会引发认知负担的组合级数暴增。 - [14:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=844s) 敏捷开发宣称重构与测试能让人零成本修改代码纯属妄谈,修改软件的前提是开发者能够对程序行为进行“非形式化推理”。 - [15:33](https://www.youtube.com/watch?v=SxdOUGdseq4&t=933s) 盲目依赖测试套件如同撞着护栏开车的“护栏式编程”,测试只能验证已知用例,无法指引系统朝着正确的架构方向演进。 - [16:48](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1008s) 一味追求上手容易而忽视复杂度的敏捷团队,其开发速度将在中后期因不断重做旧代码而彻底陷入停滞。 - [19:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1175s) 使用织布机构建看似精美的“毛线城堡”会带来严重的附带复杂度,一旦面临变更其弹性远逊于正交解耦的“乐高城堡”。 - [22:34](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1354s) 面对复杂系统,人类个体的智力生理差距微不足道,唯一的出路是通过简化架构把问题拉回到人脑能够处理的近处。 - [31:43](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1903s) 古英语词汇“Complect”精准定义了将独立条带扭结编织在一起的原罪,真正的组合(Compose)应是并置独立的模块。 - [33:08](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1988s) 仅仅将代码进行文件与层级切分的“模块化”往往只是将一团乱麻切成数段,无法带来本质上的系统解耦。 - [35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) 有状态编程将“值”与“时间”深度捆绑,即便在单线程环境下也会因隐式状态难以还原而成为线上调试的梦魇。 - [39:26](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2366s) 对象、方法、语法、继承、Actor 与 ORM 等面向对象主流构件,在底层都将不同的概念维度强行交织在一起。 - [42:54](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2574s) 完全可以使用不可变值、纯函数、通用数据结构(Maps/Sets)和按需多态等朴素工具,构建出同等规模的大型系统。 - [47:56](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2876s) 真正的抽象是将物理实体属性抽离出纯粹的规格说明,单纯隐藏实现细节的信息封装绝不等于抽象。 - [54:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3275s) 在架构中广泛使用队列能够彻底解开组件调用在时间(When)与空间网络位置(Where)上的强耦合。 - [58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) 开发者必须建立起洞察“Complecting”的纠缠雷达,宁愿拥有更多互不缠绕、垂直垂挂的独立小部件,也不要纠缠在一起的死结。 ## Detailed walkthrough ### [00:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=4s) 开场与引言:简单作为可靠性的先决条件 Rich Hickey 以一个轻松的玩笑开场,问现场听众是否准备好听更多关于范畴论(category theory)的内容,接着打趣道:“你们都走错房间了。”他指出,这篇演讲的内容乍看之下可能显得“看似极其显而易见”(deceptively obvious)。Strange Loop 的与会者普遍走在技术前沿,许多人正在拥抱新技术、实践函数式编程,听演讲时可能会频频点头认同。然而,Rich Hickey 希望大家在听完这场演讲后,不仅是感到熟悉,更能带走一套切实的思考与表达工具,以便日后能去说服其他人做出正确的架构选择。 随后,Rich Hickey 引用了计算机科学家 Edsger W. Dijkstra 的经典名言作为诉诸权威的切入点:“简单是可靠性的先决条件。”尽管 Rich 坦言自己并非完全赞同 Dijkstra 的所有观点(尤其是在形式化证明方面),但他坚信这句话切中了要害。软件开发者如果想要构建优良的系统,就必须构建简单的系统,而行业目前对此的重视程度远远不足。 - 演讲定位:针对走在技术前沿、探索函数式编程的开发者,提供用于向团队普及架构设计的论证工具。 - 权威引言:“Simplicity is a prerequisite for reliability.”(简单是可靠性的先决条件 —— Edsger W. Dijkstra)。 - Rich Hickey 的核心判断:正如他直接强调的,“We need to build simple systems if we want to build good systems.”(如果我们想构建优秀的系统,就必须构建简单的系统)。 --- ### [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) 词源探析:何为“Simple”(简单) Rich Hickey 表达了他对词源学的浓厚兴趣。词汇在日常演变中,其含义往往会被约定俗成地磨损或泛化,而探寻本义往往能带来极具穿透力的精确性。他分析了演讲中最重要的第一个词——“simple”(简单)。该词的词根是“sim”与“plex”,字面本义是指“单股的编织、单重的折叠”(one fold or one braid or twist)。如果一件东西只有一重编织,换言之,它其实没有任何与其他线条绞合的扭结。 与之相对的词是“complex”(复杂),其字面含义是“交织在一起、折叠在一起”(braided together or folded together)。Rich Hickey 指出,衡量软件是否被绞合交织在一起,是本次演讲最核心的立足点。在软件设计中追求简单,意味着系统中的模块、组件或抽象应当扮演单一角色、完成单一任务或目标、专注单一概念(例如安全),或仅解决问题领域中的某一个特定维度。 Rich Hickey 紧接着厘清了一个极其关键的混淆:简单关注的是“有无交错”,而不是数量上的单一。简单并不等于系统只能有“一个”东西,也不等于接口只能包含“一个”操作。开发者必须严格区分基数(cardinality,即计数的多少)与交织(interleaving)。没有交错才是简单的本质。更重要的是,一件东西是否存在绞合交错,是一个客观事实;人们可以通过检查连接关系客观判断它是否交织。因此,“简单”是一个客观概念。 ```mermaid flowchart TD accTitle: Simple vs Complex Etymology accDescr: 简单与复杂的词源结构对比:Simple 为单股无缠绕,Complex 为多股交织缠绕。 subgraph Simple["Simple(简单:Sim-Plex)"] S1["单股编织 / 单重折叠"] --> S2["无交错状态(No Interleaving)"] S2 --> S3["单一职责 / 单一概念 / 明确维度"] S3 --> S4["客观属性(Objective Notion)"] end subgraph Complex["Complex(复杂:Com-Plex)"] C1["多股编织(Braided Together)"] --> C2["交织折叠(Interleaved / Folded)"] C2 --> C3["概念与职责深度缠绕"] end ``` - 词源解析:“simple”源于 *sim-plex*(单股折叠/编织),对立面是 *complex*(缠绕折叠在一起)。 - 维度的专注:简单的组件应专注单一角色、概念(如安全性)或问题空间的一维,不揉杂无关事物。 - 关键辨析:基数(Cardinality,数量多少)≠ 交织(Interleaving,缠绕与否)。简单追求的是“无交织”,而非数量只有一个。 - 客观性判断:如同 Rich Hickey 所断言,“So simple is actually an objective notion.”(因此,简单实际上是一个客观的概念)。 --- ### [05:16](https://www.youtube.com/watch?v=SxdOUGdseq4&t=316s) 词源探析:何为“Easy”(容易)的三重含义 接下来,Rich Hickey 剖析了常与“simple”混为一谈的词——“easy”(容易)。从词源追溯,“easy”最终源自拉丁语中与“adjacent”(相邻、邻近)同源的词根,本义是“位于近处、就在身边”(to lie near / to be nearby)。其反义词是“hard”(困难),词根意味着坚硬或痛苦,与距离并无对应关系。Rich 将“近”(nearness)拆解为影响软件开发者的三个层次: 1. **物理上的邻近**:触手可及。在现代软件环境中,这体现为代码就在本地硬盘上、属于现成工具链,或者能通过安装器在极短时间内一键下载安装。 2. **认知/心理上的邻近(熟悉度 Familiarity)**:处于现有的技能树或已知知识范围内。开发者往往把“我读得懂、我见过”当成容易。Rich 尖锐地反驳:自己看不懂德语,并不代表德语本身不可读,只是因为自己没学过。一味追求“熟悉”,会导致人永远无法学习新事物,因为任何真正全新的技术都不可能与既有经验高度相似。 3. **能力上的邻近(Near to Capabilities)**:处于人类思维能力的边界之内。Rich 指出,大家往往抗拒讨论这一点,因为它刺痛了程序员的自尊心。如果说拉小提琴或登山需要专门能力,大家不会觉得难堪;但软件开发是纯粹的脑力工作,承认某事超出自己的思考能力,会受到傲慢与不安全感的双重阻挠。然而事实是,人类的心智局限彼此相差无几。 与客观的“简单”截然不同,“容易”是一个完全主观且相对的概念。拉小提琴对某些人轻而易举,对另一些人则寸步难行;阅读德语亦然。Rich 批评道,当人们随口说“我喜欢某项技术是因为它简单”时,他们真正的意思往往只是“它对我来说很容易”,而这进一步等同于“它跟我已知的东西长得很像”。这种概念混淆摧毁了关于软件质量的客观讨论。 - 词源定义:“easy”源自拉丁词根,核心含义为“lie near”(邻近、在近处)。 - 容易的三重维度: - 物理邻近:现成可用、一键安装、能在 5 秒内跑起来(哪怕它内部是个巨大的复杂毛线球)。 - 认知邻近(熟悉度):类似于已有知识。但这会导致学习停滞,因为“熟悉”排斥真正的创新。 - 能力邻近:落在人脑的心智承受极限之内。 - 相对性本质:正如 Rich Hickey 所指出的,“The last thing I want to say about easy and the critical thing to distinguish it from simple is that easy is relative, right?”(关于容易,我想说的最后一点,也是将它与简单区分开的关键所在,就是容易是相对的,对吧?) --- ### [09:07](https://www.youtube.com/watch?v=SxdOUGdseq4&t=547s) 构造物与工件:开发体验与长期运行的冲突 Rich Hickey 进一步将分析推向关键领域:编程构造物(constructs)与最终运行的工件(artifacts)之间的脱节。我们在日常编码中使用的是编程语言的语法构造、语法糖和库,开发者往往沉迷于代码敲入时的即时快感,比如“我只敲了 16 个字符!连分号都不用打!”。但软件业务的本质在于它交付后的最终工件——软件并不是把源码打包送去给用户看,用户也不会因为源码写得舒服而买单;用户在乎的是软件长期运行时的表现。 系统的可靠性、性能、应对新需求时的可修改性,全部是“运行工件”的属性,而非“语法构造”的属性。然而,无论是程序员还是企业雇主,都对前两种“容易”(上手快、形式熟悉)陷入了极度痴迷。雇主尤其喜欢“认知邻近与工具普及”,因为当代码库充满大众熟悉的样板代码和工具时,开发者就变成了可以随时替换的标准化零件;只要新人能坐到工位上立刻敲键盘,企业甚至完全不在乎第三种容易——即系统本身到底有没有人能真正理解。 Rich 呼吁,软件工程必须停止把注意力锁死在打字手感、语法糖或某种工具文化的表象上,而应基于工件在长期维护中的真实表现,来重新评估我们所选用的语言和设计构造。 | 对比维度 | 构造物(Constructs)/ 追求“容易” | 工件(Artifacts)/ 追求“简单” | |:--- |:--- |:--- | | **关注核心** | 编码打字体验、语法精简(如少敲字符、无分号) | 系统长期运行的表现、健壮性与架构韧性 | | **评估依据** | 熟练度、上手速度、工具库是否现成可见 | 是否实现预期功能、缺陷修复难度、需求变更能力 | | **企业视角** | 便于招募替代人员,谁坐下都能立即写代码 | 决定系统的长期维护成本与生存寿命 | | **本质属性** | 主观、相对、局部快感 | 客观、全局、系统属性 | Rich Hickey 严肃地告诫开发者:“We have to start assessing our constructs based around the artifacts, not around the look and feel of the experience of typing it in or the cultural aspects of that.”(我们必须开始根据最终工件的属性来评估我们的编程构造,而不是依据敲代码时的体验感或者某种工具文化。) ### [12:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=739s) 人类心智的物理局限与交织带来的认知负担 Rich Hickey 开玩笑说,他前面的这套简单逻辑只是为了把大家催眠,让大家觉得他接下来讲的一切都言之成理,“因为我没办法用单子(Monads)来做到这一点”。他指出,人类根本不可能造出既高度可靠、自己又无法理解的事物。正如 Gerald Jay Sussman 教授所言,在系统的某些未来发展中,当我们为了让事物更加灵活、动态和可扩展时,必然会在理解系统行为和保证正确性的能力上做出权衡。然而,对于那些我们必须彻底理解并确保无误的核心部分,人类的认知能力其实存在极大的天然限制。 人类一次能在大脑中维持的事物数量非常有限,就像杂耍抛球一样,普通人同时能抛在空中的球非常少。当系统中的事物被紧密交织(intertwined)在一起时,我们就彻底丧失了孤立审视单个组件的能力。每次你想从软件中抽离出一块代码去理解,由于它死死绑定在另一个组件上,你就不得不把另一个组件也强行塞进大脑;两者纠缠在一起,你根本无法单独思考其中之一。这种交织给人类大脑带来的认知负担是呈组合级数(combinatorial)暴增的。因此,从根本上讲,复杂度——也就是事物之间的相互交织与编织——直接锁死了我们理解软件系统的能力。 ```mermaid flowchart TD accTitle: 紧密交织导致的认知组合爆炸 accDescr: 展示代码组件交织如何迫使人类有限的脑容量陷入过载 subgraph Mind["有限的人类心智容量"] Slots["工作记忆槽位 (极少数概念)"] end subgraph Software["交织纠缠的代码 (Braided/Intertwined)"] A["组件 A"] --- B["组件 B"] B --- C["组件 C"] A --- D["组件 D"] end Software -.->|"无法孤立理解,被迫同时载入"| Slots Slots --> Overload["认知超载 / 无法推理正确性"] ``` --- ### [14:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=844s) 敏捷神话的破灭与“非形式化推理”的必要性 面对软件系统的变更,社区流行着一种近乎迷信的论调。Rich Hickey 提到自己在当天的另一场演讲中听到有人声称:敏捷开发(Agile)和极限编程(Extreme Programming, XP)已经证明,重构和单元测试能让人以零风险、零代价完成修改。Rich 对此直言不讳地反驳道,这种说法根本就是无稽之谈:“我从来不知道有这回事。我现在依然不知道,因为那根本不是一件可知的事实,那纯粹是胡扯(That's phooey)。” 要修改软件,开发者首先必须分析代码当前的行为,并判定它未来应当如何表现。你至少必须评估一次潜在改动所带来的连带影响,弄清楚到底要修改哪些模块才能达成目标。无论你使用极限编程还是敏捷方法,都无法绕开一个冷酷的事实:“如果你无法对程序进行推理,你就无法做出这些决定(If you can't reason about your program, you can't make these decisions)。” Rich 特别澄清,这里所说的“推理”(Reason about)绝非学术界那种形式化验证或数学证明: - 他并不相信在工程中追求形式化程序证明是一个务实的目标。 - 他所倡导的仅仅是“非形式化推理”(Informal reasoning),即人们在日常生活中决定下一步做什么时所用的常识与普通逻辑。 - 开发者不需要搬出范畴论(Category theory)也能对系统做出清醒的因果推导,敏捷实践无法替代这种推导过程。 --- ### [15:33](https://www.youtube.com/watch?v=SxdOUGdseq4&t=933s) 线上 Bug 与“护栏式编程”的荒谬 软件生命周期中的任务无非两件:一是增加新功能,二是修复没做好的旧功能。Rich 向全场抛出一个耐人寻味的问题:“在线上环境中发现的每一个 Bug,都有什么共同特征?”台下有人回答“它们都被写出来了”。Rich 赞同之后追问更深层的事实:线上所有的 Bug 不仅被写了出来,而且“它通过了类型检查器,它还通过了所有的单元测试”。 面对这种全绿灯通过却依然在线上崩溃的窘境,软件界陷入了一种被 Rich 戏称为“护栏式编程”(Guard rail programming)的悲哀现状。许多开发者盲目自信地认为:“因为我有完善的测试套件,所以我可以肆无忌惮地改代码。”Rich 用了一个辛辣的比喻:这就像一个人开着车在马路上横冲直撞,一路靠不断碰撞道路两侧的护栏来防止坠毁,还沾沾自喜地庆幸“幸好有护栏,不然我都没法准时赶到会场”。 护栏根本无法引导车辆驶向目的地,测试用例遍地都是,但它们从不指引程序演进的正确方向。当所有外部测试的“护栏”都失效、Bug 依然在生产环境中爆发时,最终拯救系统的唯一手段仍然是开发者对代码进行逻辑推理的能力。唯有当系统保持简单、没有被过度交织时,开发者才能通过常识性逻辑断定“根据基本逻辑,Bug 绝不可能存在于这个模块,它必然在那个模块”,从而迅速排查定位。 --- ### [16:48](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1008s) 速度假象:百米冲刺不能叠加成马拉松 许多人对追求“简单”颇有微词,抱怨思考简单性会拖慢开发进度,吹嘘借助那些“容易上手”的现有工具,自己凭借敏捷开发跑得飞快。Rich 反问:什么样的跑者能够从起跑那一瞬间开始就竭尽全力冲刺?答案显而易见:只有跑极短途赛程的短跑选手才能做到。 软件行业自以为比专业跑者更聪明,发明了一种自欺欺人的解决办法——每跑完一百米就开一次发令枪,并将其命名为一次“新冲刺”(New sprint)。然而现实是残酷的,软件开发是一场漫长的长跑:“我的观点基于实际经验:如果你忽视复杂度,你终将慢下来。从长远来看,你注定会慢下来(It's my contention based on experience that if you ignore complexity, you will slow down. You will invariably slow down over the long haul)。” Rich 展示了一张幽默的“高度科学折线图”,坐标轴上没有任何具体数值,纯粹源自三十年的经验总结: - **聚焦于“容易”(Ease)而忽略“简单”(Simplicity)**:项目在起跑阶段确实能以最快速度狂飙,但随着时间推移,不断累积的系统复杂度最终会彻底摧毁团队。到了后期,几乎每一个 Sprint 能够交付的实质成果越来越少,团队大部分的冲刺时间都在全盘重做之前已经完成的功能,整体开发完全停滞不前。 - **起步时聚焦于“简单”**:项目初期无法达到那种狂飙的极速,因为团队必须在前置阶段进行深刻的思考,将简单性原则切实体现在架构中。但这道初期的爬坡过渡(Ramp up)一旦完成,系统将维持恒定且长久的高速演进能力。 ```mermaid flowchart LR accTitle: 易用与简单在软件生命周期中的速率对比 accDescr: 展现聚焦易用导致后期停滞,与聚焦简单带来长期恒定输出的对比 subgraph EaseApproach["路径 A:只顾易用 (Ease)"] direction TB E1["起跑狂飙 (极速原型)"] --> E2["复杂度死结累积"] E2 --> E3["每个 Sprint 都在重做旧代码"] E3 --> E4["实质进度停滞"] end subgraph SimpleApproach["路径 B:深思简单 (Simplicity)"] direction TB S1["前期爬坡 (思考与解耦)"] --> S2["构建正交简单组件"] S2 --> S3["独立变更与低调试成本"] S3 --> S4["长期持续高速前进"] end ``` --- ### [19:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1175s) 织布机隐喻与“附带复杂度”的罪责 现代软件开发中充斥着大量看似极其精简、描述极短但底层极其复杂的构件。许多危险的技术在语法层面上看起来微不足道,并且非常符合面向对象编程者的既有习惯;从常规标准来看,它们既容易获取又容易上手。然而终端用户根本不在乎程序员在写代码时有多省力、有多开心,用户真正在乎的是软件运行是否稳定可靠。 如果选用这些便捷构件所产生的系统产物充满了混乱与纠缠,那么这种复杂度就是所谓的“附带复杂度”(Incidental complexity)。这种复杂度根本不是用户原始业务需求的一部分,而是开发者盲目选择了具有固有复杂度的工具后强加进来的包袱。Rich 戏谑地给出了他自己的词源解释:“附带的(Incidental)在拉丁语里就是‘你的错’(Your fault)的意思。” 开发者必须反思自己是不是在用织布机写代码:坐在机器前愉快地将梭子飞快地穿来穿去,看似产出惊人,但织机另一端吐出来的却是一团乱麻构成的“毛线城堡”(Knitted castle): - 无论这团毛线外观编织得多漂亮,一旦你想做任何局部的结构调整,整座城堡就会因牵一发而动全身而崩溃。 - 相较之下,用乐高积木搭建的城堡(Lego castle)各个构件独立正交,拆卸与重组极具弹性。 - Rich 再次质问敏捷倡导者:“拥有测试套件和重构工具,能让你修改毛线城堡的速度比修改乐高城堡更快吗?绝对不可能,这两者毫无关系。” 保持简单带来的第一层直接收益是“易于理解”(Ease of understanding)、“易于修改”和“调试极其轻松”。第二层核心收益则是前所未有的“灵活性”与“决策独立性”:由于关注点没有被交织,开发者可以把部署位置的决策与系统性能优化的决策完全隔离开来,分别进行正交决策。 --- ### [22:34](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1354s) 认知的生理天花板:必须靠“简单”把问题拉近 如果目标不是一味哀叹软件危机,我们到底该如何让事物变“容易”(Easy)?Rich 回顾了“容易”的维度: 1. **触手可及(Location / At hand)**:将工具加入开发者的工具箱。这相对简单,执行安装即可,最多只需要说服团队管理者同意采用。 2. **熟悉程度(Familiarity)**:面对未见过的概念,通过阅读书籍、查阅教程、请人讲解以及动手实践来完成学习。 前两个维度完全掌握在开发者自己手中,我们可以持续学习更多知识。但最难克服、也最令人回避的,是人类的第三个维度——**心智能力的生理极限(Mental capability)**。 Rich 强调了一个残酷的现实:人类可以学会更多知识,但人类的大脑无法在生理上变得更聪明。我们不可能把大脑硬生生拉近复杂度的深渊,唯一的出路是通过简化事物,把事物拉回到人类大脑能够轻松处理的近处。 有人误以为世界上存在能够轻松驾驭极度复杂软件的“超级天才”,Rich 再次搬出抛球杂耍的客观规律来粉碎这种幻想:一个普通的杂耍演员可以同时抛 3 个球;全世界最顶尖的杂耍大师也许能同时抛 9 个或 12 个球,但绝对没有任何人能同时抛 20 个甚至 100 个球。面对软件系统能够轻易编织出的庞大复杂度,人类个体之间的脑力差异在统计学上几乎可以忽略不计——在应对复杂度的生理能力面前,我们所有人其实都处在同一个水平线上,那就是:我们的大脑都非常有限,都极其不擅长处理交织的事物。 ### [24:21](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1461s) 杂耍中的多余小球:Lisp 括号的“简单”与“容易”辨析 软件开发就像在空中杂耍抛接小球,人的心智容量非常有限,能够同时兼顾的事情屈指可数。在这些球当中,究竟有多少应当属于解决问题本身的必要部分,又有多少是莫名其妙被塞进来的偶发复杂度(incidental complexity)?常常有人跑过来推销说“快用这个新工具吧”,结果转眼间又往你手里扔进几颗必须要勉强应付的新球。 以 Lisp 饱受诟病的“括号太多太难”(parens are hard)为例,这种抱怨完全是程序员主观使用体验上的反馈。初学者觉得括号难以应付,首先是因为工具不顺手(not at hand)——他们的编辑器缺少括号匹配或结构化编辑的能力,甚至装了插件却从未启用相关模式;其次是因为不熟悉(not familiar),人们虽然都见过括号,但从没见过括号居然放在方法调用的左边。Rich Hickey 强调,这两点属于用户作为学习者自身应当去克服的责任。 然而,更深层的分析在于:全由圆括号构成的语言,在客观上真的“简单”吗?答案是否定的。在 Common Lisp 和 Scheme 中,圆括号的使用方式完全被重载(overloaded)了:它们既用来包裹函数调用,又用来表示代码分组,还用来表示数据结构。按照“交织缠绕即是复杂度”的定义,这种概念重载本身就是客观存在的复杂度。Rich Hickey 指出,早期的抱怨并非完全无理,但原因在于语言设计者留下了缠绕的设计:“And it was not simple for a reason that was the fault of the language designer, which was that there was overloading.”。而解决之道并非抛弃 Lisp 的本质,而是在语言中引入向量(vector)和映射(map)等额外的数据结构来消除重载。一旦客观上的缠绕被解开,剩下的就纯粹是程序员熟悉语法与配置工具的问题了。 ### [27:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1655s) 盲目追逐收益的程序员与简单/复杂工具箱 历史上曾有对 Lisp 程序员的经典讥讽,暗指他们只知概念的价值,却不知其代价——通过大量 `cons` 操作消耗内存并频繁求值,在当时受限的硬件条件下让程序运行得像头笨拙的猪。Rich Hickey 认为这句话完全可以原封不动地送给当今的技术社区。在 Hacker News 等技术论坛上,人们常常看到某个新工具就欢呼雀跃,赞叹它带来的某项优势或代码写起来多么短小,却几乎从未停下来权衡取舍:这项技术带来了什么负面代价?它有什么副产物?现代程序员往往只盯着收益,却对伴随而来的复杂性代价视而不见。 为此,Rich Hickey 罗列了工具箱中常见概念的对比清单,左侧是产生交织的“复杂(Complexity)”,右侧则是解耦独立的“更简单(Simplicity)”。他澄清自己并没有直接打上“好与坏”的道德标签,而是留给听众自行评判,但两者的结构性差异极为鲜明: | 复杂(Complexity) | 简单(Simplicity) | 简要说明 | |:--- |:--- |:--- | | **状态与对象** (State & Objects) | **值** (Values) | 状态交织了时间与值,而纯粹的值完全不可变且独立。 | | **方法** (Methods) | **函数与命名空间** (Functions & Namespaces) | 类和方法往往构成了极其局促狭隘的迷你命名空间。 | | **变量** (Vars / Variables) | **托管引用** (Managed references) | 显式分离身份与状态演进,减少不受控的就地修改。 | | **继承 / 条件分支 / 模式匹配** (Inheritance / Switch / Pattern matching) | **按需多态** (Polymorphism à la carte) | 避免将类型层级与调度逻辑强行捆绑在一起。 | | **语法** (Syntax) | **纯数据** (Data) | 数据结构透明且易于自省,无需复杂的 AST 语法解析包裹。 | | **命令式循环 / Fold** (Imperative loops / Fold) | **集合函数** (Set functions) | 循环和甚至高级的 fold 仍绑定了迭代顺序或结构,集合操作更声明式。 | | **Actor 模型** (Actors) | **队列** (Queues) | Actor 常常将处理逻辑、状态与通信机制交织,队列职责更单一。 | | **ORM** (对象关系映射) | **声明式数据操作** (Declarative data manipulation) | ORM 试图强行弥合对象与关系模型,带来无穷伪抽象。 | | **最终一致性** (Eventual consistency) | **一致性** (Consistency) | 不一致性在概念上迫使人同时思考分散各处的状态,极其烧脑。 | | **条件判断** (Conditionals) | **规则引擎** (Rules) | 条件分支将流程与判定强耦合,规则逻辑则更平铺可组合。 | Rich Hickey 提醒大家牢记“简单”的本义是“不缠绕、不扭结”,绝不能把它偷换成“我已经理解它的含义”:“Simple does not mean 'I already know what it means.'” ### [31:43](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1903s) Complect:将事物编织缠绕的原罪 为了精准表达软件是如何被一步步搞砸的,Rich Hickey 引入了一个古英语词汇——“complect”。它代表将原本独立的条带互相编织(braid)、穿插缠绕(entwine)在一起。他直言不讳地指出,大家不需要用温和中性的词,因为 complect 本身就带着天然的负面色彩:“Complect is obviously bad. Right? It happens to be an archaic word, but you know, there's no rules that say you can't start using them again.” 复杂度正是在不断 complect 的过程中滋生出来的。从物理结构上看,如果拿出四根笔直垂落的布条,每一根清晰明了,一眼就能看懂;但只要有人将它们互相交错编织成麻花,虽然底层的布条数量和质地没有发生任何改变,最终却变成了一个令人头痛的死结。编程也是同样的道理:同一套业务功能,可以用四行清晰平铺、互不缠绕的解耦代码实现;也可以用另一种语言或糟糕的抽象构架,生生将其绞成一团谁也理不清的乱麻。 ```mermaid flowchart LR accTitle: Complect versus Compose accDescr: Visualizing the difference between complecting (braiding together) and composing (placing together side by side). subgraph S1["Complect(编织缠绕 - 复杂)"] direction TB A1["逻辑 A"] --- B1["逻辑 B"] B1 --- C1["逻辑 C"] C1 --- A1 end subgraph S2["Compose(并置组合 - 简单)"] direction TB A2["独立模块 A"] B2["独立模块 B"] C2["独立模块 C"] A2 -.->|"清晰协议连接"| B2 B2 -.->|"清晰协议连接"| C2 end ``` ### [33:08](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1988s) 模块化的谎言:代码组织不等于系统简单 软件工程界长久以来存在一个共识:我们要构建可组合(composable)的系统。词源上,“compose”仅仅意味着“放置在一起”(to place together),通过并置简单的组件来构建健壮的软件,这个理念本身没有争议。然而,许多人由此产生了一个致命的误解,以为只要把软件做成“模块化”(modular),系统自然就变得简单了。 Rich Hickey 尖锐地反驳了这种观点。他在台上一边调侃自己是不是该讲完下台了,一边举起双手询问台下究竟有多少人见过所谓“模块化”却彼此深度缠绕的代码。两个看似完全独立的模块,可能表面上连显式函数调用都没有,暗地里却在运行逻辑上彻底绑定在一起。这就像乐高积木一样,模块化要真正起作用,关键在于组件被允许“思考”什么——组件之间绝不能去感知对方内部的具体实现,而只能依赖于极其精简、统一的抽象凸起接口。 如果组件本身是高度复杂、互相关联的,那么无论你在水平方向上如何横向切割,或者在垂直方向上做多少层分层架构(stratification),得到的都只是把一团乱麻切成好几段而已,根本无法带来任何本质上的简单性。Rich Hickey 郑重告诫听众,千万不要被优雅的代码组织形式所蒙蔽:类库看起来由很多彼此分离的类构成,调用关系似乎井井有条,可一旦拿到生产环境跑起来,才发现某个模块居然暗中假设另一个模块绝不能返回数字 17。 ### [35:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2139s) 状态的代价:He-Man 式编程与时间交织 谈及有状态编程(stateful programming),Rich Hickey 澄清自己并不是站在某种纯函数式编程的高尚道德制高点上进行空洞说教,他的反思完全来自切肤之痛。他回顾了自己早年从事 C++ 开发的漫长岁月,将其戏称为充满猛男硬汉色彩的“He-Man 式状态编程”:“I did years and years, C++ you know, He-Man stateful programming. It's, it's really not fun. It's not good. It's, it's never simple.” 在程序中引入状态,其本质就是永远不可逆转地把“值”(value)与“时间”(time)给 complect(缠绕)在了一起。一旦某个变量被赋予了可变状态,你就再也无法脱离时间维度的限制去获取一个客观、纯粹的值。你此刻读到的变量,不仅受制于当下的读取时刻,还随时可能在下一个时刻被其他并发执行的指令所篡改,甚至连在真正意义上拿到一个准确的值都变成了奢望。有状态编程之所以在行业内大行其道,仅仅是因为它太容易(easy)、太贴近人们习惯性思考物理世界的直觉,但这绝不意味着它在系统设计上是简单的。 ### [36:28](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2188s) 状态的渗透性毒害与心智负担 编程语言普遍将可变状态置于随手可得的位置,让开发者轻而易举地陷入复杂性之中。许多人以为借助面向对象的模块化封装就能万事大吉——只要把赋值语句藏在类的方法内部,外界看不见变量就算解耦。然而,Rich Hickey 指出这完全是一种自欺欺人:如果一个方法在传入完全相同的参数时,因内部状态不同而返回不同的结果,状态的复杂性就会立刻从封装的裂缝中渗透出来。只要包裹它的外层是有状态的,调用者每次提问依然会得到不同的答案。 Rich Hickey 用了一个极具画面感的比喻来形容状态的破坏力:“它就像毒药,就像把深色墨水滴进花瓶里一样,最终会蔓延渗透到每个角落。”消除这种复杂性的唯一途径,是将其严格限制在一个对外呈现纯函数式接口(True Functional Interface,即相同输入必得相同输出)的边界之内;寻常的代码组织方式与模块化手段根本无法阻挡状态的扩散。 更关键的是,状态问题经常被误认为是并发编程的专属痛点。Rich Hickey 强调这与并发无关,它关乎的是开发者理解程序运行逻辑的心智极限。即使是一个完全单线程的程序,哪怕所有单元测试全部通过、类型检查器一路绿灯,系统一旦在生产环境中崩溃,开发者仍然束手无策。如果代码里塞满了可变变量,要想重现故障,就必须完整还原客户端发生故障时那一瞬间的隐式状态,而这在现实中几乎是一件不可能轻松完成的苦差事。 - 状态的定义:每次用相同的问题去询问同一个组件,却得到了不同的答案。 - 纯函数边界:只有对外提供严格的“同输入即同输出”的构件,才能阻断状态复杂性的外溢。 - 调试的梦魇:单线程程序同样会被可变状态摧毁,其根源在于外部无法有效捕获或重现错误发生时的瞬时状态。 ### [38:02](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2282s) 语言中的状态构造:变量、引用与时间解耦 很多新型编程语言标榜自己解决了状态问题,引入了 `var` 或引用(`ref`/`reference`)等语法机制。但在 Rich Hickey 看来,这些语法糖并没有真正让状态变简单,甚至 Clojure 自身的状态构件也谈不上从本质上简化了状态。不过,这类显式的状态声明至少带来了一大收益:它们会在状态出现时发出明确警告。在不可变性非默认的语言中,一旦开发者需要特意声明才能使用可变状态,他们写出的代码中状态总量往往会呈现数量级式的骤降,因为大多数业务场景起初根本不需要可变状态。 在众多语言机制中,Rich Hickey 尤其推崇 Clojure 和 Haskell 处理引用的方式。这两者之所以更为优越,是因为它们在概念上拆分并重新组合了“值”(Value)与“时间”(Time)。这些构件一方面提供了对时间流逝的抽象,另一方面赋予了开发者在特定时间点提取不可变“值”的能力。这种从引用中读取并带走不可变快照的能力,是通往简单性的核心路径;若缺乏这一层解耦,将可变变量到处传递只会让整个系统持续遭受状态毒害。 Rich Hickey 顺势调侃了前一天大会 Keynote 上展示的一张所谓“理想计算机内存模型”幻灯片——图里声称解引用一个内存地址就能直接还原出一个复合对象。Rich Hickey 幽默地感叹道:“我都想买一台那样的电脑了,你们谁用过?反正我买不到,我特意打电话问了苹果公司,他们说根本没有这种东西。”现实中的计算机在解引用地址时,永远只能拿到一个标量(Scalar)或者一个字(Word),没有任何机器能通过单次解引用直接还原一个复合的可变对象。正因如此,普通的变量机制彻底交织了值与时间,让开发者无法安全截取一致的数据状态。 > Rich Hickey 明确指出:“没有哪种机制能让状态变得简单,我甚至不会这么标榜 Clojure 自己的构件;在简单性的本质层面上,它们并没有把状态化繁为简。” ### [39:26](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2366s) 软件构造中的交织清单(Complecting) 为了厘清复杂性的根源,Rich Hickey 逐一检视了现代编程范式中常见的各种抽象构件,揭示它们如何将原本独立的概念紧紧“编织”(Complect)在一起,导致系统难以拆卸和推理: ```mermaid flowchart TD accTitle: 编程构件中的交织关系图 accDescr: 展示对象、方法、语法、继承等构件所错误捆绑交织的概念维度 subgraph OO["面向对象与代码组织"] Obj["对象 (Objects)"] -->|"交织"| C1["状态 + 身份 + 值"] Method["方法 (Methods)"] -->|"交织"| C2["函数 + 状态 + 命名空间"] Inh["继承 (Inheritance)"] -->|"交织"| C3["类型与类型之间的绑定"] end subgraph Lang["语言表意与控制流"] Syn["语法 (Syntax)"] -->|"交织"| C4["含义 + 单向时序"] Switch["分支匹配 (Switch/Match)"] -->|"交织"| C5["执行主体与处理逻辑"] Loop["循环与折叠 (Loops/Fold)"] -->|"交织"| C6["计算目标 + 遍历步骤/方向"] end subgraph System["系统与架构抽象"] Actor["Actor 模型"] -->|"交织"| C7["做什么 + 由谁来做"] ORM["对象关系映射 (ORM)"] -->|"极限交织"| C8["关系数据 + 内存对象图"] Cond["散落的条件分支"] -->|"交织"| C9["业务规则 + 程序组织结构"] end ``` - **对象(Objects)**:交织了状态(State)、身份(Identity)与值(Value)。这三者被杂糅在一个结构体中,开发者无法单独分离其中任何一项。 - **方法(Methods)**:交织了函数逻辑与内部状态;在 Java 等语言中,方法还与命名空间发生冲突——若子类继承的两个接口含有同名同参方法,系统便陷入混乱。 - **语法(Syntax)**:交织了含义与语序,且往往是严格单向的。Rich Hickey 赞同 Gerald Sussman 教授关于“数据优于语法”的论断,直言无论你多么迷恋某种语言的花哨语法,“它在各个方面都彻头彻尾地劣于纯粹的数据”。 - **继承(Inheritance)**:通过强行绑定将两种类型交织在一起,这在定义上就是典型的主动制造编织。 - **模式匹配与分支判断(Switching / Matching)**:在一个封闭的代码块中,硬生生交织了“谁来处理”与“具体做什么”的多组配对关系。 - **变量(Vars / Variables)**:将值与时间无法分割地绑死,导致外部无法安全读取稳定状态。 - **循环与折叠(Loops / Fold)**:普通循环交织了“做什么”与“怎么遍历”;即便是高阶抽象的 `fold`,也暗中交织了从左至右的处理顺序约束。 - **Actor 模型**:将“要执行的业务任务”与“承载该任务的执行节点/进程”强行交织在了一起。 - **对象关系映射(ORM)**:Rich Hickey 惊呼其编织程度堪称灾难,根本无法用简单的语言描述其恶劣程度。他借用数学对偶(Dual)的概念嘲讽道:如果说“值”代表一致性,那 ORM 映射出来的东西就是对偶物“Co-value”,其本质就是“不一致性”,根本没人真正需要这种糟糕的东西。 - **散落的条件语句(Conditionals)**:大量业务规则被生硬地切割并嵌在系统的各个执行角落,规则本身与程序的控制流结构完全交织。 > Rich Hickey 犀利地总结继承与语法的本质缺陷:“我不在乎你有多喜欢自己钟爱语言的语法,它在各个方面都比数据低劣得多。” ### [42:54](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2574s) 用更纯粹简单的工具构建同等复杂的系统 Rich Hickey 提出本次演讲希望听众带走的两大核心收获:第一是分清“Simple”(客观朴素、未编织)与“Easy”(主观便捷、随手可得);第二则是建立一种信心——我们完全可以用极其简单、朴素的工具,构建出与现有重度复杂工具功能完全等价的大型系统。Rich Hickey 回顾了自己数十年的职业生涯,他曾深度使用 C++、Java 和 C# 开发过规模庞大的系统,因此深知那些面向对象机制的繁文缛节绝非必需。摆脱那些语言构件附带的泥沼杂质后,开发者才能把精力彻底聚焦在系统“应该做什么”这一本质目标上。 为了迈向简单的软件工程实践,Rich Hickey 梳理了一份由繁入简的替代清单: | 制造交织的复杂工具 | 朴素纯粹的简单替代方案 | 替代方案的核心优势 | |:--- |:--- |:--- | | 可变变量 / 对象状态 | 不可变值(Values)与持久化集合(Persistent Collections) | 大多数语言支持 `final` 或 `val`;不可变聚合消除时间与状态绑死的问题 | | 有状态的类方法 | 纯函数(Functions) | 纯粹的无状态逻辑单元(Stateless Methods),相同输入必得相同输出 | | 充血领域模型 / 嵌套类 | 纯数据(Maps、Sets、Sequences) | 数据的本质分类极少,无需为特定业务凭空发明几十万种无关本质的变体类 | | 类继承多态 / 接口绑定 | 自由组合的多态(Polymorphism à la carte) | 类似 Clojure Protocols 或 Haskell Type Classes,将数据结构、函数集与绑定动作三者解耦 | | 紧密绑定的 RPC / 命令行文本 | 数据驱动的通信与队列(Queues) | 告别 Unix 那种依赖文本解析的命令行字符串拼接,统一采用结构化数据通信 | | 散落在代码中的条件嵌套 | 声明式数据操作(SQL、LINQ、Datalog)与规则引擎 | 将决策规则与业务流转从宿主语言结构中剥离,集中由规则系统或事务处理 | Rich Hickey 语重心长地呼吁:“程序员们,按理说我们写的是‘数据处理程序’,可看看现在的系统,里面根本没有真正的数据!”充斥系统的是层层包裹在数据之外的繁杂对象与类包装。实际上,纯数据的形态极其精炼,无非是映射(Maps)、集合(Sets)以及线性序列(Sequential lists)。人们为了包装数据发明了成千上万种毫无本质意义的类型变体,反而极大阻碍了通用的数据操纵能力。 此外,通信层面亦是同理。Web 通信之所以进步,是因为抛弃了早年 Unix 管道中“任意字符串作为参数、输出任意字符流、全靠写解析器处理”的混乱模式。而在抽象层面,最强大的武器当属“自由组合的多态”(Polymorphism à la carte):它让数据结构的定义、函数签名契约的定义、以及两者之间的实现绑定彻底成为三个互不干扰的独立维度。虽然类似规则引擎、LINQ 或 Datalog 这种与语言深度集成的声明式方案在当下的生态中仍有门槛,但它们指明了软件架构告别复杂泥潭的正道。 > Rich Hickey 总结道:“你完全可以用大幅简化的工具构建出同等精密的系统,这意味着你将真正聚焦于系统本身应有的功能,而不是陷入所用构件衍生出的一堆烂摊子中。” ### [47:22](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2842s) 环境复杂性:固有复杂度的真正来源 在阐述了可以通过选择简单工具来规避的人为复杂性(Incidental Complexity)之后,Rich Hickey 话锋一转,引出了另一类开发者极难掌控且无法归咎于个人的复杂性——环境复杂性(Environmental Complexity)。 编写完成的程序最终都必须部署到实体机器上,与宿主机内的其他程序、甚至自身的各个子模块并发运行。在这一物理现实下,所有进程都在疯狂争抢共享资源:物理内存、CPU 运算周期、网络带宽与磁盘 I/O。这种竞争是实现层面无法回避的客观规律。Rich Hickey 带着调侃的语气现场幽默了一把:“这里的‘固有’(Inherent),在实现层面的语境里,其拉丁语词根的意思其实就是‘不是你的错’。”这种复杂性固然不是原始业务问题本身所固有的,但只要代码落地到物理实现空间,竞争与环境制约就会不可避免地降临。 ### [47:56](https://www.youtube.com/watch?v=SxdOUGdseq4&t=2876s) 策略不可组合的困境与抽象的真正含义 Rich Hickey 坦言,开发者无法理直气壮地对客户说“你想要的功能不行,因为我遇到了垃圾回收(GC)问题”,但在现实工程中,垃圾回收和硬件资源分配等底层瓶颈确实会横生枝节。虽然人们可以尝试采用硬件分片或资源隔离策略——为各个模块切分固定的内存与 CPU,但这种做法极其浪费,因为它强制进行了静态预分配,彻底抹杀了系统的动态伸缩能力。 更深层的危机在于,这类针对底层资源的控制策略根本无法组合。Rich Hickey 犀利地指出:“If everybody says I'll just size my thread pool to be the number of cores. Well, how many times can you do that in one program.” 如果系统中的每一个独立组件都自作聪明地将自己的线程池大小设定为 CPU 核心数,整个程序很快就会在资源争抢中崩溃。分散地做出这类决策非但没有让系统更简单,反而引入了极大的偶合与复杂度,因为这类决策理应由掌控全局信息的协调者在单一位置做出。随后,Rich Hickey 引用了一段名言强调:编程的本质不是敲代码,而是思考。 要设计出属于自己的简单事物,首要工作是通过“抽象”(Abstraction)来解耦。Rich Hickey 澄清了词源上的严谨定义:抽象是指“将某物抽离出来”(to draw something away),特别是抽离其实际的物理实体属性。他严厉抨击了行业内滥用该词的现象:“I do want to distinguish this from the, sometimes people use this term really grossly to just mean hiding stuff. That is not what abstraction is, and that's not going to help you in this space.” 单纯把实现细节隐藏或封装起来根本不是抽象。在系统设计中,最行之有效的心态恰恰是“我不知道,我也不想知道”(I don't know, I don't want to know)。Rich Hickey 回忆自己在讲授 C++ 课程时常把这句话挂在嘴边,以至于有位学生专门送了他一件印有 Booch 图(Booch diagram)的 T 恤,图上的每一条连线旁都印着这句名言。 ```mermaid flowchart TD accTitle: 真正抽象与伪抽象的对比 accDescr: 对比真正的抽象(抽离本质与解耦)和伪抽象(仅仅隐藏细节),展示两者的后果。 subgraph TrueAbs["真正的抽象 (Abstraction)"] direction TB A1["审视物理实体与机制"] --> A2["抽离出纯粹的规格说明 (Specification)"] A2 --> A3["保持‘不想知道具体实现’的边界解耦"] end subgraph FalseAbs["常见的伪抽象 (Information Hiding)"] direction TB B1["将复杂机制打包封装"] --> B2["隐藏具体内部字段与状态"] B2 --> B3["实质仍紧密交织 (Complected)"] end ``` --- ### [50:39](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3039s) 简化的拆解维度:What、Who、How 为了系统化地拆解问题,Rich Hickey 推荐运用“Who、What、When、Where、Why”的新闻学五要素分析法,逐一审视设计决策。 关于“What”(做什么):这是由操作和目标构成的抽象,即用命名来定义一组操作或函数集合。无论编程语言提供的是接口(interface)、协议(protocol)还是类型类(type class),它们的核心使命都是充当规格说明(specification)。Rich Hickey 强调这些接口必须保持极其小巧: - 现代 Java 等语言中的接口往往庞大臃肿,主要原因在于缺乏联合类型(union types),导致开发者被迫将若干功能捆绑成一个大而全的接口。 - 抽象规格说明在签名中应当且仅当使用不可变的值(values)和其他抽象概念作为输入与输出。 - 最严峻的隐患是把“做什么”(What)与“怎么做”(How)交织(complect)在一起。例如在 API 中直接暴露具体类而非接口,或是在语义中暗示某种固定的求值机制(如 `fold` 操作隐式规定了顺序遍历的方式)。将两者严格剥离,才能把具体的执行策略完全甩给下游机制(如数据库引擎或逻辑求解器)。 关于“Who”(实体与组件):这指代抽象最终所关联的数据或具体实体。构建顶层组件时应当采用直接注入的形式组合子组件,避免把依赖关系写死在代码中。Rich Hickey 认为绝大多数系统中的子组件数量严重不足,开发者往往习惯把所有逻辑塞进一个单元;如果用分析法拆解出了五个小组件,这不仅不是问题,反而是巨大的设计胜利。 关于“How”(如何实现):这是真正的执行代码。实现部分应当通过语言的多态机制与上层规格说明相连接,特别是开放运行时多态(open polymorphism),它的表达力远胜于把所有逻辑写死在单个模式匹配(pattern matching)或 `switch` 分支中。Rich Hickey 警示道:“Beware of abstractions that dictate how in some subtle way, because when you do that, you're really, you're nailing the person down the line who has to do the implementation, you're tying their hands.” 实现代码本身应该尽可能成为互不干扰的独立孤岛。 --- ### [54:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3275s) When、Where、Why 的解耦与“信息即数据” 在处理时间与空间(When and Where)维度时,系统中最常见的反模式是将对象直接连接: ```mermaid flowchart LR accTitle: 对象直接调用与队列解耦对比 accDescr: 模块A直接调用模块B导致时空偶合,而引入队列后彻底解除了时空交织。 subgraph DirectCall["直接连接:产生时空交织 (Complected)"] direction LR A["组件 A"] -->|"必须知道 B 的位置 (Where)<br/>必须立刻阻塞执行 (When)"| B["组件 B"] end subgraph QueueDecoupled["队列解耦:彻底分离 When 与 Where"] direction LR C["组件 A"] -->|"写入数据"| Q[("队列 (Queue)")] Q -->|"独立拉取"| D["组件 B"] end ``` 如果组件 A 在处理完输入后直接调用组件 B,A 不仅必须知道 B 的网络位置或内存地址(Where),还锁定了调用的精确时机(When)。Rich Hickey 直截了当地指出解除这种交织的利器:“Stick a queue in there, right? Queues are the way to just get rid of this problem. If you're not using queues extensively, you should be, you should right away, like right after this talk.” 关于“Why”(业务规则与策略):业务逻辑和规则往往被随意散落在应用程序各处,导致开发者无法把源码拿给客户一起审查逻辑。Rich Hickey 讽刺了那些编写纯英文测试用例以取悦客户的伪测试框架,呼吁应当使用独立的声明式系统或规则引擎,将业务策略外置化。 关于“信息”(Information):Rich Hickey 抛出了颠覆面向对象教条的论断:信息本身是极其单纯的,面向对象机制本意只是用来封装像屏幕、鼠标这类无法直接触碰的 I/O 外部硬件,根本不应该强套在纯粹的信息数据之上: - 把数据包裹在类或 ORM 中不仅扼杀了编写通用数据操作函数的能力,还会把逻辑与具体的物理表现形式深深绑定。 - 正确的做法是“将数据视为纯粹的数据”(represent data as data),直接拥抱最基础的映射(map)与集合(set),而不是每来一个新信息字段就去新建一个无谓的 Class。 对于如何简化既有的复杂遗留系统,Rich Hickey 总结其核心任务就是解开那些交缠在一起的逻辑线条(disentangling),追踪并重构每一个依赖标签。在演讲的收尾阶段,Rich Hickey 掷地有声地总结道:“The bottom line is simplicity is a choice. It's your fault if you don't have a simple system.” 系统的简单与否不是命运的偶然,而是开发者主动选择的结果。 ### [58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) 复杂性文化与纠缠雷达:摆脱“容易即简单”的思维定势 软件工程行业深陷于一种根深蒂固的“复杂性文化”中。行业里的开发者不断使用那些产出物极度复杂的工具,长此以往陷入了自我强化的陈规陋习之中,难以自拔。Rich Hickey 指出,大家必须跳出这个思维定式。对于台下那些已经理解并践行这套理念、早已选用幻灯片右栏推荐技术(即简单解耦的替代方案)的听众,他希望这次演讲能够成为一套有力的论据库,帮助他们去说服身旁那些仍执迷不悟、不相信“简单优于复杂”的同行。 选择简单绝非一劳永逸,而是一个需要“时刻保持警惕”(constant vigilance)的主动抉择。许多人寄希望于护栏机制,但早前的分析已经证明,所谓的安全护栏根本无法自动带来简单性,在这方面起不到实质帮助。追求简单需要开发者建立起真正的鉴赏力与审慎态度,而多数人最大的盲区就在于直觉性地将“易用”(ease of use)等同于“简单”(simplicity)。正如 Rich Hickey 所言:“Your sensibilities about simplicity being equal to ease of use are wrong.”(你们认为简单等同于容易使用的直觉是错误的。) 要想看清本质,就必须彻底区分“简单”(未交织缠绕)与“容易”(近在手边、熟悉好用)的词源与定义。开发者必须训练并建立自己的“纠缠雷达”(entanglement radar)。当审查一段软件代码时,关键关注点不应仅仅停留在变量命名好不好听、代码外形轮廓是否优雅,或者行尾有没有漏掉分号——这些固然有其意义,但真正的功力在于一眼洞穿系统内部是否存在“complecting”(编织缠绕)。开发者的雷达必须能敏锐捕捉到原本可以彼此独立、如今却被强行绑死互联的各个部件,因为解除这种交织才是获取系统威力与自由度的根本源泉。 ```mermaid flowchart TD accTitle: 复杂与简单的思维路径分歧 accDescr: 对比容易导向的复杂缠绕与简单导向的解耦构造 subgraph EasyPath["容易导向 (追求熟悉与就手)"] E1["偏好熟悉的语法与工具"] --> E2["忽视深层交织"] E2 --> E3["事物打成死结<br/>(Complecting)"] E3 --> E4["依赖辅助护栏修补"] end subgraph SimplePath["简单导向 (培养纠缠雷达)"] S1["关注产物本身的结构"] --> S2["敏锐识别事物交织"] S2 --> S3["各自独立垂直垂挂<br/>(Untangled)"] S3 --> S4["赋予系统极高的变更能力"] end ``` ### [59:32](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3572s) 安全网的局限性与产物优先:关注长期运行的真正构造 开发者手中掌握的所有关于可靠性的工具(例如复杂的测试套件或重型类型系统),在性质上全都属于次要手段,因为它们根本不是冲着“简单性”去的。Rich Hickey 直截了当地指出,这些保障机制完全触碰不到复杂性问题的核心:“They're safety nets, but they're nothing more than that.”(它们只是安全网,除此以外什么都不是。)如果系统的底层架构从一开始就纠缠不清,再多的安全网也无法消除复杂带来的隐患。 那么,究竟该如何让“简单”变得“容易”?答案在于做出明确的选择:选用那些能够生成简单产物(simpler artifacts)的语言构件,坚决回避那些产物极其复杂的构件。Rich Hickey 强调,评价的核心是“产物本身”,而不是“编写代码时的个人快感”(artifacts, not authoring)。在团队协作中,一旦有人为诸如“我们应该用某某花哨语法或框架”而争吵,就必须把讨论焦点纠正过来:编写代码时敲入键盘的视觉排版和主观手感完全是无关紧要的表面功夫,真正要日复一日与之共存、长期维护和承担后果的,正是那些编译与运行出来的底层产物。 - 必须分清代码编写体验(authoring)与系统运行产物(artifacts)的根本区别,产物的结构决定了维护生命周期的成本。 - 静态检查、自动化测试和类型保护机制等可靠性工具充其量只是跌落时的安全气垫,无法反向治愈设计本身的缠绕病灶。 - 构建抽象必须以简单为基石,在真正动手敲代码前投入时间在源头上将问题解构简化。 ### [01:00:25](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3625s) 简单并非数量之争:将纠缠解开赋予系统变更的能力 在推进简化的过程中,开发者往往会遇到一个违反直觉的现象:当你把事物彻底理清、去除缠绕后,系统里的构件数量往往反而变多了。对此,Rich Hickey 澄清道:“Simplicity is not about counting, right?”(简单性绝非看构件的数量,对吧?)他用了一个极具画面感的比喻来解释构件数量与复杂度的关系:“I'd have rather have more things hanging nice, straight down, not twisted together then just a couple of things tied in a knot.”(我宁愿要更多个各自笔直向下垂挂、绝不扭结在一起的物件,也不想要仅仅两三个紧紧系成死结的物件。) 把系统部件彻底分离独立开来的最大益处,在于赋予系统应对未来变更的极致灵活性。现实中软件系统维护的最大痛点就是牵一发而动全身,而解开缠绕后,你便能随意替换、修改或扩展其中任意单一部件,而无需担忧整个系统随之坍塌。这才是拥抱“简单”所带来的核心生产力收益。 在全场演讲的最后,Rich Hickey 呼吁大家将这套反思带入日常工程实践,或者将其作为说服团队同仁走向简单主义的思维工具。在全场热烈的掌声与笑声中,他给出了最后一句意味深长、幽默锋利的警句,专门送给那些试图推销繁琐机制的人:“This is what you say when somebody tries to sell you a sophisticated type system.”(当有人试图向你兜售一套复杂的类型系统时,这就是你应该给他们的回答。) ## Notable quotes > [00:04](https://www.youtube.com/watch?v=SxdOUGdseq4&t=4s) We need to build simple systems if we want to build good systems. — Rich Hickey > [16:48](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1008s) 如果你忽视复杂度,你终将慢下来。从长远来看,你注定会慢下来。 — Rich Hickey > [54:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3275s) The bottom line is simplicity is a choice. It's your fault if you don't have a simple system. — Rich Hickey > [58:19](https://www.youtube.com/watch?v=SxdOUGdseq4&t=3499s) Your sensibilities about simplicity being equal to ease of use are wrong. — Rich Hickey > [27:35](https://www.youtube.com/watch?v=SxdOUGdseq4&t=1655s) Simple does not mean 'I already know what it means. — Rich Hickey > [01:24](https://www.youtube.com/watch?v=SxdOUGdseq4&t=84s) So simple is actually an objective notion. — Rich Hickey ## Mentioned Rich Hickey, Strange Loop, Edsger W. Dijkstra, Gerald Jay Sussman, Clojure, Datomic, InfoQ, Haskell, Common Lisp, Scheme, Java, C++, C#, LINQ, SQL, Datalog, Apple, Hacker News ## Video description Rich Hickey, the author of Clojure and designer of Datomic, is a software developer with over 30 years of experience in various domains. Rich has worked on scheduling systems, broadcast automation, audio analysis and fingerprinting, database design, yield management, exit poll systems, and machine listening, in a variety of languages. This keynote was given at Strange Loop 2011, and is perhaps the best known and most highly regarded of Rich's many excellent talks, ushering in a new way to think about the problems of software design and the constant fight against complexity. The video was recorded at Strange Loop in partnership with InfoQ, who have hosted it on their site since 2011. This version (released 10 years later) is a new edit made from the original HD video and slides, restoring the slide transitions and animations as it was given in 2011. [Source](https://www.youtube.com/watch?v=SxdOUGdseq4)

Source quotes

[0:04] Rich Hickey: We need to build simple systems if we want to build good systems. [16:48] Rich Hickey: 如果你忽视复杂度,你终将慢下来。从长远来看,你注定会慢下来。 [54:35] Rich Hickey: The bottom line is simplicity is a choice. It's your fault if you don't have a simple system. [58:19] Rich Hickey: Your sensibilities about simplicity being equal to ease of use are wrong. [27:35] Rich Hickey: Simple does not mean 'I already know what it means. [1:24] Rich Hickey: So simple is actually an objective notion.

Source quotes

[50:39] Rich Hickey: Strictly separating what from how is the key to making how somebody else's problem. [58:19] Rich Hickey: Your sensibilities about simplicity being equal to ease of use are wrong. [39:21] Rich Hickey: I don't care how much you really love the syntax of your favorite language. It's inferior to data in every way. [57:21] Rich Hickey: The bottom line is simplicity is a choice. It's your fault if you don't have a simple system. [56:07] Rich Hickey: The only thing you can possibly do with information is ruin it. [22:34] Rich Hickey: We actually can't get much smarter. We're not going to move our brain closer to the complexity. We have to make things near by simplifying them.

Original transcript

[0:04] All right, who's ready for some more category  theory? You're all in the wrong room. [0:16] So this talk I hope seems deceptively obvious. One  of the things that's great about this conferences, [0:26] this is a pretty cutting edge crowd. A lot of  you are adopting new technologies. A lot of you [0:30] are doing functional programming and you may, you  may be nodding saying, yeah, yeah, yeah through [0:35] parts of this. And if some of it's familiar then  that's great. On the other hand, I think that [0:43] I would hope that you would come away from  this talk with some tools you could use [0:47] to help conduct a similar kind  of discussion to this talk [0:51] with other people that you're trying  to convince to do the right thing. [0:58] So I'll start with an appeal to authority.  "Simplicity is a prerequisite for reliability." [1:05] I certainly agree with this. I don't agree  with everything Dijkstra said and I, and I [1:09] think he might've been very wrong about proof in  particular, but I think he's right about this. [1:17] We need to build simple systems  if we want to build good systems. [1:21] And I don't think we focus on enough on that. [1:24] I love word origins. They are tremendous  fun. One of the, the reasons why they're [1:29] fun is because words eventually become come  to mean whatever we all accept them to mean, [1:34] you know, whatever's commonly understood  to be the meaning is what it means. [1:38] And it's often interesting to say, well, I wish I  could, I wish we'd go back to what it really means [1:43] and use that. And I think there's a couple  of words that I'm going to use in this talk [1:46] that I would love for you to come away  knowing the origins of, and try to use [1:51] more precisely, especially  when talking about software. [1:56] So the first word is "simple". And the roots  of this word are "sim" and "plex", and that [2:02] means one fold or one braid or twist. And that  characteristic about being about one literally [2:14] fold or twist, of course, one twist, Hey, what's  one twist look like? No twists, right? Actually. [2:22] And it, the opposite of this word is complex,  which means braided together or folded together. [2:31] Being able to think about software in terms of  whether or not it's folded together, is sort of [2:34] the central point of this talk. The other word  we frequently use interchangeably with "simple" [2:41] is the word "easy". And the derivation there is  through a French word, and the last step of the [2:47] derivation is actually speculative but I bought  it because it serves this talk really well. [2:54] And that is from the Latin word that is the  root of "adjacent", and which means to lie near [3:00] and to be nearby. And the opposite is "hard".  Of course, the root of hard has nothing to do [3:05] with lying near. It doesn't mean lie far away.  It actually means like strong or tortuously so. [3:16] So if we want to try to apply simple  to the kinds of work that we do [3:21] we're going to start with this  concept of having one braid [3:25] and look at it in a few different dimensions.  I thought it was interesting in Eric's [3:29] talk to talk about dimensions because it's  definitely a big part of doing design work. [3:34] And so if we want to look for simple things,  we want to look for things that have sort of [3:37] one of something they do, they have one role, [3:41] they fulfill one task or job they're  about accomplishing sort of one objective. [3:47] They might be about one concept like security  and sort of overlapping with that as they may, [3:57] they may be about a particular dimension  of the problem that you're trying to solve. [4:02] The critical thing there though, is that when  you're looking for something that's simple, [4:05] you want to see it have focus in these areas. [4:08] You don't want to see it combining things. On the  other hand, we can't get too fixated about one. [4:15] In particular simple doesn't mean  that there's only one of them. [4:20] Right. It also doesn't mean an  interface that only has one operation. [4:26] So it's important to distinguish [4:31] cardinality, right? Counting things from actual  interleaving what matters for simplicity is that [4:38] there's there's no, interleaving not that there's  only one thing. And that's very important. [4:47] Okay. The other critical thing about simple  as we've just described it is if something is [4:54] interleaved or not that's sort of an objective  thing. We can probably go and look and see, [5:02] I don't see any connections. I don't see  anywhere where this twists with something else. [5:06] So simple is actually an objective notion. [5:11] That's also very important in deciding  the difference between simple, easy. [5:16] So let's look at easy. I think  this notion of nearness is really, [5:21] really cool. In particular obviously there's  many ways in which something can be near, right. [5:26] The sort of the physical notion of it being near  right, is something you know, like right there. [5:33] And I think that's where the root of the word came  from. You know, this is easy to obtain cause it's, [5:38] it's nearby. It's not in the next  town. I don't have to take a horse or [5:41] whatever to go get to it. We don't have the same  notion of physicality necessarily in our software. [5:46] But we do sort of have our own  hard drive or our own tool. [5:51] Or sort of the ability to make things  physically near by getting them through [5:55] things like installers and stuff like that. The  second notion of nearness is something being near [6:01] to our understanding, right. Or in our current  skillset. And I don't mean in this case near to [6:09] our understanding, meaning a capability, I mean,  literally near something that we already know. [6:15] So the word, the word in this  case is, is about being familiar. [6:21] I think that collectively we are infatuated  with these two notions of easy. We are just so [6:29] self-involved in these two aspects  it's hurting us tremendously. [6:34] All we care about is, you know, can I get this  instantly and start running it in five seconds? [6:38] It could be this giant hairball that you  got, but all you care is "can you get it?". [6:44] In addition, we're fixated on,  oh, I can't, I can't read that. [6:51] I can't read German. Does that mean German  is unreadable? No, I don't know German. [6:58] So you know, this, this sort of  approach is, is definitely not helpful. [7:03] In particular, if you want everything to  be familiar, you will never learn anything [7:07] new because it can't be significantly  different from what you already know [7:10] and not drift away from the familiarity. [7:13] There's a third aspect of being easy that  I don't think we think enough about. That's [7:20] going to become critical to this discussion, which  is, which now is being near to our capabilities. [7:26] And we don't like to talk about this because  it makes us uncomfortable because what kind of [7:30] capabilities are we're talking about? Like, if  we're talking about easy in the case of violin [7:34] playing or piano playing or mountain climbing  or something like that. Well, you know, I don't [7:40] personally feel bad if I don't play the violin  well, because I don't play the violin at all. [7:46] But the work that we're in is conceptual  work. So when we talked, start talking about [7:50] something being outside of our capability  well, you know, it really starts trampling [7:55] on our, our egos in a big way. And so,  you know, due to a combination of hubris [8:02] and insecurity we never really talk about whether  or not something is outside of our capabilities. [8:07] It ends up that it's not so embarrassing after  all, because we don't have tremendously divergent [8:15] abilities in that area. The last  thing I want to say about easy [8:20] and the critical thing to distinguish  it from simple is that easy is relative, [8:25] right? Playing the violin and reading  German are really hard for me. [8:30] They're easy for other people, certain other  people. So unlike simple, where we can go and [8:35] look for interleaving, look for braiding. Easy  is always going to be, you know, easy for whom [8:41] or hard for whom. It's a relative term. The  fact that we throw these things around sort [8:46] of casually saying, oh, I like to use  that technology because it's simple. [8:49] And when I'm saying simple, I mean easy. And  when I'm saying easy, I mean, because I already [8:53] know something that looks very much a like,  that is how this whole thing degrades, and we [8:58] can never have an objective discussion about the  qualities that matter to us in our software. So [9:07] what's one critical area where we, where  we have to distinguish these two things and [9:14] and look at them from a perspective  of them being easy and being simple? [9:19] It is, it has to do with constructs and artifacts, [9:22] right? We program with constructs, we have  programming languages, we use particular libraries [9:27] and those things in and of themselves when we  look at them, like when we look at the code, [9:32] we've right have certain characteristics in and  of themselves, but we're in a business of art. [9:39] Right. We don't ship source code and the user  doesn't look at our source code and say, okay, oh, [9:44] that's so pleasant, right? No, they run  our software and they run it for a long [9:50] period of time. And over time we keep  glomming more stuff on our software, [9:56] all that stuff, stuff, the running  of it. The performance of it, [10:00] the ability to change it all is an attribute  of the artifact, not the original construct. [10:07] But again, here, we still focus so much on our  experience of the use of the construct. Look, [10:14] I only had to type 16 characters. Wow. That's  great. No semicolon or things like that. [10:21] This whole notion of sort of  programmer convenience. Again, [10:24] we are infatuated with it.  Not, not to our benefit. [10:29] On the flip side, it gets even worse. Our  employers are also infatuated with it, right. [10:35] Those first two meanings of easy. What do they  mean. Right. If I can get another programmer in [10:41] here. Right. And they look at your source code and  they think it's familiar. Right. And they already [10:51] know the toolkit, right. So it's near, at hand.  They've always had the same tool in their toolkit. [10:55] They can read it. I can replace you.  It's a breeze, especially if I ignore [11:00] the third notion of easy. Which is whether  or not anybody could understand your code. [11:05] Right. Because they don't actually care about  that. They just care if somebody can go sit in [11:08] your seat, start typing. So again, as sort  of a business owners this sort of again, [11:16] the same kind of focus on those first two aspects  of easy, cause it makes programmers replaceable. [11:21] So we're going to see, we're going to contrast  this with the impacts of long-term use. [11:26] Right. What does it mean to use this  long-term and uh, yeah, what what's there, [11:33] what's there as all the meat, right? Does  the software do what it's supposed to do? [11:39] Is it of high quality? Can we rely  on it doing what it's supposed to do? [11:43] Can we fix problems when they arise?  And if we're given a new requirement, [11:48] can we change it? These things have nothing  to do with the construct as we typed it in [11:54] or very little to do with it and have a lot  to do with the attributes of the artifact. [12:01] We have to start assessing our  constructs based around the artifacts, [12:06] not around the look and feel of the experience  of typing it in or the cultural aspects of that. [12:13] So let's talk a little about bit  about limits. Oh, look, it does move. [12:19] This is just supposed to sort of lull you into  this state where everything I say seems true [12:27] because I can't use monads to do that. [12:35] This stuff is pretty simple logic, right? How  can we possibly make things that are reliable [12:40] that we don't understand? Right. It's very, very  difficult. I think Professor Sussman made a great [12:48] point saying there's going to be this trade-off  right. As we make things more flexible and [12:53] extensible and dynamic in some possible futures  for some kinds of systems we are going to make [12:58] a trade-off in our ability to, to understand their  their behavior and make sure that they're correct. [13:06] But for the things that we want to understand and  make sure are correct, we're going to be limited. [13:12] We're going to be limited in our understanding  and our understanding is very limited, [13:15] right? There's the whole notion of, you  know, how many, how many balls can you [13:18] keep in the air at the time? Or how many  things can you keep in mind at a time? [13:21] It's a, it's a limited number and it's a very  small number . Right. So we can only consider [13:25] a few things. And when things are intertwined  together, we lose the ability to take them in [13:30] isolation. So if every time I think I pull out  a new part of the software I need to comprehend [13:36] and it's attached to another thing I had to  pull that other thing into my mind because I [13:40] can't think about the one without the other.  That's the nature of them being intertwined. [13:44] So every intertwining is adding this burden and  the burden is kind of combinatorial as to the [13:52] number of things that we can, we can consider. So  fundamentally this complexity and by complexity, [13:57] I mean this braiding together of things is  going to limit our ability to understand our [14:04] So so how do we change our software? Apparently  I heard in the talk today that agile and extreme [14:14] programming have shown that refactoring and  tests allow us to make a change with zero. [14:23] I never knew that. I still do not know that [14:27] because that's not actually a  knowable thing. It's, that's phooey, [14:36] If you're going to change software,  you're going to need to analyze [14:38] what it does and make decisions about  what it ought to do. You know, I mean, [14:42] at least you're going to have to go and say,  what is the impact of this potential change, [14:46] right. And what parts of the software do  I need to go to, to affect the change? [14:54] And you know, I don't, I don't care if we're using  XP or Agile or anything else you're not going to [14:59] get around the fact that if you can't reason about  your program, you can't make these decisions. [15:06] But I do want to make clear here because a lot  of people, as soon as they hear the word reason [15:09] about they're like, oh my God, are you saying  that you have to be able to prove programs? [15:13] I am not. I don't believe in that.  I don't think that's an objective. [15:16] I'm just talking about informal reasoning,  the same kind of reasoning we use every day to [15:20] decide what we're going to do. We do not take out  category theory and say, you know, we, we actually [15:27] can reason without it. Thank goodness.  So what about, what about the other side? [15:33] Right? There's two things you do with the future  of your software. One is you add new capabilities. [15:37] The other thing is you fix the ones you didn't  get, you know, so, you know, done so well. And [15:43] I like to ask this question. What's true of  every bug found in the field? Got written. [15:50] It got written. Yes. What's a  more interesting fact about it. [15:55] It passed the type checker. What else did it do?  It passed all the tests. Okay. So now what do [16:05] you do? Right. I think we're in this world. I'd  like to call it guard rail programming. Right. [16:15] It's really, really sad. We're like,  I can make change cause I have tests. [16:19] Right? Who does that? Who drives their  car around banging against the door? [16:25] I'm glad I've got these guard rails because  I'd never make it to to the show on time. [16:32] Right. And, and do the guard rails  help you get to where you want to go? [16:39] Like the guard rails, like guide you places. No, [16:42] those guard rails everywhere. They don't, they  don't point your car in any particular direction. [16:48] So again, we're going to need to be able to  think about our program. It's going to be [16:52] critical. All of our guard rails will fall. We'll  have failed us. We're going to have this problem. [16:56] We're going to need to be able to reason about our  program say, well, you know what? I, because maybe [17:02] if it's not too complex, I'll be able to say,  I know through ordinary logic it couldn't be in [17:08] this part of the program. It must be in that part.  And let me go look there first, things like that. [17:14] Now, of course, everybody's going to start  moaning, but I have all this speed. I'm agile [17:19] and fast. You know, this easy stuff is making  my life good cause I have a lot of speed. [17:27] So what kind of runner can run as fast as they  possibly can from the very start of a race, right? [17:36] Only somebody who runs really short races. Okay. [17:42] But of course we are programmers and we're smarter  than runners apparently because we know how to fix [17:49] that problem. Right. We just fire the starting  pistol, every a hundred and call it a new sprint. [18:03] I don't know why they haven't figured that out,  but right. It's my contention based on experience [18:11] that if you ignore complexity, you will slow down.  You will invariably slow down over the long haul. [18:18] Of course, if you are doing something that's  really short term, you don't need any of this. [18:22] You could write it, you know, with ones and  zero's and this is my really scientific graph. [18:28] You notice how none of the axes are, there's no  numbers on it. Cause I just, I completely made [18:33] it up. It's a, it's an experiential graph.  And what it shows is if you focus on ease [18:42] and ignore simplicity. So I'm not saying you  can't try to do both. But if you focus on ease [18:48] you will be able to go as fast as possible from  the beginning of the race, but no matter what [18:52] technology you use or sprints or firing pistols or  whatever the complexity will eventually kill you. [18:58] It will kill you in a way that will  make every sprint accomplish less. [19:01] Most sprints be about completely  redoing things you've already done. [19:05] And the net effect is you're not moving forward  in any significant way. Now, if you start by [19:10] focusing on simplicity, why can't you go as  fast as possible, right at the beginning? [19:15] Okay. Right. Cause some, some tools that are  simple are actually as easy to use as some tools [19:20] that are not, why can't you go as fast, then  you have to think you have to actually apply [19:27] some simplicity, work to the problem before you  start. And that's going to be maybe this ramp up. [19:35] So one of the problems I think we have is  this conundrum that some things that are easy [19:40] actually are, are complex. [19:43] So let's look. There are a bunch of constructs  that have complex artifacts that are very [19:50] succinctly described, right? Some of the things  that are really dangerous to use are like so [19:54] simple to describe they're incredibly familiar,  right? If you're coming from object orientation, [19:59] you're familiar with a lot of complex  things they're very much available, right. [20:06] And they're easy to use. In fact, by all measures, [20:09] conventional measures, you would  look at them and say, this is easy. [20:14] Right. But we don't care about that. Right.  Again, the user's not looking at our software [20:18] and they don't actually care very much about  how good a time we had when we were writing it. [20:24] Right. What they care about is what the  program does and the, and if it works well, [20:29] it will, it will be related to whether  or not the output of those constructs [20:34] were simple. In other words, what,  what complexity do they yield? [20:38] When there is complexity there? We're  going to call that incidental complaint. [20:43] Right. It wasn't part of what the user asked us  to do. We chose the tool, it had some inherent [20:48] complexity in it. It's incidental to the  problem. I didn't put the definition in here. [20:53] But incidental is Latin for "your fault". [21:02] and it is, I think you really  have to ask yourself, you know, [21:05] are you programming with a loom?  You know, you're having a great [21:09] time. You're throwing that shuttle back and  forth and what's coming out the other side [21:12] is this knotted, you know, mess. I mean, it may  look pretty, but you have this problem, right? [21:20] Where's the problem. The problem is the knitted  castle, right? Do you want a knitted castle? [21:31] What benefits do we do we get from  simplicity? We get ease of understanding, [21:35] right? That's sort of definitional. I contend we  get ease of change and easier debugging. Other [21:43] benefits that come out of it that are sort of on  the secondary level are increased flexibility. [21:48] And when we talk more about modularity and  breaking things apart, we'll see where they, [21:51] where that falls like the ability to [21:57] change policies or move things around. Right. As  we make things simpler, we get more independence [22:03] of decisions because they're not interleaved. So  I can make a location decision that's orthogonal [22:09] from a like a performance decision. And I really  do want to make, you know, ask the question [22:16] agilist or whatever it is is having a test  suite and refactoring tools going to make [22:24] changing the knitted castle faster than changing  the Lego castle. No way, completely unrelated. [22:34] Okay. So how do we make things easy? [22:37] Presumably, you know, the objective here is  not to just bemoan the software crisis. Right? [22:42] So what can we do to make things  easier? So we'll look at those [22:44] parts, those aspects of being easy.  Again, there's a location aspect, [22:48] making something at hand, putting it in  our tool kit. That's relatively simple. [22:53] Right? We just install it. Right. Maybe  it's a little bit harder because we have [22:57] to get somebody to say, it's okay to use it. Then  there's the aspect of how do I make it familiar? [23:03] Right. I may not have ever seen this  before. That's a learning exercise. [23:06] I've got to, you'll get a book, go take a  tutorial, have somebody to explain it to me. [23:10] Maybe try it out. Both these things are we're  driving, we're driving. We install, we learn [23:16] where it's totally in our hands. Then we have  this other part though. Right. Which is the mental [23:23] capability part. And does the part, that's always  hard to talk about the mental capability part. [23:28] Maybe because the fact is  we can learn more things. [23:33] We actually can't get much smarter. We're  not going to move. We're not going to move [23:38] our brain closer to the complexity. We have  to make things near by simplifying them. [23:45] The, but the truth here is not that  that's like there are these super, [23:47] super you know, bright people who can do these  amazing things and, and everybody else is stuck. [23:54] Because the juggling analogy is pretty  close, right? The average juggler can do [23:59] three balls. The most amazing juggler in  the world can do like nine balls or 12 or [24:05] something like that. They can't do 20 or a  hundred. Right? All, we're all very limited, [24:12] you know, compared to the complexity  we can create, we're all, you know, [24:16] statistically at the same point in our ability  to understand it, which is not very good. [24:21] So we're going to have to bring things towards  us and because we can only juggle so many balls, [24:26] you have to make a decision.  How many of those balls [24:30] do you want to be incidental complexity and  how many do you want to be? Right. How many [24:35] extra balls you won't have somebody throwing your  balls that you have to try to incorporate in here? [24:39] Oh, use this tool. And you're like, whoa. You  know, more, more stuff who wants to do that? [24:46] All right. So let's look  at a, let's look at a fact. [24:54] So I've been on the other side of this  complaint and I, I like it. We can look [24:59] at it really quickly. Only because it, it's  not, this analysis has nothing to do with the [25:05] usage. This complexity analysis is just about the  programmer experience, right. So parens are hard, [25:12] right? They're not at hand for most  people who haven't otherwise used it. [25:16] And what does that mean? It means that like,  they don't have an editor that knows how to do, [25:22] you know, paren matching or move stuff around  structurally, or they have one and they've never [25:26] loaded the mode that makes that happen. Totally  given. Right. It's. Nor is it familiar? I mean, [25:32] everybody's seen parentheses, but they  haven't seen them on that side of the method! [25:38] I mean, it's just crazy, [25:50] but you know, I think this it's,  this is your responsibility, [25:54] right? To fix these two things  as a user, as a potential user, [25:57] you got to do this. But we could dig deeper. Let's  look at the third thing. Did you actually give me [26:02] something that was simple. Is a language built  all out of parens simple in the case I'm saying? [26:09] Is it free of interleaving and braiding? And  the answer is no right. CommonLisp and Scheme [26:17] are not simple in this sense, in their use of  parens because the use of parentheses in those [26:23] languages is overloaded, right? Parens wrap calls,  they wrap grouping, they wrap data structures, [26:33] right? That overloading is a form of complexity  by the definition of, you know, I gave you, right. [26:39] And so if you actually bothered to get  your editor set up and learn that the [26:43] parenthesis goes on the other side of the  verb this was still a valid complaint. Now, [26:50] of course, everybody was saying easy. It's  hard. It's complex in that we're using these [26:54] words really weekly. Right. But it was hard  for a couple of reasons you could solve. [26:59] And it was not simple for a reason that  was the fault of the language designer, [27:03] which was that there was overloading. Right. And  we can fix that. Right. Just add another data [27:08] structure. It doesn't make lists, not lisp to have  more data structures, right. It's still a language [27:13] defined in terms of its own data structures, but  having more data structures in play means that [27:18] we can get rid of this overloading in this  case, which then makes it your fault again. [27:25] Right. Because now this, the  simplicity is back in the construct. [27:28] And it's just a familiarity thing,  which you can solve for yourself. [27:35] Okay. This is an old dig at list programmers.  I'm not totally sure what the what he was talking [27:45] about. I believe it was a performance related  thing that lispers would just, they cons'ed up [27:49] all this memory and they did all this evaluation  and it was, it was a pig. LISP programs at that [27:54] time were, were LISP programs at that time  were complete pigs relative to the hardware. [27:59] So they, you know, they knew the value of  all these constructs, right? This dynamism, [28:02] dynamic nature, these things are all great.  They are valuable. Right. But there was this [28:07] performance cost. I'd like to lift this whole  phrase and apply it to all of us right now. [28:13] Right. As programmers, we are  looking at all kinds of things. [28:17] And I just see it, you know, read hacker news  or whatever. It's like, oh, look, this thing has [28:21] this benefit. Oh, great. I'm going to do that.  Oh, but this has this benefit. Oh, that's cool. [28:26] Oh, that's awesome. You know, that's shorter.  You never see in these discussions, was there a [28:33] trade off? Is there any downside, you know, is  there anything bad that comes along with this? [28:37] Never nothing. It's just like, we look all for  benefits, right? So as programmers now, I think [28:42] we're, we're looking all for benefits and we're  not looking carefully enough at the byproducts. [28:49] So what's in your toolkit. I have  you know, I have these two columns, [28:57] one says complexity, and one says, simplicity,  the simplicity column just being simpler. [29:04] It doesn't mean that the things  over there are purely simple. [29:09] I didn't label these things bad and  good. I'm leaving your minds to do that. [29:21] So what, what things are complex and what are  the simple replacements. I'm going to dig into [29:25] the details on these. So I won't actually  explain why they're complex. We're going [29:27] to say state and objects are complex and values  are simple and can replace them. In many cases, [29:35] I'm going to say methods are complex and  functions are simple and namespaces are simple. [29:40] And the reason why methods are there because often  the, the space of methods the class or whatever [29:47] is also a mini very poor namespace. vars  are complex, and variables are complex. [29:57] Managed references are also  complex, but they're still simpler. [30:02] Inheritance, switch statements, pattern matching  are all complex and polymorphism ala carte is [30:10] simple. Okay. Now remember the meaning of simple,  meaning of simple means unentangled. Right. [30:19] Not twisted together with something else. It  doesn't mean I already know what it means. Right? [30:23] Simple. Does not mean "I already know what it  means." Okay. Syntax is complex. Data is simple. [30:33] Imperative loops, fold, even  which seems kind of higher level [30:37] still has some implications  that tie two things together. [30:40] Whereas set functions are simpler.  Actors are complex and cues are simpler. [30:47] ORM is complex and declarative  data manipulation is simpler. [30:52] Okay. Even Eric said that in his talk,  he said it really fast near the end. [31:00] "Oh yeah. And eventual consistency  is really hard for programs." [31:07] Conditionals are complex in interesting ways and  rules can be, can be simpler. And inconsistency [31:16] is very complex. It's almost definitionally  complex, right? Because consistent means [31:20] to stand together. So inconsistent means to  stand apart. And that means taking a set of [31:25] things that are standing apart and trying  to think about them all at the same time. [31:28] It's inherently complex to do that. And anybody  who's tried to use a system that's eventually [31:32] consistent knows that. Okay. So there's this  really cool word called complect. I found it. [31:43] I love it. It means to  interleave or entwine or braid. [31:47] Okay. I want to start talking about what  we do to our software that makes it bad. [31:52] I don't want to say braid or entwine  because it doesn't really have the good, [31:56] bad connotation that complect has.  Complect is obviously bad. Right? [32:02] It happens to be an archaic word, but you  know, there's no rules that say you can't [32:05] start using them again. So I'm going  to use them for the rest of the talk. [32:09] So what do you know about  "complect"? It's bad. Don't do it. [32:14] Right. This is where complexity comes from.  Right? Complecting. That's very simple. [32:24] Right? And in particular, it's something  that you want to avoid in the first place. [32:28] Right? Look at this diagram. Look at  the first one. Look at the last one, [32:33] right? It's the same stuff in both those diagrams,  the exact it's the same strips. What happened? [32:40] They got complected. And now it's hard to  understand the bottom diagram from the top [32:47] one, but it's the same stuff.  You're doing this all the time. [32:51] You can make a program, a hundred different ways. [32:53] Some of them it's just hanging there. It's  all straight. You look at it. You say, oh, [32:57] I see it's four lines this program, right. Then  you could type in four lines in another language [33:01] or with a different construct. And you end up with  this knot. So you've got to take care of that. [33:08] So "complect" actually means to braid together.  And "compose" means to place together. [33:16] And we know that right. Everybody keeps telling  us what we want to do is make composable systems. [33:19] We just want to place things together, which  is great. And I think there's no disagreement, [33:25] right? Composing simple components. Simple in that  same respect is the way we write robust software. [33:34] So it's simple. Right? All we need to  do is (everybody knows this I'm up here [33:41] just telling you stuff you know), we can make  simple systems by making them modular. Right. [33:47] We're done. I'm like halfway through my talk.  I don't even know if I'm going to finish. [33:50] It's so simple, right? This is it. This is  the key. No, it's obviously not the key. [33:58] Right? Who has seen components that  have this kind of characteristic? [34:04] I'll raise my hand twice because not enough people  are raising their hands. It's ridiculous. Right? [34:07] What happens? You can write modular software  with all kinds of interconnections between them, [34:12] right? They may not call each other,  but they're completely complected. [34:18] Right. And we know how to solve this. It has  nothing to do with the fact that there are [34:24] two things it has to do with what those  two things are allowed to think about. [34:27] If you want to really anthropomorphize and what  do we want to make things allowed to think about? [34:33] And only these things, some abstractions, I  don't know if that's coming out that well, [34:39] that's a dashed white version  of the top of the LEGO, right? [34:43] That's all we want to limit things to. Cause now  the blue guy doesn't really know anything about [34:47] the yellow guy and the yellow guy doesn't  really know anything about the blue guy [34:50] and they both become simple. So  it's very, it's very important [34:55] that you don't associate simplicity with  partitioning and stratification, right? [34:58] They don't. Right. They are enabled  by it. If you make simple components, [35:03] you can horizontally separate them  and you can vertically stratify them. [35:08] Right. But you can also do that with complex  things and you're going to get no benefits. [35:14] And so I would encourage you to be particularly  careful not to be fooled by code organization. [35:20] Right. There's tons of libraries that look,  oh, look, there's different classes. They're [35:25] separate classes. They, you know, they call  each other and sort of these nice ways, [35:29] right. Then you get out in  the field and you're like, [35:30] oh my God, this thing presumes, that, that  thing never returns to number 17. What is that? [35:39] Okay. I'm not going to get up here and tell  you, state is awesome. I like state. I'm not [35:45] a functional whatever guy, whenever I'm going  to say instead I did this and it sucks. Right. [35:52] I did years and years, C++ you know, He-Man  stateful programming. It's, it's really not fun. [36:01] It's not good. It's, it's never simple. Having  state in your program is never simple, right? [36:08] Because it has a fundamental complecting that  goes on in its artifacts, right? It complects [36:13] value in time. You don't have the  ability to get a value independent of, [36:18] of time, and sometimes not an ability to get  a value in any proper sense at all. But again, [36:25] it's a great example. This is  easy. It's totally familiar. [36:28] It's at hand, it's an, all the programming  languages. This is so easy. This complexity [36:34] is so easy. Okay. And you can't get  rid of it, everything. So I'll have I [36:40] have modularity. And that, that assignment  statement is inside a method, right? Well, [36:44] if every time you call that method with the  same arguments, you can get a different result. [36:48] Guess what happened? That complexity. It  just leaked right out of there. It doesn't [36:52] matter that you can't see the variable,  right? If the thing that's wrapping it is [36:56] stateful. And the thing that's wrapping  that is still stateful. In other words, [36:58] by stateful, I mean, every time we ask it the  same question, you get a different answer. [37:02] You have this complexity and it's like  poison and it's like dropping, you know, [37:07] some, some dark liquid into this, to a vase. It's  just going to end up all over the place. The only [37:13] time you can really get rid of it is when you  put it inside something that's able to present [37:17] a functional interface on the outside, a true  functional interface, same input, same output. [37:23] You can't mitigate it through the  ordinary code organization things. [37:28] And note in particular, I didn't  talk about concurrency here. [37:31] This is not about concurrency. This has nothing  to do with concurrency. It's about your ability [37:37] to understand your program. Your program was  out there, it's single-threaded it didn't work. [37:41] All the tests passed. It, made it through  the type checker, figure out what happened, [37:46] right? If it's full of variables, where are  you going to need to try that? Recreate the [37:52] state that was happening at the client when  it went bad. Is that going to be easy? No. [38:02] But we fixed this, right? Your language, your  new shiny language has something called var [38:07] or maybe it has refs or references. [38:11] None of these constructs make state simple.  That's the first primary thing. I don't want [38:16] to say that even of Clojure's constructs,  they do not make state simple in the case [38:19] I'm talking about in the, in the  nature of simple I'm talking about [38:22] but they're not the same, right? They  all do warn you when you have state. [38:25] And that's great. Most people who are  using a language where immutability is [38:30] not the default, and you have to go out of  your way to get it, finds that the programs [38:34] they end up writing have dramatically like  orders of magnitude, less state than they [38:38] would otherwise. Cause they never needed  all the other state in the first place. [38:42] So that's really great. But I will call  out Clojure and Haskell's references as [38:48] being particularly superior in  dealing with this because they [38:52] compose values and time. They're  actually little constructs that do [38:56] two things. They have some abstraction over  time and the ability to extract the value. [39:03] That's really important. Because that's, that's  your path back to simplicity. I mean, if I have [39:09] a way to get out of this thing and get a value  out, I can continue with my program after I've [39:12] passed that variable to somebody else or  a reference to something that's going to [39:16] find the variable every time through the varying  thing, I'm poisoning the rest of my system. [39:21] So you know, look at the var in your  language and ask if it does the same, [39:25] the same thing. All right, let's see why  things are complex. State. We already [39:31] talked about it complects everything  it touches. objects, complect state, [39:37] identity and value. They mix these three things  up in a way that you cannot extricate the parts. [39:43] Methods complect function and state ordinarily,  right? In addition, in some languages they [39:50] complect namespaces right. Derive from two  things in Java, they have the same name, method. [39:57] It doesn't work. syntax, interestingly [40:01] complects meaning and order often  in a very unidirectional way. [40:06] Professor Sussman made the great point about data  versus syntax and it, you know, it's super true. [40:14] I don't care how much you really love the syntax  of your favorite language. It's inferior to data [40:19] in every way. Inheritance complects types,  right? So it says these two types are complected, [40:26] that's what it means. Inheritance.  Complecting. It's like it's, [40:30] it's definitional, right? Switching and matching. [40:34] They complect multiple pairs of  who's going to do something and [40:40] what happens. Right. And they do it all in  one place in a closed way. That's very bad. [40:48] Vars and variables, again, complect value  in time, often in an inextricable way, you [40:53] can't obtain a value. We saw a picture during a  keynote yesterday this amazing memory, right where [41:01] you could dereference uh, an address and get an  object out. I want to get one of those computers. [41:08] Right. Have you ever used one of those  computers? I can't get one. I called Apple [41:12] and they were like, no. The only thing you can  never get out of a memory address is a word, [41:16] a scalar, right? The thing that was all  derided, right? Recovering a composite [41:21] object from an address. It's not something  computers do. None of the ones that we have. [41:27] So variables have the same problem. You  can not recover a composite mutable thing [41:32] with, with one dereference. Loops and fold  loops are pretty obviously. Complecting what [41:40] you're doing and how to do it. Fold is a little  bit more subtle, right? Cause it seems like this [41:45] nice. Somebody else has taken care of it, but  it does have this implication about the order [41:49] of things this left to right bit. Actors complect  what's going to be done and who's going to do it. [42:00] Now professor Sussman said the, all these  talks have acronyms and I couldn't actually [42:06] modify my slides in time. So object relational  mapping has, oh my God complecting going on. [42:14] You can't even begin to talk  about how, how bad it is. [42:17] Right. And, and, and you know, if  you're going to do like, duals, right? [42:25] What's the dual of value, is it co-alue?  What's a co-value? It's an inconsistent. [42:35] Who wants that. And conditionals, I  think are interesting, right? This is, [42:38] this is sort of more cutting edge area. We  have a bunch of sort of rules about what [42:42] our program's supposed to do. It's  strewn all throughout the program. [42:48] Can we fix that? because that's complected with [42:51] the structure of the program and  the organization of the program. [42:54] All right. So if you take away two things  from this talk one would be the difference [43:02] between the words "simple" and "easy". The  other, I would hope would be the fact that [43:07] we can create precisely the same programs we're  creating right now with these tools of complexity [43:15] with dramatically drastically simpler tools. [43:19] Right. I did C++ for a long time, I did Java. I  did C#. I know how to make big systems in those [43:26] languages. And I completely. You do not need all  that complexity. You can write as sophisticated [43:33] a system with dramatically simpler tools, which  means you're gonna be focusing on the system, [43:38] what is supposed to do instead of all the guck  that falls out of the constructs you're using. [43:43] So I'd love to say the first step in getting a  simpler life is to just choose simpler stuff, [43:50] right? So if you want values, usually you  can get it. Most languages have something [43:53] like values final or, or val you know, lets  you like declare something as being immutable. [44:00] You do want to find some persistent collections. [44:02] Cause the harder thing in a lot of languages  is getting aggregates that are values, [44:09] right? You got to find a good library for that  or use a language where that's the default. [44:17] Functions. Most languages have them. Thank  goodness. If you don't know what they are, they're [44:21] like stateless methods. Namespaces is something  you really need the language to do for you. [44:27] And unfortunately, it's not done very  well in a lot of places. Data. Please, [44:33] programmers. We supposedly  write data processing programs. [44:36] There's all these programs, they don't  have any data in them. They have all these [44:40] constructs. We put around it and globbed on  top of data. Data is actually really simple. [44:45] There's not, there's not a tremendous number of [44:47] variations in the central nature of data,  right? There are maps. There are sets, there are [44:51] linear, sequential things. There's not a  lot of other conceptual categories of data. [44:57] We create hundreds of thousands of variations,  have nothing to do with the essence of the stuff [45:01] and make it hard to write programs that  manipulate the essence of the stuff. [45:05] We should just manipulate the essence of  the stuff it's not hard. It's simpler. Also [45:10] same thing for communications, right? Are we  all not glad we don't use the Unix method of [45:17] communicating on the web? Any arbitrary command  string can be the argument list for your program [45:25] and any arbitrary set of characters can come  out the other end. Let's all write parsers. [45:31] No, I mean, it's, it's a problem.  It's, it's a source of complexity, [45:35] right? So we can get rid of that. Just use  data. The biggest thing, I think the most [45:38] desirable thing, the most esoteric, this  is tough to get, but boy, when you have it, [45:43] your life is completely, totally different  thing is polymorphism ala carte, right? [45:49] Clojure protocols and Haskell type classes. And,  and constructs like that give you the ability [45:56] to independently say I have data structures.  I have definitions of sets of functions and I [46:03] can connect them together. And those  are three independent operations. [46:07] In other words the genericity is not tied to  anything in particular. It's available ala carte. [46:14] I don't know of a lot of library solutions for  languages that don't have it. Already talked about [46:17] managed references and how to get them. Maybe you  can use Clojure's from different Java languages. [46:23] Set functions. You can get from libraries.  Queues, you can get from libraries, [46:26] right? You don't need a  special communication language. [46:29] You can get declarative data manipulation  by using SQL or learning SQL finally, [46:37] or something like LINQ or something like  datalog. I think these last couple of things are [46:42] harder, right? We don't have a lot  of ways to do this. Well-integrated [46:46] with our languages. I think at currently,  LINQ is an effort to do that. Rules, right? [46:51] Declarative rule systems, instead of embedding  a bunch of conditionals in our raw language at [46:57] every point of decision, it's nice to sort of  gather that stuff and put it over someplace [47:02] else. And you can get rules systems in libraries,  or you can use languages like Prolog. If you want [47:07] consistency, you need to use  transactions and you need to use values. [47:12] Okay. There are reasons why you  might have to get off of this list, [47:16] but boy, there's no reason why you shouldn't start  with it. Okay. There's a source of complexity. [47:23] That's really difficult to deal with and not your  fault. Call it environmental complexity, right? [47:30] Our programs end up running on machines next to  other programs next to other parts of themselves. [47:35] And they contend, they contend for stuff,  right? Memory, CPU cycles, and things like that. [47:43] Everybody's contending for it. This is an inherent  complexity. Inherent is Latin for "not your fault" [47:51] in the implementation space. And  those is not part of the problem, [47:54] but it is part of the implementation, right? [47:56] You can't go back to the customer and say, [47:57] the thing you wanted is not good because I  have GC problems. But did you see problems [48:03] and stuff like that? They, they come into  play. There's not a lot of great solutions, [48:07] right? You can do segmentation. You can say,  this is your memory. This is your memory. [48:10] This is your memory. This is your CPU and your  CPU. But there's tremendous waste in that, [48:14] right? Because you pre allocate, you don't use  everything, you don't have sort of dynamic nature. [48:19] But the problem I think we're facing, and  it's not one for which I have a solution [48:23] at the moment, is that the policies  around this stuff don't compose. [48:28] Right. If everybody says I'll just  size my thread pool to be the number of [48:31] cores. Well, how many times  can you do that in one program. [48:36] Not a lot and have it still work out. Yeah.  So unfortunately a lot of things like that [48:43] splitting that stuff up and making an individual  decision is not actually making things simpler. [48:48] It's making things complex because  that's a decision that needs to be made [48:52] by someone who has better information. And I  don't think we have a lot of good sources for, [48:59] organizing those decisions in, in single places,  in our systems. This is a hugely long quote. [49:06] Basically it says programming is not about typing,  it's about thinking. So the next phase here, [49:18] I gotta move a little bit quicker is how do we  design simple things of our own? Right. So the [49:23] first part of making things simple is just to  choose constructs that have simple artifacts, [49:28] but we have to write our own constructs sometimes.  So how do we abstract for simplicity, right. [49:33] And abstract again, here's an actual definition,  not made up one, means to draw something away. [49:39] And in particular it means to draw away from  the physical nature of something. I do want to [49:44] distinguish this from the, sometimes people  use this term really grossly to just mean [49:49] hiding stuff. That is not what abstraction is,  and that's not going to help you in this space. [49:55] There's too you know, I can't totally explain how  this is done. It's really the job of designing. [50:01] But one approach you can take is just  to do who, what, when, where, why. Well, [50:05] you can just go through those things  and sort of look at everything you're [50:07] deciding to do and say, what  is the who aspect of this? [50:09] What is the, what aspect of it?  This can help you take stuff apart. [50:14] The other thing is to maintain the approach that  says, I don't know, I don't want to know. I once [50:20] said that so often during a C++ course I was  teaching that one of the students made me a shirt. [50:27] It was a Booch diagram. Cause we didn't have,  have a, whatever it is now, the unified one. [50:33] And every line just said that [50:35] that's what you want to do. You really just  don't want to know. All right. So what is, [50:39] what? What is the operations? You know, what,  what is, what is it, what we want to accomplish, [50:44] right. We're going to form abstractions by taking  functions or more particularly sets of functions [50:50] and giving them names. In particular you're going  to use whatever your language lets you use. Right? [50:55] So if you only have interfaces, you'll use  that if you have protocols or type classes, [51:02] you'll use those. So all those things are in  the category. The things you use to make sets [51:06] of functions that are going to be abstractions  and sets of specifications of functions. [51:11] The point I'd like to get across today  is just that they should be really small, [51:17] much smaller than what we typically  see. Java interfaces are huge. [51:21] And the reason why they are huge is just  because Java doesn't have union types. So [51:24] it's inconvenient to say this function takes, you  know, something that does this and that, and that [51:30] you have to make a, this  and that and that interface. [51:32] So we see these giant interfaces. And the thing  with those giant interfaces is that's a lot [51:36] harder to break up those programs. So you're gonna  represent them with your polymorphism constructs, [51:42] they're specifications, right? They're  not actually the implementations. [51:47] They should only use values and other  abstractions in their definitions. [51:51] So you're gonna define interfaces or whatever type  classes that only take interfaces and type classes [51:56] or values and return them. And the biggest problem  you have when you're doing this part of design is [52:02] if you complect this with how. Right. You can  complect it with how by jamming them together [52:06] and saying here's just a concrete function instead  of having an interface or here's a concrete class, [52:10] instead of having an interface, you can  also complect it with how more subtly [52:14] by having some implication of the  semantics of the function dictate how it [52:18] is done. And fold is an example of that. [52:25] Strictly separating what from how is the  key to making how somebody else's problem, [52:29] right? If you've done this really well, you can, [52:32] you can pawn off the work of how on somebody else.  You can say, database engine, you figure out how [52:36] to do this thing or a logic engine, you figure  out how to search for this. I don't need to know. [52:41] Who is about like data or entities. These  are the things that are abstractions are [52:44] going to be connected to eventually,  depending on how your technology works, [52:48] you want to build components up from sub  components in a sort of direct injection style, [52:53] right? You don't want to like  hardwire what the sub-components are. [52:55] You want to as much as possible, take them as  arguments because that's going to give you more [53:00] programmatic flexibility in how you build. [53:04] You should have probably many more sub-components  than you have. So you want really much smaller [53:08] interfaces than you have and more sub-components  than you probably are typically having, because [53:12] usually you have none and then maybe you have one  when you decide, do I need to farm out policy? [53:16] Yes. If you go in saying this is a job and I've  done, who, what, when, where, why? And I found [53:20] five components, don't feel bad. That's  great. You're winning massively by doing that, [53:27] you know, split out policy and stuff like  that. And the thing that you have to be [53:32] aware of when you're building, you know, the  definition of a thing from sub-components [53:35] is any, any of those kinds of, you know,  red, yellow, thinking about blue, blue, [53:39] thinking about yellow, kind of hidden detail  dependencies. So you want to avoid that. [53:46] How things happen. This is the actual  implementation code, the work of, [53:49] of doing the job. You, you strictly  want to connect these things together, [53:53] using those polymorphism constructs. That's the  most powerful thing. Yeah you can use a switch [53:58] statement. You could use pattern matching,  but it's glomming all this stuff together. [54:01] If you use one of these systems, you have an open  polymorphism policy, and that is really powerful, [54:08] especially if it's runtime open, but even if it's  not, it's better than nothing. And again, bewware [54:16] of obstructions that dictate how in some subtle  way, because when you do that, you're really, [54:21] you're nailing the person down the line who has to  do the implementation, you're tying their hands. [54:26] So the more declarative things are the better,  the better things work. And the thing that [54:31] I mean, how is sort of the bottom, right?  Don't mix this up with anything else. All [54:35] these implementations should be islands as much as  possible. When and where, this is pretty simple. [54:42] I think you just have to strenuously,  avoid comparing this with anything. [54:47] I see it accidentally coming in, mostly when  people design systems with directly connected [54:53] objects. Right? So if you know, the, if  your program is architected such that, [54:57] you know, this thing deals with the  input, and then this thing has to do [55:01] the next part of the job. Well, if, if thing  A calls thing B you just complected it, right? [55:09] And now you have a when and where thing, cause now [55:12] A has to know where B is in order to call B.  And when that happens is whenever A, does it [55:19] right? Stick a queue in there, right? Queues are  the way to just get rid of this problem. If you're [55:24] not using queues extensively, you should be, you  should right away, like right after this talk. [55:32] And then this is the why part, this  is sort of the policy and rules. [55:35] This is this is, I think this is hard for  us. We typically put this stuff all over [55:40] our application. And if you ever have to talk  to a customer about what the application does, [55:45] it's really difficult to sit with  them in source code and look at it. [55:48] Now, if you have one of these pretend testing  systems that lets you write English strings [55:52] so the customer can look at that. That's just  silly. Right? You should have code that does [55:56] the work that somebody can look at, which means  to try to, you know put this stuff someplace [56:02] outside, try to find a declarative system or a  rule system which will let you you do this work. [56:07] Finally in this area information, it is simple, [56:11] right? The only thing you can possibly do with  information is ruin it. Right? Don't do it. Right. [56:19] Don't do this stuff. We, I mean, we got it.  Objects, objects just made to like encapsulate [56:23] IO devices. So there's a screen, but I can't like  touch the screen, so I have an object, right? [56:28] There's a mouse. I can't touch the mouse's object.  Right. That's all they're good for. They were [56:32] never supposed be applied to information and we  apply them to information. That's it's just wrong. [56:38] It's wrong. But I can now say it's wrong for a  reason, right? It's wrong. Cause it's complex. [56:45] In particular, it, it ruins your ability  to build generic data manipulation things. [56:49] If you leave data alone, right. You can build  things once that manipulate data and you can [56:56] reuse them all over the place and you know,  they're right once and you're done. Yeah. The [57:00] other thing about it, which also applies to ORM  is that it will tie your logic to representational [57:04] things, which again, tying complexing,  intertwining, so represent data as data. [57:10] Please start using maps and sets  directly. Don't feel like I have [57:14] to write a class now because I have a new  piece of information. That's just silly. [57:21] So the final aspect, right? So  we we've, we choose simple tools. [57:25] We write simple stuff and then sometimes we have  to simplify other people's stuff. In particular, [57:30] we, we may have to simplify the problem  space or some code that somebody else wrote. [57:35] This is a whole separate talk. I'm  not going to get into right now. [57:39] But the job is essentially one of  disentangling. Right. We know what's, [57:43] what's complex. It's entangled. So what do we need  to do? Right. We need to somehow disentangle it. [57:49] Right? You're going to get this. You're going to  need to first sort of figure out where it's going. [57:57] You're going to have to follow stuff around and  eventually label everything. Right? This is the [58:02] start. This is roughly what the process  is like. But again, it's a whole separate [58:06] talk to try to talk about simplification. All  right, I'm going to wrap up a couple of slides. [58:13] The bottom line is simplicity is a choice. It's  your fault if you don't have a simple system. [58:19] And, and I think we have a culture  of complexity, to the extent we all [58:23] continue to use these tools that have complex  outputs. We're just in a rut. We're just [58:28] self-reinforcing. And we have to get  out of that rut. But again, like I said, [58:32] if you're already saying, I know this, I  believe you, I already use something better. [58:35] I've already used that whole right  column then hopefully this talk will [58:38] give you the basis for talking with  somebody else who doesn't believe. [58:42] Right. Talk about simplicity versus  complexity, right? But it is a choice, [58:47] right? It requires constant vigilance. We already  saw the guard rails don't yield simplicity. [58:52] They don't really help us here. Right.  It requires sensibilities and care. [58:57] Your sensibilities about simplicity being equal  to ease of use are wrong. They're just simply [59:02] wrong. Right. We saw the definitions of simple  and easy. They're completely different things, [59:07] right? So easy is not simple. You have to start  developing sensibilities around entanglement. [59:14] That's what you have to just, you  have to have entanglement radar, [59:17] right? You want to look at some software  and say, ah, you know, not that I don't [59:20] like the names you used or the shape of the  code, or there was a semi-colon. I mean, [59:24] that's also important too, but you  want to start seeing complecting. [59:28] You want to start seeing interconnections  between things that could be independent. [59:32] That's where you're going to get the most  power. All the reliability tools you have, [59:37] right. Since they're not about  simplicity, they're all secondary. [59:42] Right. They just do not touch  the core of this problem. [59:47] Right. They're safety nets, but they're nothing  more than that. So how do we make simplicity easy? [59:56] Right. We're going to choose  constructs with simpler [60:02] artifacts, right? And avoid constructs  that have complex artifacts. [60:07] It's the artifacts. It's not the authoring. As  soon as you get in an argument with somebody [60:12] about, oh, we should, we should be using  whatever, get that sorted out, you know, [60:17] because however they feel about the shape of  the code, they type in is independent from this. [60:22] And this is the thing you have to live with. [60:25] We're going to try to create abstractions  that have simplicity as a basis, [60:28] right? We're going to spend a little time up  front simplifying things before we get started [60:36] and recognize that when you simplify things,  you often end up with more things. Right. [60:42] Simplicity is not about counting, right? I'd  have rather have more things hanging nice, [60:47] straight down, not twisted together then  just a couple of things tied in a knot. [60:53] And the beautiful thing about making them separate  is you'll have a lot more ability to change it [60:58] which is where I think the benefits lie. [61:00] So I think this is a big deal and I hope  everybody's able to bring it into practice or [61:09] use this as a tool for convincing somebody else  to do that. So I'll leave you with this. This [61:15] is what you say when somebody tries to sell  you a sophisticated type system. Thank you.