Simple System + Rick Feedback
I saw someone posted in the Clojurians Slack about something Clojure has taught them:
I’ve come to see programming as:
1. building simple systems
2. and building nice feedback loops for interacting with those systems.There is no three. I am so happy Clojure helps me with both.
—teodorlu
This is beautiful. It’s wonderful. And it’s a complete list, as far as I can tell.
I want to unpack these three points a litttle bit in the context of Clojure, partly to remind myself of these points, but also to better understand why we feel that Clojure is such a good teacher. And, yes, there are three points in the quote, which I’ll get to.
Hickey Simplicity
Clojure’s creator, Rich Hickey, has included the industry’s understanding of what simplicity means. After Simple Made Easy, we think of simplicity as a function of how well decomposed a thing is. We pull apart a problem until we understand how it is made of subproblems. This is the simplicity of making a thing to solve a problem at the appropriate level of granularity and generality. General solutions tend to be simpler.
My favorite example of this kind of simplicity from Clojure is the atom. It found a common problem—sharing mutable state between threads—and it solved it in a very straightforward way: You get one mutable reference with a very constrained interface. In return, you get a strong but limited guarantee. Hickey found the problem, separated it from the rest of the problems, and solved it in a minimal and useful way. His achievement of simplicity is inspiring.
Incidentally, I believe the careful decomposition of problems is why Clojure’s parts seem to work so well together. For example, atoms love to work with immutable data structures and update. They’re each solving small, related problems—safely sharing values (immutable data), working with nested structures (update), and changing the value over time (atom). I believe they compose well together because they were decomposed by the designer himself.
Simple systems
The Slack message mentions “simple systems”. In systems theory, according to Donella Meadows, we get a different definition of simplicity. There, simplicity is about the number and nature of feedback loops, delays, and nonlinear causal graphs—in short, interactions within the system between the parts. If you have fewer loops and branches, the system’s behavior is easier to understand.
Again, I’ll use the example of the atom. In many languages, we would use an object with mutable state with methods meant to read and modify that state. We might make a whole new class just to represent the count of the number of words in a directory of text files. And if multiple files are being counted in parallel, we’d need thread-safe coding practices, probably locks, to make sure we counted correctly. But in Clojure, it’s just an atom. It feels to me that the causal chain is much shorter, perhaps because the atom itself is so reliable. Locks are reliable, too, if you get them right. But you have to take the lock, do your work, then release. You need a try/finally so you release reliably, even after a failure. There’s a lot to get right. With an atom, you just:
(swap! count inc)
Simplicity as clarity
Dieter Rams is lauded as a master of simplicity. Many people conflate simplicity with minimalism. But Rams insists it is about clarity, not minimalism. The volume knob changes the volume. The on-off switch is clearly a toggle. The extreme focus on clarity can breed the aesthetic of minimalism.
Clojure too focuses on clarity. The clarity of purposed of each special form—if, let, etc.—is part of this form of simplicity. So too ar the function in the core library. Though there are some outliers, most functions reveal their purpose plainly. And they are so plain that even though there are many functions, their docs are shown on a simple page. When I look at the Javadocs for the standard libraries, I see staggering obscurity. Each class seems like a world of its own, ready to be studied. Methods return yet other classes—more worlds to understand.
Feedback
Now let’s talk about feedback. Clojure excels at feedback. The obvious mechanism of feedback in Clojure is the REPL. The read-eval-print-loop is an interface between you and your code’s execution. A skilled programmer at the REPL will evaluate lots of expressions and subexpressions. You can recompile functions and run tests just as they would be executed in a production system.
But there are more subtle things that are easy to overlook. All of the literal data structures and atomic values like numbers, strings, keywords, and symbols, can be printed with no extra work. You can put a prn right in your program and print out the arguments to see them.
You can navigate the namespaces and perform reflection. The reflection works on Clojure (list all the vars in this namespace) and for JVM stuff (class, supers). These tools are a source of information about your code.
Clojure added tap> a few years ago. it’s a built-in pub/sub system used during development. Tools like Portal use it to get values from your running system and visualize them.
There is no three
The third point should be unpacked, too. “There is no three.” It’s a point stated in the negative. It implicitly excludes all of the things that aren’t listed above. It’s sort of an invitation to abandon all the bad habits you picked up over the years. My bad habits include adding to many layers of indirection, trying to anticipate the future, and overmodeling. The rule to combat these is called YAGNI (You Ain’t Gonna Need It). Or DRY (Don’t Repeat Yourself). These are good practices that help you build simpler systems. But they’re all subsumed in the refocusing on the first two positive statements.
I think there’s value in enumerating the little rules of thumb this list leaves out, especially for beginners. As someone becomes an expert at something, the way they talk about their skill often sounds more and more abstract. “It’s just simplicity,” hides how hard simplicity is to achieve and even to understand. What sounds wise glosses over the thousands of details that you learned on the way up. That’s not to take away from the beauty of the expression. Just saying that these abstract expressions of what’s important leave a lot out.
That said, the reason the third item is so refreshing is that we’ve been taught at school and at work to code in a certain way. I was taught in Java to seek out the isA hierarchy inherent in a domain and to express it with a class hierarchy. It’s where we get the classic class Dog extends Animal. But it’s putting the cart before the horse. Yes, a dog is an animal. But is that relevant to my software? Saying “There is no three” gives me permission to stop and refocus on simplicity and feedback.
So thanks, Teodor, for sharing this. It’s a wonderful view into your progress as a programmer. You should be proud of all you’ve accomplished. I really like how you’ve boiled it down to these three ideas. It reminds me of how much I’ve learned from Clojure and how far I still have to go.


