3 keys to expressivity in software design
Closure property + stratified design + fit
Please submit questions for the next weekly Office Hours (Thursday the 15th). I hope to see you there! You can ask questions on my online form, in the YouTube chat, or on the Zoom call.
Grokking Simplicity has passed 14k copies sold! That’s not bad for a programming book. If you want a copy, please buy it on Amazon because it helps boost the rank and help others find it. Leaving a review will help, too, because it will help people make the decision to buy or not. And if you’re subscribed to O’Reilly’s service, you can find Grokking Simplicity there.
I’ll be leading a discussion about Gemba at Bridges Summit, which is an online workshop for people interested in steering software engineering practices in industry. Be there and we can chat. Sign up for free on the registration page.
I’ll be speaking in September at Heart of Clojure in Belgium. Get your tickets for 10% off with this magic coupon link. This looks like one cool Clojure conference.
3 keys to expressivity in software design
One of the reasons I get to write a newsletter is because I’m actually making good progress on my next book, Runnable Specifications. If I’m writing in my book, I get lots of great ideas. Those ideas make great topics for my newsletter. And they’re reasonably well-developed (having worked on them for the book), so it’s easy to turn them into a form for an essay-style newsletter issue.
If I don’t write in my book, I have to come up with an idea from scratch on Monday morning when I sit down to draft the newsletter. It takes longer. So long, in fact, that it cuts into the time I have to write in the book! How ironic. And that’s just one example of working on big things (a book) helping you also work on little things (an essay).
Today I’d like to talk about three things that work together to build expressivity in your software. By expressivity, I mean how easy it is to write a solution to a specific problem. There are other definitions of expressivity, but this is the one I’m talking about here. The important thing is that expressivity is about the writability (dual of readability) of a specific problem.
Here’s a paradox that is on my mind a lot. Let’s say I want to write a program to draw a picture like this:
I could do something tedious, like write a program that specifies the color at each individual pixel. That would be a long program, but it would produce the correct output.
Or I could write an entire “framework” for drawing any picture of this style (repeated shapes with translations and rotations), then use the framework to write a program draw this specific picture. The framework + picture code would be orders of magnitude less code. In fact, the code savings is so great, I could code many similar drawings and still save code. In other words, the framework is very expressive, while coding individual pixel values is much less so.
Obviously, we could probably do better than specifying pixels. Let’s call that the worst case naive solution. But anything we do to improve on it is moving in the direction of building a general solution, so the improvements on it are of the same nature as our framework solution.
The paradox is why solving a more general problem (the framework) is better than solving the problem directly. Put another way: Why do we find the Kolmogorov complexity by indirecting through a general solution?
Although I don’t have an answer to that (it is related to why an artist can start an entire art movement with a single work), noodling on the paradox has given me a better sense of how to achieve the paradox—that is, maximize the writability of solutions to specific problems.
Here are the three things that I think work together to make t:
Find operations and entities that are important in the domain (high fit)
Build those operations and entities in multiple layers (stratified design)
Design your operations to work together on as few concepts as possible (closure property)
I have not come up with these ideas, but I believe I am the first to put all three together. Abelson and Sussman tried to answer this problem in Lisp: A Language for Stratified Design by describing #2 and #3. But they missed #1, which I find to be even more important.
Sandi Metz barely mentions the domain in her work (#1), though I’ve tried to show that she is doing domain modeling, even while she focuses on the “code maintainability” and the power of refactoring.
High fit
I would argue that this one is the most important feature for maximizing expressivity. Without it, you are building your layers out of unimportant ideas. For instance, you might find that there are many squares 2x2 squares of pixels that are all the same color. Instead of writing out four different pixel values, you could “abstract” the square to its own function (e.g. blackSquare() and redSquare()). Then you find that there are repeated areas where there are black squares on opposite corners and red squares on opposite corners, so you abstract again to oppositeBlackRed(). You could continue all day, building layers of these repeated patterns. It’s less code, but still very hard to write the whole image.
But this is just the kind of thing we do all the time when we refactor our code. We see some repeated stuff and extract it into a function. Unless it happens to be an important concept in the domain, it won’t give you the leverage you’re looking for.
The only way to find those high-leverage abstractions is by analyzing the domain. You have to look hard at that painting and see the patterns. You have to see that rotation and translation are important—more important than small, repeated squares of the same color.
Refactoring existing code is not enough. We need to go back to the domain first, find those concepts that make it work, and use the skills of refactoring to build operations that fit those concepts. Fit is one of the concepts I am hammering home in Runnable Specifications.
Stratified Design
Stratified design gives us a lot of leverage. Each layer adds meaning to the layer beneath it. I like to think of it as the paradox applied recursively. If it’s better to solve the general problem before solving the specific one, what is the problem that is even more general than that one? Repeat that for each layer, and you get stratified design.
I explain stratified design fairly well in Grokking Simplicity, chapters 8 & 9, so I won’t go too deep into it here. Abelson and Sussman’s paper does a decent job of explaining it, too. The idea is that each layer adds specific meaning to the layer below it, and the layer above it uses that meaning to create even more specific meaning. By the time you’re at the top of the layers, you’re extremely specific—writing the code for a particular image.
But when you look back down, each layer gets more general—that is, it is applying the paradox where it is better to indirect through a more general solution than to solve the problem directly. Because we’re doing this multiple times, we get a multiplicative effect on the expressivity, resulting in the huge savings when the problem is complex.
At the bottom, you could create a concept of a picture. In the next layer, you show how you rotate, translate, and compose pictures into new pictures. The layer at the bottom is general—any picture. The next layer is more specific—only pictures that have been rotated, translated, or composed. Then you build a layer that’s even more specific—only pictures that are four pictures put together to meet around a point
Closure property
The final idea is the closure property. The closure property, simply put, is when a function takes the same type as an argument as it returns. For example, addition and multiplication take two number and return numbers. We say addition and multiplication are closed over numbers.
Because of the closure property, we can arbitrarily nest expressions, like this:
7a + 8b + c(3 + x) + d(4 + 5y + (2 + zw))
If it seems unimportant or silly, just think if we couldn’t do that. What if we could only add and multiply at the end, like we do on a pocket calculator? We could not succinctly express this formula. We would have to write down intermediate values and enter them in again because we could not nest. This is routinely how much software gets designed: You do more complex stuff by adding lines on at the end, storing intermediate values in variables instead of nesting.
The closure property lets us write very complexly nested expressions. In essence, nesting gives us a second dimension of expressivity in addition to the existing sequential dimension. And just like with arithmetic, if we have multiple operations that all close over the same, small number of types, each operation we add multiplies the number of possible expressions.
In our example, the function that rotates a picture takes a picture and returns a new picture. The function that puts two pictures next to each other takes two pictures and returns a new picture. These functions close over pictures. Once you have a picture, you can make more pictures in many, many ways.
Putting them together
If you can define a high-fit concept in one layer, then add a layer defining high-fit operations that close over that concept, the leverage you achieve is amazing. Do this repeatedly and you can succinctly express a solution to any problem.
These three concepts are not easy to achieve, which is why I’m writing a book on it. The book also addresses meta aspects, like improving feedback loops. In Abelson’s and Sussman’s paper, they also describe meta-linguistic abstraction, which is the topic for a whole book by itself. Maybe one day.
Okay, now some open-ended questions for the comments:
Have you applied any of these concepts (fit, stratified design, closure) in your work?
What other concepts have you found helpful for building expressive software?
Do you think these three concepts adequately explain how to operationalize the paradox?



Congratulations! And it’s cool that they give you the actual number of copies sold. My royalty statements from O’Reilly have never been that concrete.
This example reminds me a video on Denotational Design by Conal Elliott, check It out if you haven't already!
There is also a book, Algebra-Driven Design by Sandy Maguire, inspired by his work