Hacker News new | past | comments | ask | show | jobs | submit login
Write Dumb Code (2018) (matthewrocklin.com)
113 points by dvcoolarun 3 months ago | hide | past | favorite | 76 comments



This advice boils down to "write good code." Unfortunately, practical advice that goes beyond that is really hard.

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.


> This advice boils down to "write good code." Unfortunately, practical advice that goes beyond that is really hard.

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.


> Sure, you could write that algorithm as three nested list comprehensions, but does that accomplish anything other than showing off?

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


One of the annoying things about "code simplicity" is that it is a subjective trait. Whether a piece of code is simple to understand depends very heavily on what the reader is used to, and that's not something that the writer of the code has much control over.


"Code simplicity" is indeed too abstract. "Cognitive load", however, is more tangible thing to grasp.

https://github.com/zakirullin/cognitive-load


That has pretty much the same problem. Assembly programs have low "cognitive load" by that definition, but few would say that assembly programs are simpler.


I wouldn't say so. When you're on assembly programs, you hold things like "register values" in your head as well as other low-level things. It's not only about abstractions and language features.


There are much lower number of register values compared to number of variables any half complicated code has.


Assembly uses more than registers.

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.


> number of variables any half complicated code has.

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.


x64 has 16 general-purpose registers. "Much lower" starts somewhere around a half. I say, touching 32 variables in a given scope is not "half complicated", it's sabotage.


Not really, assembly has much more cognitive load. Try reading more than a trivial chunk of assembly and you'll see you can get overwhelmed pretty quickly by the amount of information you have to remember. You have to memorize code paths, which register or address has what, offsets of structures, juggle registers.


My friends call this the complexity hump. Pretty much everyone goes through this phase. The faster you get out of it the better. Some stay there their whole career.


> Some stay there their whole career.

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 think a big part of the problem is the inherited collective ego of software being birthed by computer science. If you go look at early software, early Unix, whatever, there is such a tremendous volume of really clever things going on. Little DSLs, weird experiments that bled into POSIX. All the effort devoted to grammars and compilers and making Hard Problems look Easy.

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.


> But can we actually? Ever? Are we just fooling ourselves?

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.


If you only have grugs, you will never solve the difficult problems, though. There's no binary advice to be given around complexity - whether to avoid it or to embrace it, I think.


Most programming is not really a difficult problem, in fact it is rather boring. That is why 200IQ programmers are constantly making it more complex than necessary. They need a challenge. Plus, it gives them authority.


In a way yes. If we only settled for mature tech ever, we wouldn’t get anywhere. However, even for new ideas and abstractions complexity should always be avoided. All else equal, less complexity is better.


Yup, moving from 40 hour weeks to 168 hour weeks (if the body supports) or a 4x hiring spree (if they integrate into the team) seems like nothing for some complicated crap I've seen.


Then, there are a lot of programmers who are completely unaware of these tradeoffs, and simply follow some trend or methodology without any thought of their own. 20 lines of code split over 4 microservices? Sure, why not? "It's supposed to make development more flexible and improve the developer experience". Cherish coworkers who actually care about their craft, not all shops have them.


I'm wading through this level of craziness right now, and it is slowly dawning on me that it was done on purpose, to stretch out a couple of weeks of programming to years and years. A technically legal form of corruption.

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.


A bit late reply here, so I don't know if you'll see it. But, how sure are you that it was done intentionally? Incompetence is pretty is to argue, but intentionally making something bad?


The extremely vehement push-back after any suggestion of using less code[1] in the future. Any talk of efficiency or lean techniques were instantly shut down, with a distinct undertone of nervousness. As in: “He knows! We’ve been found out!”

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.


Thanks. I appreciate the reply.

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.


What you're saying about everything being a trade off really resonates with me.

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...


Reminds me of this quote attributable to Kent Beck

“first you learn the value of abstraction, then you learn the cost of abstraction, then you’re ready to engineer”


Dr. Gregory House's motto is "Everybody lies."

My fantasy is to create a hit TV series about an engineering firm and the motto is "Everything's a trade-off."


i thought that show's key lesson was that "everything is probably lupus" but you may be right.


oh yeah i remember my first interface factory factory singletons with variable dependency injection aritys built with a flywheel pattern. pretty sure i got fired for that one.


I really don't see how it's just "write good code." In fact the opening line is about removing code being better than writing more.

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."


Maybe you're right. Obviously, if this advice helps people, then I should just back off.

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


> The lesson that code is a liability not an asset is not represented in either "be good" or "be simple."

I think it’s an extension of simplicity. 0 lines of code is simpler than 1 line, no?


Usually that's true but that's not obvious to a junior dev who thinks it's their job to wrap everything in more code and scripts and patterns.


Being able to list different solutions and their tradeoffs is a true mark of seniority.


for me I try to find the simplest solution that fulfils the requirement I have for the task on hand. No fancy abstractions/ design patterns- most of the time you don't need them, and when you do you certainly need them in another way, completely different from the one you chose. Abstraction based on sample size one and "foresight" is a sure mark of a noob :)


Fantastic comment. This is the best answer to "What is good code?" that I've ever seen. Nicely succinct too.


[dead]


Haskell is incredible for refactoring. And if the team is composed of people with the outlook of the post author, the code will be usually be easy to understand. One ought not conflate the difficulty of understanding code written in an exotic language with which one is unfamiliar, and the difficulty of understanding code which is extremely abstract and unnecessarily clever. The latter sort can be written in any language.


I mean, learning Haskell has made me a better programmer even if I've never used it at work.

Perl I have used. I wouldn't say the same about that...


Perl is the canonical example of "less is sometimes better".

I love coding in Perl. But it seems that everyone uses a disjoined subset of it, which makes collaboration awkward.

And, if C let you shoot yourself in the foot, C++ give you a shotgun to do so.. Perl gives you a timed nuclear device.


Perl (without dependencies) works awesomely well as a replacement for bash in scripts, in my experience. Unlike Python, chances that it will break the next month (or the next decade) are virtually nil.


Python without dependencies will also work everywhere basically forever. Hell, most Python 2 is valid Python 3, but it's been over a decade now - Python 3 is the default system Python in most everything.


“Most” is a highly load bearing item here.

You can’t go back and rewrite the scripts that don’t work so they do.


You know there are companies making money and providing value in Haskell right now, right?

You probably use some in fact.

Your claim it is irrelevant or has no utility has no merit.


Write boring code. Dumb code - that's too much, no need for. Boring is just right. Steve Maguire's "Writing Solid Code" is the rare book that is both practical and abstract in the right amounts. With time passing, it looks to me that boring, obvious is close enough to solid.


Code that is hard to understand at first is fine; but once you do, it should make you think "wow, that's it?" and not "I can think of a simpler way to do this."

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.


I think it is better to have a little "smarter" code if that means you will not bring a dependency. For example in Python or C I like things that do not need anything except the standard library. When you need to think about PIP or compilation of another library it is more code shifted to things that usually suck (like build systems) or you lose control and when something breaks you are still to blame. Sometimes it means to use something that is a bit more complex in comparison to using some nice libraries. Maybe the thing is that the library is not a dumb code anymore?


Conversely, your efforts to not bring in a dependency are being paid for in code you have to write and test and hope doesn't trip over a more fundamental security flaw or exploit somewhere. If you have problems they're now bespoke, there's no userbase of knowledge out there.

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).


> We should not seek to build software. Software is the currency that we pay to solve problems, which is our actual goal. We should endeavor to build as little software as possible to solve our problems.

Slow clap


"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger."

-- Dijkstra in 1988


Discussed at the time:

Write dumb code - https://news.ycombinator.com/item?id=16257270 - Jan 2018 (66 comments)


There are too many articles that preach dumb code, KISS, etc. I think the issue is overemphasized, especially in the modern age of bootcamps and whatnot. Who is writing all that "smart code" people are complaining about? If code seems too smart for you, maybe PEBCAK. That seems far more realistic nowadays than ever before.

> 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.


> 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.

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).


to me it seem you have bad experience and I agree that this kind of simple may be the wrong kind of simple ;)

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.


I couldn’t write any other type of code if I tried


Hmm. I always think I’m writing the simplest possible code, and yet when I look at it six months later, it’s still difficult.

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…



I have a nit with this concept being called "dumb" code. I see that term is an ironic rebuttal to clever code. However, calling it dumb is misleading.

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.


Imagine if engineers at Intel were told: design circuits that an electronics novice can understand!

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 guess every adjective we put in front of code will create an endless stream of opinions.

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.


The easiest way to explain dumb code is that there’s a brain budget, and reading someone else’s code is 2x harder than writing code. Therefore, writing dumb code means: make sure the code you write never exceeds the budget when it needs to be read.

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.


There are metrics for complexity that nobody who writes these articles ever mentions: Halstead complexity [1], LCOM4 [2], cyclomatic complexity [3] (and others) that directly measure how complex a given piece of code is.

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


Blog posts like this have noble intentions but since they often lack real world examples and actual code, I don't find them very useful. Everyone has a different interpretation of what constitutes dumb or simple code. In fact, the absence of concrete, unambiguous, actionable advice is something I always found fascinating about software engineering.

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.


Let me quote the very first words of the article:

> 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.


This should extend to dumb (boring) tech stack/architecture. Not sure what it is that makes people choose some crazy complex tech to implement systems used internally by a few hundred people. Scalability? Really!? UI that in no way needs to be reactive using React or Angular? Oh, and we need a workflow engine. And we are moving to the cloud, so rather than some simple flask built api, learn and deal with FaaS stuff. And then good luck deploying updates. Crazy.


> And we are moving to the cloud, so rather than some simple flask built api, learn and deal with FaaS stuff. And then good luck deploying updates.

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.


Lines of code is a poor metric, as usual. You can easily have multiple transformations in one line, or write expressions with a dozen nodes without intermediates, like a mathematician. Perhaps you believe this makes code more readable. When you run that code in a debugger (the one true way to understand any program) that will come back to haunt you.


It is not "dumb code".

It is single interpretation, simple, noise free code, using the least powerful language features possible.


> 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.


Some people learn new concepts and are eager to try them even in scenarios where they are not the best tool for the job.


So what you are trying to say is "don't use complex language features unnecessarily"? That is a very different statement.


I printed some of this and put it on the wall in my office for everyone to see


That's a neat idea. Do something simple, easy to understand. Can't go wrong, right? No chance of complexity sneaking in all sorts of ways.

I wonder if someone had this idea before.


Could we use LLMs to give a readability score to codes?

If a dumb LLM can understand a piece of code, then maybe that's one way to check if it's a sensible code.


while i agree that simple is better than complex, there is no absolute measurement nor definition for what simple is. and typically complex structures/foundations are what we build simple on top of. if our medium of expression isn’t already complex enough, we risk accidental complexity.

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.


this is true




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: