Code Is Written For Humans To Read

Part of Read before contributing, an opinionated guide by William Reade

Code Is Written For Humans To Read

…and only incidentally for machines to execute. It is more important that your code be clear than that it be elegant or performant; and those goals are only very rarely in opposition to clarity anyway.

And people will be reading your code while trying to solve some bug, and they’ll be in a hurry, and it will be easy for them to miss nuances in the code and end up making things worse instead of better. If you’ve written proper tests then they have at least some guardrails, but if your code doesn’t do exactly what it looks like it does at first glance you’ve given them pit traps as well.

In particular:

  • comment your code

    • explain what problem you’re solving
    • ideally, also explain what associated problems you’re not, and why, and how/where they should probably be addressed
    • point out the tricky bits in the implementation
    • if you did something bad, or inherited something bad, add a note making clear that it’s bad – that way there’s half a chance people won’t see it and unthinkingly duplicate it, and maybe a 1-in-2^16 chance that they’ll spot an opportunity to fix it
    • oh, and, read existing comments and update them
  • think hard about names

    • words mean things, pick ones that do not egregiously violate your readers’ expectations
    • excessively long names are bad; vague, unclear, or misleading names are much worse
    • when choosing names, think about your clients
      • they don’t need to know how your func works – they need to know what it does
      • they don’t need to know that field’s implementation – they need to know what it’s for
    • receivers and loop index vars can still be very short, and everyone knows what err is
    • other vars? please just say what they are
  • avoid unnecessary nesting

    • return/continue early on failure
    • use intermediate variables
    • do not nest more than two func definitions
    • extract internal funcs at the drop of a hat
  • avoid unnecessary indirection

    • indirection in service of abstraction is the noblest form; but be sure you know the difference, and that you really have abstracted something and not just made it less direct
    • callbacks are for frameworks, try not to write frameworks until the need is clear and present (i.e. when you are extracting an implicit framework that’s proven its value and isn’t amenable to a more direct implementation. Please don’t try to write them from scratch)
    • DI doesn’t mean you pass factories around – making a type repsonsible for creating all its dependencies (as well as using them) violates SRP.
      • DI infrastructure code probably will use some factories, this is not the same thing, leaking them unnecessarily is bad
  • avoid clever scoping tricks

    • named returns are ok for deferring error handling
    • avoid using them in other circumstances
    • definitely don’t ever do the magic naked return thing
    • don’t shadow vars that mean something in the enclosing scope:
      maybe you will never screw up = vs := but the rest of us aren’t so smart.
4 Likes