✨ What is this post about: As a part of my professional growth, I make time to watch tech talks. Previously, I'd just watch them but now I take and publish notes for future reference.
✨ Talk: "Simple Made Easy" by Rich Hickey
✨ One-paragraph summary: 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.
✨ Impression: I loved this talk. Plenty of food for thought, both with regards to my engineering work and Developer Relations. Even though the talk was delivered over a decade ago, most points are still very much relevant - and many discussions are still ongoing. That is a testament to how well-planned this talk was, but also to that some themes in the tech community discourse are universal.
Introduction
- "We need to build simple systems if we want to build good systems."
- Word origins
- simple - roots are 'sim' and 'plex', which means 'one fold' or 'one braid'
- complex - at roots, it means 'braided together'
- easy - at roots, it means 'lie near' (adjacent)
- hard - at roots, means 'strong'
"Simple"
- Simple things are like one fold: they have one role, one task, one objective, one concept; it is focused
- There might be many instances of the same simple thing - and it remains simple as long as these instances are not mingled
- The simplicity can be objectively stated
- my side note: simple is a language of fairy tales
"Easy"
- Easy things are near, at hand, easy to obtain, they are at reach; in software, that would be an easy reach like on our hard drive, tool set, IDE
- Easy things are near to our understanding, our skillset, they are familiar
- my side note: easy is Swedish because to me it looks like English and German
- Easy things are near our capabilities
- Easy is relative, it depends on the context an individual has
Construct vs Artifact
- Construct: "We program with constructs. We have programming languages. We use particular libraries, and those things, in and of themselves, when we look at them, like when we look at the code we write, have certain characteristics in and of themselves."
- Artifacts: "But we're in a business of artifacts. We don't ship source code, and the user doesn't look at our source code and say, "Ah, that's so pleasant." They run our software, and they run it for a long period of time. (...) All of that stuff, the running of it, the performance of it, the ability to change it all is an attribute of the artifact, not the original construct."
- We focus too much on the construct (the programming languages)
- Even though the users are infatuated with the artifact (say, the UI), the developers focus on the construct (the library)
- Our employers are infatuated with the "easiness" of the contruct, too - it's "easy" to replace a programmer and this programmer will know how to move around the codebase and use the tools because they are "easy" - near their context and familiar (though it's not necessarily easy in the sense of whether the person has the capabilities to actually code in this codebase)
- We should focus on the long-term results of the use of the artifact
- Does the software do what it's supposed to do?
- Is it of high quality?
- Can we rely on it doing what it's supposed to do?
- Can we fix problems when they arise?
- And if we're given a new requirement, can we change it?
- Conclusion: We must assess constructs by their artifacts
- my side note: this is a really interesting framing; to me, it's really helpful because I have been thinking a lot about how much talk there is about some libraries (say, React) and so little talk about how inaccessible the resulting UI is
Working with limitations
- Only the things we can understand we can make reliable
- The more extensible, flexible, and dynamic stuff is, the more tradeoff there is in our ability to understand it
- We can only consider a few things at a time; this is a limited number
- my side note: some research suggests that we can't focus on more than one thing at a time and other that we can keep track of limited number of things, like 2-4, at the same time
- "If things are intertwined together, we lose the the ability to take them in isolation."
- Every intertwining is adding more burden - and the intertwining (braiding things together) is going to limit our ability to understand systems
- Conclusion: complexity undermines understanding
Changing things
- "What is the impact of this potential change?": if you're going to change software, you're going to need to analyze what it does and make decisions about what it ought to do
- "And what parts of the software do I need to go to to effect the change?": if you can't reason about your program, you can't make these decisions without fear
- Once we have software, we do two things: add capabilities, and debug what doesn't work
- Good joke about debugging at 15:43 mark
- Tests are useless if we can't understand our program; they may eventually fail us
- Another good joke, this time about agile, at 17:27, and I'm going to quote it here: > What kind of runner can run as fast as they possibly can from the very start of a race? Sprinter, only somebody who runs really short races, okay? But of course, we are programmers, and we are smarter than runners, apparently, because we know how to fix that problem, right? We just fire the starting pistol every hundred yards and call it a new sprint.
- If you ignore the complexity, you will slow down over the long haul
- if you focus on ease, you will be able to go as fast as possible from the beginning of the race but the complexity will eventually get you, and you will accomplish less with every next sprint, and you will end up redoing things you've already done, "and the net effect is you're not moving forward in any significant way."
Easy but complex
- Some things that are easy are actually complex
- they are succinctly described
- they are familiar
- they are available
- easy to use
- Users don't care about the underlying complexity, they care about what the program does
- Is the outcome of your work drowning in complexity?
- The benefits of simplicity:
- ease of understanding
- ease of change
- ease of debugging
- increased flexibility (change things around) - modularity
- Is it easier to change a knitted castle or a LEGO castle?
Making Things Easy
- Make it reachable - easy to install, easy to approve
- Make it familiar - it's a learning exercise
- Make it simple so it's easy to understand, we are limited in our ability to understand complexity
- Good point about parens in Clojure: there are some things you can solve (get more familiar, start using the tool) and some that you cannot (the tool is complex)
- Developers know the value of everything and the cost of nothing (rephrased from: "LISP programmers know the value of everything and the cost of nothing" from Alan Perlis) - we talk a lot about benefits but not the tradeoffs
- Avoid complexity (don't complect/braid together)
- Instead, compose (place together)
- composing simple components is the key to robust systems
- it's not only about modularization
- State is never simple but it is easy (familiar, at hand)
- You don't need all this complexity. You can make a sophisticated system with simple tools - you can focus on the system, what it's supposed to do, instead of the constructs.
- If a decision needs to be made by a person who has better context, that system is not simple.
Main points of complexity
Construct | Complects |
---|---|
State | Everything that touches it |
Objects | State, identity, value |
Methods | Function and state, namespaces |
Syntax | Meaning, order |
Inheritance | Types |
Switching/matching | Multiple who/what pairs |
variables | value, time |
Impreative loops | what/how |
Actors | what/how |
ORM | everything |
Conditionals | why, structure of the program |
These can be replaced with the following simpler stuff:
Construct | Get it via... |
---|---|
Values | final, persistent collections |
Functions | stateless methods |
Namespaces | language support |
Data | JSON, maps, sets, arrays, xml |
Polymorphism a la carte | Protocols, type classes |
Managed refs | languages like Clojure |
Set functions | libraries |
Queues | libraries |
Declarative data manipulation | SQL |
Rules (declarative system of rules) | libraries |
Consistency | Transactions, values |
Data is actually really simple. There are not a tremendous number of variations in the essential nature of data: there are maps, there are sets, there are linear, sequential things. There are not a lot of other conceptual categories of data. We create hundreds of thousands of variations that have nothing to do with the essence of this stuff and make it hard to write programs that manipulate the essence of the stuff. We should just manipulate the essence of the stuff. It's not hard. It's simpler.
Abstraction for simplicity
- abstract (meaning drawn away from its physical nature)
- "abstraction" sometimes is used in a sense of hiding complexity but that's not what it is about
- If you want to take stuff apart, look at a concept and ask who, what, when, where, why, how
- What
- What is the operation? What is what we want to accomplish?
- If you separate "what" from "how", you can make "how" someone else's problem
- Who
- Data or entities - these are the things that our abstractions are going to be connected to eventually depending on how your technology works.
- Pursue many subcomponents so you work with small interfaces
- How
- How the work happens - the implementation logic
- connect to abstractions and entities via polymorphism
- prefer abstractions that don't dictate how (declarative are good)
- When, where
- don't complect with design (if A causes B, you're complectin - use queues instead)
- Why
- the policy and rules of the application
- often strewn everywhere
Information is simple
- Don't ruin it
- Simplify the problem space or some code that somebody else wrote by disentangling:
- identifying individual threads/roles/dimensions
- following through the user story
- Simplicity is a choice - it's your fault if you don't have a simple system
- You need vigilance, sensibilities, and care
- Easy is not simple. Simple is something that's not entangled.
Simplicity made easy
- Choose simple constructs over complexity-generating constructs
- It's the artifacts, not's the authoring
- Create simple abstractions
- Simplify the problem space before you start
- Simplifying means making more things, not fewer