👨🏫 Office hours this week! I’m happy to answer any questions. I prioritize people on Zoom, then chat, then submitted questions. People who show up go first.
👨💻 I’ll be leading a discussion this Wednesday about engaging managers in development 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.
📖 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 boosts the rank so people who want it find it. Leaving a review will help, too, because it will help people decide whether to buy. You can also find Grokking Simplicity on O'Reilly Online.
Synthetic vs Organic (plus Meta)
I remember someone I worked with proudly showing me the code he had worked all week on in a greenfield project. It was 10 classes, half of them “Managers” of the other half. The managers called methods on the managed objects. I asked him how much of the functionality was written, and he said none. This was just the structure for the functionality he was going to write next week. 🤦
I didn’t want to burst his bubble. He was clearly proud and clearly working at the edges of his abilities. I let it go, but it gave me a distaste for the “top-down” approach. It is much better to start with the functionality and refactor it into a better design than to write a bunch of code that does nothing.
Or, at least I thought that until I worked at a place that was religious about “bottom-up”. I would read their code and find monstrous functions with abstract names like a-b-rotate
and hydrate
. These were functions used in a few places in the code. Clearly, they had been the result of factoring out repetition in the code. Now, the code worked—it had that going for it—but it also seemed overly abstract and difficult to read and modify. 🤦
So there it was—bottom-up was creating monstrous code, and top-down was creating monstrous code. The only advantage to bottom-up was that at least it produced working code before it became monstrous. Clearly, something was missing.
Well, if you’ve been reading my newsletter for a while, you might know what I’m going to say: What’s missing is reference to the domain. In both approaches the domain plays an essential role. Otherwise, expect monstrosities. Whether you’re going top-down or bottom-up, it is essential that the concepts and structures you build into your code correspond to the concepts and structures in the domain. The monstrosities develop due to creating structures that don’t belong to the domain.
I prefer to call the top-down approach synthetic. In the synthetic approach, you don’t just make up stuff that sounds good, then encode it in features of your language. Instead, you analyze the domain. You read, talk to experts, and observe the real world. You abstract from that a model, which you encode into appropriate features of your language. This works best in situations where the domain is well-understood, such as accounting, civil engineering, etc. There are book, experts, best-practices. Think Complicated domain in the Cynefin framework.
I prefer to call the bottom-up approach organic. In the organic approach, you write code first to satisfy some requirement. But you don’t just look for any duplicated code and “DRY” it up. First you use the coding as an opportunity to explore the domain. When you find repeated concepts and structures, you compare them to the domain. Do these correspond to anything you find there? Do they have obvious names? If they don’t, then they probably aren’t the right abstractions. This approach works best in situations where the domain is less understood, for instance when creating software to manage a new process. Think Complex domain in the Cynefin framework.
Each approach has its pitfalls. In synthetic, it’s easy to build “puppeteer code”—like the manager classes above. And in organic it’s easy to build these super-abstract concepts (a-b-rotate
) just because there was a bit of duplication. In both cases, you might miss the essential and powerful abstractions if you’re not referencing the domain.
But when you do reference the domain, the two approaches complement each other. You alternate between exploring the domain through code and exploring it from external sources. You develop a strong feedback loop, where the external sources influence the code exploration and vice versa. And that’s where meta comes in.
Sometimes, I write code just to improve the feedback loop. I call that meta. It doesn’t correspond to top-down or bottom-up. Instead, it’s code that might not make it into production, but that helps me learn faster.
For instance, when I’m developing a GUI in the browser, one of the first things I do is put in a <pre><code>
tag that displays a JSON representation of the state. State is usually invisible until it shows up in the GUI. But if I’m developing the GUI, I have at most a partial view of the state. And my GUI code could be wrong. So I want to see my state in a form I trust like JSON. This is an example of meta. That JSON won’t be used in production, but it helps me get more information from my code while I’m developing.
Organic, synthetic, and meta work together to help you design better software. The domain is essential to the organic and synthetic approaches, while meta is about managing the process. As a whole, they give you the tools you need to stay grounded while you code—grounded to the domain.
This essay was adapted from my upcoming book, Runnable Specifications, in the Composition Lens chapter.
Discussion questions
Do you use synthetic, organic, or both approaches?
Do the terms synthetic and organic work better than top-down and bottom-up?
What things do you code for meta—improving the process?
When I'm building out new HTML UI elements, I often add {{data}} to a page to get a rendering of the data I'm working with (using Selmer for HTML templates), so it's nice to see it crop up here and be given a name.
Although I agree with your indication of the synthetic approach being more akin to the Cynefin's Complicated domain, I would be very careful to place the activity of building software somewhere other than the Complex domain, because even when we apply more formal methods to design and understand the domain (if I understood correctly your explanation of the synthetic approach), building software will always be a creative process where we don't necessarily know how we'll get there before actually doing it, and in many cases we learn new things after collecting feedback on our implementation. Thus "Best Practices" has no place in the "probe, sense and respond" approach of the Complex realm.