Write Dumb Code (2018)
matthewrocklin.com
matthewrocklin.com
Software engineering is hard because everything is a trade off! Should I add another layer of abstraction or just add another optional parameter? Should I add flexibility for future requirements or simplify the code so it's obvious to a junior programmer?
The answer to almost every question is, "It depends!"
What are the skills of the dev team? How much time do you have? Do you already have a lot of tech debt? Are you building a prototype or a safety-critical system?
Worse, many answers depend on unknowable facts: How will requirements change in the future? Will we ever need this functionality? Did we bet on the right ecosystem?
The best software engineers can balance all these questions, even the ones they don't have answers to, and come up with a design that works. The fact that we, as an industry, can build multi-million-line programs is a minor miracle. The wonder isn't that so many projects fail but that any succeed.
If anyone were to ask me for advice--which I notice they're not--I would just say two things:
1. Write lots of programs.
2. Work with good software engineers.
I don't think I really "grasped" software engineering until a more experienced friend told me, a couple decades ago, that every abstraction is also has trade offs and hidden costs.
To me it seemed ludicrous, since abstractions were "obviously good", but ruminating on that and observing closely the code I was maintaining taught me that it was true. Everything is indeed a trade off...
“first you learn the value of abstraction, then you learn the cost of abstraction, then you’re ready to engineer”
My fantasy is to create a hit TV series about an engineering firm and the motto is "Everything's a trade-off."
Maybe it boils down to KISS but IMO it goes deeper than that. The lesson that code is a liability not an asset is not represented in either "be good" or "be simple."
It's true that removing code is better than adding it, and that code is a liability. And I admit that I needed to hear that early in my career.
But the article acknowledges that "over-adherence to [that] dogma can be counter productive". Great--so how can I tell if I've gone too far? How do I know if I should write more code or less code? How do I know if my code is "dumb" enough? The article does not help with that.
Saying that "code is a liability" is not actionable advice.
Edit: In contrast, I found this article full of actionable advice: https://tidyfirst.substack.com/p/mastering-programming
I think it’s an extension of simplicity. 0 lines of code is simpler than 1 line, no?
Everyone here seems to be labouring under the assumption that we all want to write good code, we just can't precisely agree on what good looks like.
No, sadly, there are people out there writing bad code on purpose.
For reference this is in public service where it’s easy to get away with this kind of thing, even for decades or until retirement.
To clarify: this is the softest form of corruption. There’s no brown paper bags with wads of cash in them exchanging hands. It’s a passive exploit of a disinterested organisation with too much money that’s happy to pay for developers to write verbose code for years on end with no business purpose.
[1] To put some numbers against this: recently I was tasked with migrating a data feed API to the cloud. It has about six Visual Studio projects: multiple tiers of APIs calling each other over the network. Roughly 3K likes of incredibly repetitive code written by hand. I replaced it with 70 lines of code. It was faster to do that than to write the infrastructure templates for such a complex architecture.
In my experience, incompetence has been the underlying cause for a lot that seems like malice or something more deliberate. Especially when combined with social aspects like not wanting to lose face, or having an ego.
I suppose that the transition between incompetence and malice isn't exactly black and white either, or mutually exclusive for that matter.
I've seen complacency too. People who know better, but instead of arguing that point, they go along the path of least resistance. "I get my paycheck either way"-kind of mindset.
I have yet to experience someone deliberately turning 70 lines of code into 3k lines. But, I've certainly seen that exact same thing happen. Almost surprising how similar it sounds. I should check how many lines it is, but if I would guess, it was 1k lines of code that could be done in 10. Which is what you get when you manually write a API client and objects, and you've split out the 10 lines of code into 4 separate micro-services (this isn't an exaggeration). I'm 99% sure that this was a case of not understanding why it was a terrible approach, and it being driven by consultants who are happy to explore some unsuited approach just for the CV-padding of it, if nothing else.
Deliberate feigned incompetence to make a problem worse, or job safety. I've yet to actually experience and confirm this for myself.
What you describe as "He knows! We've been found out!" might be entirely true, but it could also reflect being found out as in the impostor syndrome.. or "... that we have no clue", etc.
I don't think that's true. I think a better way to phrase it is "avoid being clever if you don't need to be." Sure, you could write that algorithm as three nested list comprehensions, but does that accomplish anything other than showing off? Do you really need that metaprogramming or would a simple function suffice? The worst code I've ever had to deal with was never written by juniors, it was written by guys that knew fancy template metaprogramming in C++ and would create a nightmare for everyone else to deal with because they wanted to show how smart they were.
A massive cohort of programmers, some at very high positions that are highly analytically minded, do not understand this. It’s not for a lack of intelligence, but curiosity and empathy (as in the dictionary definition - the theory of mind stuff).
My current best way I try to explain this is: go ahead, assume you’re 10% or even 200% smarter than others - a true 10x engineer. It doesn’t matter, because complexity scales not linearly but exponentially. The difference between big brain and small brain is minuscule in face of the omnipresent beast of complexity. It’s like surviving a terminal desease - an athlete might live longer but you’re still gonna die. And complexity creeps in simply as a byproduct of doing anything, so ignoring it will result in more, just like more code leads to more bugs.
at least grug can see T-Rex
I sometimes get frustrated that my colleagues don't know how to read an awk script, or that they think it's just a fact of life that they have to delete their git repository and perform a fresh clone because that's just the way npm is sometimes. Am I asking for too much? But then I remember that we have built all of this on impossibly tall ivory towers of complexity. It's almost unfathomable.
Almost every project is someone's vice in this vein form of worship we call software development. We tell ourselves that we've moved past this phase, we've seen what complexity really is. But can we actually? Ever? Are we just fooling ourselves?
It always seemed to me that it's ideal to use sophisticated tools to build mundane and interesting solutions. But every solution is just a tool for someone else. So much of what I love about what we do is hopelessly couples to that very complexity I despise. How can we reconcile this?
Out of sight, out of mind, I suppose.
I think the real problem is that we consider this all one big profession when in reality it's as varied as the crafts: electricians, plumbers, metalworkers, laborers, carpenters, mechanics, engineers, architects, painters, roofers, landscapers...
We just do it all.
I don’t have any hopes of the deepest theoretical complexity problems will be solved. However, let’s not be too defeatist. Look back 10-20-30 years. The things that survive in the long run (longer than a ~5y hype-cycle) are almost always an improvement compared to the past. Some genies can’t be put back in the bottle - good ideas, models and abstractions fit that bill, in my view. I’m hopeful.
> Out of sight, out of mind, I suppose.
Yes but this isn’t always bad. Things like schedulers, compilers, query planners should be out of mind. Or at least it’s the best way we can compartmentalize complexity today. It’s a last resort, so you shouldn’t pick immature and highly complex deps at the same time. Fortunately, we have mature tech that does a lot of heavy lifting, even if magical.
Ask different programmers this and you'll get different answers. One group will say that the list comprehensions reduces ceremony and makes the essential business logic clear, and writing out a loop longhand would serve no purpose other than showing off your knowledge of implementation trivia. Another group would say that writing the code without using list comprehensions makes it simpler, and the other group is just showing off their knowledge of language features.
"Make it simpler" advice is rarely actionable and usually just makes the debates more contentious. https://m50d.github.io/2018/12/11/people-who-disagree
It is not about sheer number of variables some code has.
It's about how many variables we have to fit in our head in order to understand some flow or calculation.
And unlike registers and memory addresses, you don't really have to juggle regular variables in your head unless the program has a lot of unnecessary mutability, or really bad variable names.
The counterpoint to dumbing down has been called "Kernighan's lever": https://www.linusakesson.net/programming/kernighans-lever/in...
but I noticed that the interns were having trouble with it
That's their problem. The less they're incentivised to learn and grow, the less they will; and once they no longer consider themselves beginners, they will know less than those who came before. The vicious cycle continues.
It's worth noting that software development is an aberration; literally no other profession I know of has come to widely espouse the belief that beginners should be encouraged to remain stupid and unlearning while everyone else stoops to their level.
And there's a big difference between going from ZERO dependencies, to 1. Because once you've got 1, the complexity cost has been mostly paid already so your efforts not to bring in other dependencies are going to be marginal.
So the question is, are you really saving time or complexity by avoiding the dependency management process, or just wasting it deferring something you'll already have to do later? Is it wise to constantly reproduce the same common code across multiple bits of software, rather then write it as your own library anyway (at which point you'll be inheriting a dependency management process as well, even if it's only internal).
Slow clap
-- Dijkstra in 1988
Write dumb code - https://news.ycombinator.com/item?id=16257270 - Jan 2018 (66 comments)
> Look! I replaced this recursive function with a for loop and it still does everything that we need it to. I know it’s not as clever, but I noticed that the interns were having trouble with it and I thought that this change might help.
Recursion is not clever, it is not a trick. It's a basic technique kids learn in high school in Advanced Placement CompSci A. I do not want to work with people who know less about their professional field of choice compared to kids. And if the educational system has failed interns, at least let them learn what recursion is on the job! Isn't that what internship is for anyway?
Honestly, I don't even get the sentiment. Many problems are naturally formulated in terms of recursion. And rewriting them as loops would be considered an optimization technique that obfuscates the reasoning.
but: for me simple is to have a short and concise solution of a problem/ requirement that does exactly what is required and not more.
It's not an optimization so much as it is non-pessimization. You have a lot less control over how your runtime's call stack behaves, which means that basic things like error handling, memory usage, observing intermediate state, etc. all become much more inconvenient (if possible at all).
I just last night was trying to remember how a PKCE client works by looking at my previous implementation. It’s not simple. This morning I was thinking, there must be a simpler way. And yet…
Readability, composability and elegance in programming are always a reaction to trauma caused by the shortcomings of some previous system. Therefore I think it is not possible to teach new programmers to write it.
I think this trauma is the most fundamental force in organizational coding standards as well as programming language design.
Sometimes, the novice doesn't understand the problem that the code is solving, no matter how the code is written, and even if it is documented in detail.
Now, sure, if people who easily understand what the code does have trouble seeing how the code does it, that could be a problem. Especially if the code uses a paradigm that they already understand.
If code solves a problem they understand, but with some unfamiliar paradigm like logic programming or functional programming, that's their problem, though.
"Don't use logic programming or functional programming because some novices don't understand it" isn't very good general advice. In situations where it makes sense, it's not about the quality of the code but about programmers being easily replaceable with novices off the street.
I don't see why any programmer should, as a matter of habit, write any code that can be maintained by people less knowledgeable or skilled than he or she. Unless it's stipulated as a contractual requirement. In whose interest is that, anyway?
I had comments in PRs where I should replace `str1 + str1` with `''.join([str1, str2])` (Python) because it's more "clean".
So I judge my code and others code whether it works or not. Works in the sense that it's correct but also in the sense that I can maintain it in the long run. That's it. It's kind of hard to get used to this and keep opinions to yourself though.
Also the brain budget is very low because you often have a wide mix of talent working on the same stuff. It’s that simple.
Halstead complexity measures the complexity of the "vocabulary" of code and the code "size". It uses the number of unique terms (arguments, variables), the number of unique operations (functions/methods/symbolic operators), and the total number of terms and operators to calculate difficulty and effort to produce. Lower is better. Obviously, more things and aliases and combinations of things are harder to understand than fewer things.
LCOM4 measures how many different responsibilities a module of code has. It measures whether or not two things in a module belong together by treating the code as a graph of nodes and counting the unique connected components in the graph. Greater than 2 means there is too much going on in the module, and it should be split to enhance understanding and reduce churn during maintenance. Obviously, if you are reading parts of the code that end up being irrelevant to the task at hand, you are going to be slower to accomplish whatever you are trying to accomplish.
Cyclomatic complexity measures how many execution paths there are in a given piece of code. Obviously, the fewer the better.
There are others - the counting complexity of the number of possible inhabitants of a given interface is a favorite of mine as well (is your interface a product, or a sum? Do you need strings or will an enum work? Do you need Long or can you get away with something smaller like Short or Int? Sum interfaces are smaller than product interfaces, and smaller ranged sum types - Short/int vs. Long - are better than larger ones because they reduce the number of possible satisfying implementations of the interface).
But the point is that all good simplicity metrics are static analysis metrics that are quantitative and not qualitative. Most are automated checks that can be performed given an abstract syntax tree of a program.
1. https://en.wikipedia.org/wiki/Halstead_complexity_measures 2. https://www.researchgate.net/publication/238729882_Measuring... 3. https://en.wikipedia.org/wiki/Cyclomatic_complexity
My background is in medicine where it's much more common to follow flow charts, go through check lists and administer first line treatment that's based on empirical evidence.
I understand that this is very hard to implement in software, but the almost complete absence of it means that every project is more or less unique. Sure, if you zoom out far enough then a lot of back ends boil down to "get data from DB apply to template" but it can still be surprisingly challenging to take your knowledge from one such system and apply them to the next.
> Write Dumb Code
> The best way you can contribute to an open source project is to remove lines of code from it
What am I missing here? How is writing short code dumb? How could the author argue that using "smart" techniques, makes the code longer? This is not smart, this is actually dumb. Maybe the author should put the "Dumb" in quotes, as a critique of techniques that are praised as smart but actually are worse than the common "dumb" techniques. The linked-lists might be a good example of over-engineered solutions that usually are worse than a simple vector (array).
But no, the author clearly means code readability. WHY ON EARTH would you even consider using some kind of advanced syntax or pattern if it was longer? Typically such are used to shorten code, like various meta-programming techniques (macros, reflections, metaclasses, you name it) or patterns like iterators, abstractions, match expressions etc.
Why would the HN crowd dig up a low quality article like this?
Oh, one more thing, code could be longer but smarter because it's performing better. But I don't think that's what the author meant, if he did, I imagine a lot of us would disagree.
For me it’s been the opposite.
My FaaS stuff just stays up despite my, at the beginning, minimal knowledge. I’ve had, on the other hand, instances crash just because firewall logs piled up and filled all available storage.
But I agree on the overall point.
It is single interpretation, simple, noise free code, using the least powerful language features possible.
Why on earth would you do that? That just leads to more complex code where bugs can hide.
I wonder if someone had this idea before.
If a dumb LLM can understand a piece of code, then maybe that's one way to check if it's a sensible code.
compare:
sapienti pauca, a compact latin sentence which takes advantage of the complexity of latin grammar to say what may be translated into english as ‘a word to the wise is enough.’ there’s some poetic beauty in the latin sentence that adds to the force of the advice (it’s only two words after all). i don’t think we should avoid pithy expressions where the celebrate wonderful achievements (eg recursion) all in the name of sparing our audience some pain. no, write that compact code, please. we’re happy to spend time learning just as we’ve spent time learning and appreciating the works of masters of other fields.