# 📎 Some Things Have To Be Ugly By Artyom Bologov (A jarring beige thumbnail with "UGLY!" written all over it. In the corners, "AARTAKA.ME" and "ARTYOM BOLOGOV" is written all-caps in equally jarring green.) Some things are just ugly and you have to live with it. Or even make some things ugly deliberately—to highlight the complexity behind the code. ## Accidentally Ugly (#accidentally-ugly) I was working on "humanization" of medical data on my dayjob. The task is pretty simple: take the data, check its type, output the human-readable text. The data is strongly typed too, so it's a matter of dispatching over the type. I'm gonna use `case`! =================================== clojure =================================== ;; Four lines (case (:type data) ("Patient" "Person") (process-person ...) "Address" (process-address ...) (process-default ...)) ==================== Simple case over data types ==================== But what if `data` is malformed or null? We need to handle that too! But `case` no longer cuts it, because we need to make arbitrary checks. Well, we can encode these in the "else" branch of the `case`, but it's going to get dirty and deep indentation-wise. Better use good old `cond`: =================================== clojure =================================== ;; Six lines (let [type (:type data)] (cond (nil? data) (throw ...) (contains? #{"Patient" "Person"} type) (process-person ...) (= type "Address") (process-address ...) :else (process-default ...))) ==================== cond dispatch ==================== But okay, now we need to make some conditional logic in the Patient/Person branch. Humans are never easy to encode... =================================== clojure =================================== ;; Thirteen lines (let [type (:type data)] (cond (nil? data) (throw ...) (contains? #{"Patient" "Person"} type) (cond (...) ... (...) ... :else ...) (= type "Address") (process-address ...) :else (process-default ...))) ==================== cond dispatch ==================== Can I inline it in the toplevel `cond`? Yes, but what if there are five "special" cases like that? The `cond` grows longer, more branched and duplicated. Now to the fun stuff: some entities might have sub-entities. These need encoding too. So we also need to `recur` on the function containing these: =================================== clojure =================================== ;; Sixteen lines (let [type (:type data)] (cond (nil? data) (throw ...) (contains? #{"Patient" "Person"} type) (cond (...) ... (...) ... :else ...) (= type "Address") (process-address ...) (= type "Entity") (str ... (recur (:sub-entity data) "SubEntity")) :else (process-default ...))) ==================== Cond+recur, the control flow gets convoluted ==================== At this point, one (Artyom) might be tempted to rewrite it all with `defmethod`-s. Because we're dispatching over data and then delegating some logic to specialized methods. Must be a beautiful architecture... But it'll take twice as many lines and won't be able to handle more complex checks. Not today, methods. ... You might see where I'm getting with this: Some code is ugly enough to not fit in. No amount of cool language constructs can handle real world. Some home-made macros can manage it... for a while. But some things are inherently and inevitably ugly. Because problem domain often is ugly enough to require ugly code. Face it. ## Deliberately Ugly (#deliberately-ugly) Now to the controversial part: you should make your code ugly. No, not all of it, but some parts at least! Ugly code is the code that embraces the complexity. Complexity of the data, complexity of the algorithm, complexity of the problem domain. Making ugly code in a real world setting is not shameful. It's the only mode of existence we have when we encounter complexity. Complexity needs a lot of verbal and mental energy to process and convey. We (programmers) are more fluent in code than in words. Ugly code is a better representation of complexity than some domain glossary. It's a literal recipe for what complexity there is and how to handle it. It must stay that ugly. And it must be made ugly if it not yet is. Embrace ugly code and fight the complexity on your own terms. Copyright 2022-2025 Artyom Bologov (aartaka). Any and all opinions listed here are my own and not representative of my employers; future, past and present.