How to defend good code

by Jason Swett,

Why good code needs defending

Good code quite frequently comes under fire. Managers explicitly or implicitly pressure developers to cut corners in order to “move fast”.

And sadly, even programmers sometimes argue against writing good code. They say things like “it doesn’t always need to be perfect, because after all we need do to ship”.

These arguments sound reasonable on the surface but, as we’ll see, they contain subtle lies.

The biggest lie in many arguments against good code is that programmers spend too much time gold-polishing their code. To me, cautioning programmers against gold-polishing is kind of like, for example, cautioning Americans not to starve themselves and become too skinny. Sure, it’s a theoretical danger, but our in reality our problem is overwhelmingly the opposite one. Similarly, the problem in the software industry is not that we spend too much time writing good code, but that we spend too much time wrestling with bad code.

If you ever find yourself pressured to write sloppy code, my goal with this post is to arm you with some arguments you can use to push back.

Here’s what I’ll go over:

  • What “good code” means
  • Weak arguments for writing good code
  • Weak arguments for writing bad code
  • My argument for writing good code
  • Let’s start with what “good code” means to me.

    What “good code” means

    Good code is code that’s fast to work with. Good code is easy to understand and change. To me it’s nothing more than that.

    If the code can be changed quickly and easily, then it’s good, by definition. If the code is slow and difficult to change then it’s bad. Any specific coding practices like short methods, clear names or anything else are just incidental. The only thing that matters is whether the code can be changed quickly and easily.

    One reason I like this definition of good code is that it also ought to be appealing to everyone. Developers like code that’s quick and easy to change. Non-technical stakeholders also ought to like the idea of code that’s quick and easy to change.

    People might not understand exactly what it means for code to be “high quality” or “good”, but they can certainly understand what it means to be able to work quickly.

    Anytime you have to make a defense for writing good code, it seems smart to remind your “opponent” (who will hopefully become your ally) that your goal is to move fast.

    Before we address some of the bad arguments for writing bad code in order to refute them, let’s first talk about some bad arguments for writing good code. It’s good to be aware of the bad arguments for your case so you can avoid trying to use them.

    Weak arguments for writing good code

    I think if we’re going to write good code, we should have a clear understanding of why we’re doing it. We should also be able to articulate to others exactly why we’re doing it.

    The bad arguments I’ve heard for writing good code include things like “craftsmanship”, “professionalism” and “integrity”.

    Saying something like “I write good code because it’s more professional to write good code” is a little bit of a copout. It doesn’t explain why it’s more professional to write good code.

    Same with craftsmanship. You can say “I write good code because I believe in craftsmanship”. But that doesn’t explain what the benefits of craftsmanship supposedly are.

    Such appeals are also selfish. They speak to what makes the programmer feel good, not to what benefits the business. These types of arguments are unlikely to be persuasive except perhaps to other programmers.

    So when people pressure me to cut corners to get a job done quickly, I don’t ever push back with talk about craftsmanship or professionalism.

    Weak arguments for writing bad code

    Finally, here are some bad arguments for doing sloppy work and why I think each one is flawed.

    “Perfect is the enemy of the good”

    This is a good and useful saying for the cases to which it actually applies. For example, when you’re talking about project scope, “perfect is the enemy of the good” is a good saying to keep in mind. A bent toward perfectionism can eat up all your time and keep you from shipping something that’s good.

    But with respect to code quality, “perfect is the enemy of the good” is almost always a false premise. Comically so, in fact.

    The typical spectrum of possibilities for a coding change usually doesn’t range from “perfect” to merely “good”. Usually it ranges from “acceptable” to “nightmarish”. A more honest version of this saying would be “acceptable is the enemy of the nightmarish”.

    Refutation: If someone tries to pull “perfect is the enemy of the good” on you, you can say, “Oh, don’t worry, I’m not trying to make it perfect, I’m just trying to make the code understandable enough so I can work with it.” This statement is hard to refute because it appears as though you’re agreeing with the other person. Plus no reasonable person would argue against making the code understandable enough to work with. What you’re saying is also true: you’re not trying to make the code perfect. You’re just trying to make it not nightmarish.

    “Users don’t care about code”

    This idea reflects a shallow, elementary level of thinking. Yes, obviously users don’t directly care about code. But bad code has negative consequences that eventually become obvious to users.

    Bad code (again, by definition) is slower to work with than good code. When bad code is piled on top of other bad code, the slowdowns become exponential. Changes that should take a day take a week. Changes that should take a week take a month. Users definitely notice and care about this.

    Bad code is also harder to keep bugs out of than good code. Code that’s hard to understand gives bugs safe places to hide. Users are obviously going to notice and care about bugs.

    Refutation: If someone uses “users don’t care about code” on you, you may be working with someone whose critical thinking skills are so weak that the person is hopeless to try to persuade. You can of course try to point out that users don’t care directly about bad code, but users do care about the effects of bad code, like slow delivery and buggy software.

    “Your company might go out of business”

    Multiple times I’ve heard something along the lines of this: “If your company goes out of business, it doesn’t matter if the code was perfect.” This might sound on the surface like a slam-dunk argument against geekishly polishing code rather than maturely considering the larger business realities. But it’s not.

    All that’s needed to destroy this argument is a reminder that good code is called good because it’s faster to work with. That’s why we call it “good”.

    Refutation: Good code is called good because it’s faster to work with. Cutting corners only saves time in the very very short term.

    “There’s no time” or “my manager made me do it” or “they did the best they could with the time they had”

    These aren’t arguments for writing bad code but rather excuses for writing bad code.

    No one is holding a gun to your head and making you write shitty code. You’re the steward of your codebase. It’s your responsibility, and no one else’s, to protect the quality of the codebase so that the codebase can continue to be fast to work with.

    If you consciously choose to take on technical debt, you’ll almost certainly never be granted time to pay back that technical debt. Instead you’ll have to pay interest on that technical debt for the rest of your time with that codebase.

    It’s easy for your boss to tell you to cut corners. Your boss doesn’t have to (directly) live with the consequences of poor coding choices. But eventually when the poor coding choices accumulate and bring development to a crawl, your boss will blame you, not himself.

    Obviously it’s not always easy to fight back against pressure to cut corners. But I think developers could stand to fight back a little more than they do (even if it means being quietly insubordinate and writing good code anyway), and I think developers would benefit greatly from doing so. And so would their bosses and the organizations they work for.

    My argument for writing good code

    My argument for writing good code is very simple: code that’s easy to understand and change is faster to work with. Obviously that’s better.

    I’ll also point out something that might not be obvious. Coding choices are multiplicative. The coding choices you make today have an influence over how easy the code will be to work with tomorrow, the next day, and every day after that. Same with the coding choices you make tomorrow. Each day’s choices multiply against every previous day’s choices.

    The result is exponential. Poor coding choices every day lead to an exponential slowdown in productivity. Good coding choices unfortunately don’t lead to an exponential speedup, but they do at least avoid the exponential slowdown.

    You can think of each day’s code additions as having a score. If you add code that has an “easy-to-change score” of 90%, and you do that three days in a row, then your cumulative score is 0.9^3 = 72.9%. If you add code that has an “easy-to-change score” of 40% three days in a row, then your cumulative score is 0.4^3 = 6.4% (!). This is why programmer productivity doesn’t vary by a factor of just 10X but more like infinityX. Bad code can eventually drive productivity down to something close to 0%.

    Takeaways

    • Our industry has a much bigger sloppy-code problem than gold-plating problem.
    • Good code is code that’s fast to work with.
    • The popular arguments for writing poor-quality code, although they sound mature and reasonable on the surface, are a result of sloppy and confused thinking.
    • Whether you choose to write good or bad code is your responsibility, and you’re the one who will have to live with the consequences of your decisions.

