Code, Mundane and Sublime
A principle for designing systems
Check out the Clojure: The Documentary trailer! We’re so lucky to have a documentary made about how Clojure came about and what people love about it.
Let me tell you a story about the beautiful possibilities and the mundane realities of code. I lived this story. And it taught me that a good library doesn’t have to make code sing. It just needs to get the job done without making a mess.
I was working as a contractor at a company that was using Om Next, the sequel to the original Om. Both libraries were wrappers around React. While Om was a minimalist take on how to structure a UI, it revealed a problem: Often your UI’s hierarchy is very different from how your data is structured. Om Next solved this problem by introducing the idea of parsers, which let you convert a declarative expression of what a component needed into a function of the data. This indirection allowed you to shape your data and UI in different ways.
I had used the original Om, but I had not used Om Next in anger. At this job, I needed to create a few new components in the UI, so I got to writing a parser. I made a big mess. In order to express the information I needed (the query), I had to invent a new language. That’s not easy. Then you have to write code to interpret that new language, dig into the central data store for what the query needs, and return data in a new format that your component will consume. The code looked terrible (many nested ifs) and I just couldn’t see how to improve it. The existing components’ parsers looked just as bad. It looked like all of the complexity of the data model was condensed into these parsers so that the data store could remain normalized, the components could be simple, and the query could be concise.
I couldn’t help comparing this to Reagent and Reframe, other wrappers around React, both of which I was more familiar with. Reframe also had a centralized data store, but it didn’t have parsers and queries. Instead, you built subscriptions, which were “reactive” functions of the data store. If you wanted the data for a component, you would write a subscription which gave it to you. It was more direct than writing a query (new language) and a parser (new interpreter). But it was still a layer of indirection.
I asked one of the more senior engineers at the company whether I was crazy. I had never used Om Next before, so maybe I was missing something. Was all of that gnarly code worth it? Why is this better than Reframe? His answer inspired this post. He said, “It can get gnarly, but it gives you the tools such that when it’s done right, it’s really sublime.”
Being a fan of sublime code, I wondered why his answer troubled me. And then I realized the reason: If the choice is between sublime when everything goes right and horrible if anything goes wrong, that’s not a great choice. I would much rather have mostly readable, even when I’m not making perfect decisions, with less extremes on the good and bad. Put another way, mundane but workable beats sublime with herculean effort. Reframe is mostly workable. Om Next is mostly gnarly.
Since that conversation I’ve been pondering this as a principle. It’s a tradeoff between tradeoffs. When it comes to code beauty, I would rather have a fat average of the bell curve than a lot of outliers. I’d rather have base hits than home runs, because the players swinging for home runs strike out just as often. And, listen, I’m just a working programmer trying to get the cart icon to show the count of items inside. I’m not building a Rolex.
I believe my experience also inspired me to create my Reframe course. I learned to appreciate the framework. Reframe gives you the tools to build understandable UIs of medium to high complexity. And after my experience with Om Next, I saw how Reframe’s tools made decisions straightforward. They never reach the heights of sublimity that legends tell of Om Next. But the everyday code of Reframe is fine. Messes are contained and can be split. There aren’t any difficult design decisions to make, like designing a language and interpreter.
So that’s my story. I became a fan of Reframe because of how bad the default code in Om Next was. And I decided that the promise of beautiful code if you do it right is not enough. What does the code look like when I’m in a rush? What does it look like when I don’t really care that day? What does it look like when a junior programmer writes it? What does it look like when you’ve made the wrong decision but you don’t have time to rework it? On most days, things are not at their best. Making it easy to write decent code is way more important than making it possible to write beautiful code. It’s a principle I keep in my back pocket for when I or a colleague wants to make a tool to make beautiful things at work. I pull it out to dissuade them. It’s the everyday code that matters.


I think this is part of why I don't like Specter: yes, it can produce incredibly concise, powerful solutions to data navigation and transformation, but the "cost" is a very non-idiomatic DSL (in my opinion), so you end up with un-Clojure-y code.
When we first investigated ClojureScript at work, we built a p.o.c. in Om, then rebuilt it in the (then brand new) Reagent. We liked the functional simplicity of Reagent. We decided not to move forward with ClojureScript back then (we have 150K lines of React.js now, ugh!). I think my default choice these days would be re-frame -- I've built a few toy apps with it. I looked at Om Next and went "nuh-uh!". I haven't tried UIx. Replicant looks interesting, but digging into the examples, it felt more complicated than re-frame... but I might still give it a go some time.