--- title: Simplicity is Relative visibility: public --- You ever hear about Levenshtein distances before? Alright, it goes like this. You start with a piece of text, and then play a little game with three allowable moves. 1. Insert a character of your choice at any position you want. 2. Or you could remove an existing character at a given position, that's fine too. 3. Finally, you can replace a character at a position with your choice of better, cooler character. Each of those options counts as one move. So, given enough moves, you can turn any string into any other string. You could even do it in a silly, inefficient way, like `cot -> coot -> coat -> cat`. But the Levenshtein distance between two strings is the _minimum_ number of moves you need to turn one into the other, if you were an optimal player. So the actual distance between `cot` and `cat` is 1, because you can do it with a single substitution. Thanks, Rule 3. Levenshtein distances are useful for a lot of things, including spell check suggestions and search interfaces. They're also useful for expanding our idea of "distance" beyond the Pythagorean theorem, to be a tool to quantify the difference between two things. They're the open garden gate to things like the sonic fingerprints that underpin Shazam. That's exactly the way I want to use them today: as an analogy that leads to something new. ## Baselines Most people agree that simple code tends to mean smaller code. A simple Python project will probably stick to the standard library and be less than 500LOC. At most, it might depend on something like Requests. 500 lines of simple C can't do as much stuff, but it will probably care more about fitting a set of conventions, which let you think less manually while working on it. Simple Rust will heavily leverage a subset of the standard library, maybe some popular crates, to be terse but correct and readable. So size matters, but it's not the only thing that matters. Heck, consider the nightmares lovingly crafted by code golfers trying to do things as concisely as possible - those aren't even hairballs anymore, they're bezoars! Maybe you've noticed another pattern in the matrix, though? **Simplicity is about your distance from some contextual ideal.** These ideals are shaped by more specific concerns about the effort it takes to read something. For some platform, like standard Rust, the abstract/imaginary "new developer" will need to look up anything she doesn't already understand as lingua franca. This is how ideals form - as loose consensus in the big worldwide conversation that dictates what you do and don't see all the time. Complexity, then, is a measure of how much you've violated this fuzzy social contract. How far did you go from the nebulous baseline of "trivial $platform project"? The less it's sin, the more it's simple. ## Where there's sin... There's false gods, hungry for converts. Back in the day, Rails was commonly advertised as "simple". I've dealt with enough medium-scale, long-lived Rails projects to compulsively laugh at that, but before we knee jerk giggle here, it's actually valuable to understand why that pitch landed in its time. One of the big selling points was its emphasis on convention. Ruby on Rails provides an expansive set of batteries included, defaults, and assumptions. The overhead to learn the platform involves a lot of implicit bullshit that can be difficult to search for, but once you know it, you can carry this platform awareness with you to new projects. More importantly, each Rails project can be small, needing only a small divergence to go from a default new project state to something that meets your custom needs. And that sounds... familiar, doesn't it? Rails is a walkable city. You can get wherever you need in 15 minutes with few or no additional tools. That's the dream, anyways. In fact, at this point, it actually becomes a more interesting question to ask why it *didn't* work out for so many people! What's the anatomy of a false god when we cut one open? Well, in order to be like every other Rails project, yours needs to talk to the database through an ORM, ActiveModel. Much ink has been shed about the value and pitfalls of ORMs, but the punchline is: you will eventually need to think in SQL, and now you need to know two things (and their complex interactions) instead of one. You will find similar experiences in most of the Rails ecosystem - abstractions that temporarily seem to spare you from something lower, but break down in real life use cases. This is the way a false god will fell you with a messy, but effective, double tap. It's not just offering you treats that will eventually - maybe even quickly - spoil. It's also obligating you to use those tools for the sake of staying in a sweet spot of ecosystem literacy. You must bend these tools to their breaking points, because you must use them, because you cannot depart from the provided "simple" path. And this line of thought is... kinda true? If you do try to write Rails code that avoids "trapdoor" standard tools, you'll probably reinvent a lot of wheels, bloat your project, and make it inscrutable to your peers. It's a no-win scenario. There's no such thing as perfect here. Every language has some kind of way where the concise, turn-your-brain-off solution will somehow let you down, rather than reward you. But some are less treacherous than others, which is something that shapes whether a language gains popularity, or keeps it afterwards. ## Distance from _where._ I used to argue with a professional colleague, whom I respected highly (and I think it was mutual most of the time), about how to produce simple code in a particular functional language. Those arguments were often productive _in the end_, but we'd have to get there through a lot of wandering in the wilderness and pissing each other off. Even at the time (although I didn't have some overarching theory about it), it was really obvious that we were measuring simplicity with different yardsticks. He was a native to the language, steeped in its conventions, and his advice was very good for writing code that would have been obvious to fellow entrenched natives. And I was the one always saying, "well I see the value in that, but we're pioneering the use of this language in an environment where you're the only FP guy on the team, I'm catching up, and a lot of these conventions actually make it harder for the rest of the team to follow. We have an opportunity to make our own local lingua franca here, that better fits the people who will need to work on this code." Neither of us was wrong. But we were approaching the same problem from entirely different ideals of simplicity. To write code that was simple for him, I'd have to go great distances from my own ideal, and vice versa. One of our last conversations at work, before he left for greener pastures, was me being picky about something performance-related and him blowing up at me, because my definition of simple was so tied to "what the processor is doing" and his to "what we can expect other $platform developers to understand if we hire them." Even within a single language, we can find ourselves bringing contradictory *ideal simplicities* to the table, which can end up with someone getting stabbed with a fork. Sometimes it's worth explicitly examining what your ideal looks like. And maybe even making that a group activity, so you can understand where the other person's coming from. You might never end up having the same values, but you might rub off on each other a little bit, and feel better equipped to find a compromise that disappoints you equally, or better matches the real world constraints that neither of you can negotiate away. Anyways, that's enough words expanding on what's really not too complicated an idea. Simplicity is relative, there is no absolute definition of it, and software will make more sense when you stop trying to find one. There are only waypoints we can wander from, each with their own pros and cons as waypoints. That's why we have an idea of "the right language for the job," and understanding a language (or language/framework combo _platform_, as the case may be) as providing a waypoint or ideal will make you better able to judge, through the intuition of distance, whether that starting point can concisely get you where you need to go, without screwing you over long term. Or you can read "simplicity is relative" as being about KISSing your cousins, but that falls wayyyyy outside the intended purview of this article.