8 thoughts on “How to defend good code

  1. Anthony Rogan

    Good code comes from good developers, with huge amounts of experience, who are good abstract thinkers and simplifiers, using good development patterns, free from constraints on making the code as good as it can be.

    I have these qualities/ qualifications but even I expect to spend about 100 times the first-cut time tweaking code before I am happy with it.

    I expect only about 0.1% of developers would be able to develop code to my satisfaction. A huge problem for a world that is becoming more and more reliant on good code.

    Reply
  2. Greg Hall

    I agree with what you’re saying, I would just add to it that you have implicitly argued for simple code over complex code. Generally speaking, code that can move fast is simple code. Often sloppy decisions lead to more complex code, even if it is a simple change.

    As an example, we’ve all seen the “many wrappers” anti-pattern: to do something, you call a Manager that returns a Provider that returns a Service ad nauseam. It is easier to add another wrapper rather than understand and modify the existing structure, as the new wrapper lets you add your own operation in whatever way you see fit.

    I’ve come to appreciate and lean towards code that is as complex as it needs to be, but no more. Making it too simple can also lead to more work later. I would still rather err on the side of too simple, since if it is too simple, the additional work required later should be easy to add.

    Writing simple code also requires knowing how to make a problem simpler – often devs lean towards what they’ve seen that works, without really considering if there is a better way. I also find devs get too bogged down with details to think about the forest rather than the trees.

    As an example, I worked for a financial company that needed to be able to do different calculations for different customers for the same kind of investment. It is unknowable in advance how these calculations can differ from each other.

    But nobody ever said “we need to design these calculations in a way that is easy for every customer to have their own isolated calculation, which is guaranteed to be unaffected by the calculation of any other customer”. This should have been bloody obvious, but somehow it wasn’t. Literally, nobody considered this for over a decade of development.

    Reply
  3. Mike Ober

    This is possible the simplest and easiest argument for writing understandable code that I’ve every run into. Bookmarking this article.

    Reply
  4. Andre P.

    I’ve often quoted the “If I’d had more time I would have written a shorter letter” line. Often what makes code easier to understand is the distillation of an idea to its core essence. Also, one of the hardest things to do in computer science (naming things) is hard not because finding any name is hard, but because we need names to be abstract but distinct. One can tell the level of effort that went into a piece of code simply by its names.

    Reply
    1. Jason Swett Post author

      Yes. I would also add that having good names often depends on having good concepts/abstractions to give those names TO. It’s not just as simple as recognizing something in the real world and acknowledging its name. Often when things are poorly named, it’s because no one conceived of good abstractions with which to represent the work that the code does.

      Reply
  5. Anthony Hayward

    I really like your take on good code here. It gets to the heart of the issue to achieve the right focus.

    One challenge is that we can’t predict how the code will need to be changed in the future, but we are forced to do so. For example, will a computation change for all customers or just one customer? In the former case, we should put it in common code, in the latter case, we should duplicate it. But… we don’t know the answer, so we put it in common code in the name of DRY, then we change it for customer A and cause a regression for customer B.

    I wrote an article on this, hope it’s OK to share the link here.
    https://www.linkedin.com/pulse/simplicity-eye-beholder-anthony-hayward/

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *