I’ve been thinking a lot about communication and code quality over the past couple of years. It’s nothing new of course, I’m not the only one, but I believe that most of ways we address it start with a wrong perspective, and a wrong focal point.

(this is a heavily edited and expanded text version of my lightning talk at PyCon PL 2019)

Disclaimer: I don’t claim to know how to write good code. I’m drawing from my ~30 years of experience writing bad one.

There is plenty of advice on how to write good code. The Zen of Python, the DRY/Once and Only Once, a whole bunch of software metrics, random advice like particular naming schemes and attempts at consistency. Most of those are very procedural. Which is understandable, they are mostly created by techies for techies, procedures is what we do for a living and what we tend to expect.

But these are bad rules. Because, really, they are not rules at all. They are guidelines. Tools.

They are more like guidelines

Writing code is a lot like writing prose. You have one extra restriction (it has to be executable, and perform some task as it runs), but other than that you get a similar level of flexibility, complexity, and the same problem: how to convey your goals, ideas, and reasoning to other people. Yes, people.

If you focus on tools instead of communication, just like with prose you will get a result that is correct, to some definition of correct. So, congrats! But it might not be the definition you really want.

You are technically correct

We need to start from a different direction, with a different focus, and that focus should be empathy.

To be more specific: empathy for the maintainer.

Who, with high probability, might also be you.

After a long break from the project.

On a tight deadline.

Because you have a fire on production and everyone else on the team is also involved with some aspect of that fire, so you go into the code and what happens is:

I have no memory of this place

If you already know this exact scenario: good, it’s a blessing in disguise. It’s a rare chance to see your own code with completely fresh eyes; it’s some of the best, most humbling feedback you can get.

(and if you don’t, don’t worry! it’s all ahead of you)

Let’s approach the problem from a different direction. Here is the situation:

you the maintainer

There are you now, and there is the maintainer in the future. Who, again, might be an older you, but not necessarily.

The two you communicate via the source code:

you ↓ [your code] ↓ the maintainer

Assume this is the only means of communication. It’s uni-directional: you will not get immediate feedback on whether what you sent along the wire is sufficient to understand you. So put yourself in the position of the person who will receive that communication and ask some questions:

  1. How understandable is it? Are there any unnecessarily tricky parts? What can you simplify? Again, assume a fresh look by someone who does not know the full history of the project.
  2. How much of other code do they have to read? If your file or function a cohesive part of the larger whole, readable on its own? Is the control flow visible and readable? How much of the surrounding package, library, or project do they have to read to make sense of it?
  3. What can you assume they already know? The language, the framework[s], the libraries you used, and the project this file is part of, all create their own conventions and vocabulary; the deeper in this hierarchy, the less you can rely on it.
  4. Most importantly: how can you help them understand it? If you have any experience working as a developer, you already know how it feels to stare at someone else’s code, trying to make sense of it. What do you remember from situations like that? What would have helped you? (you might want to answer “documentation” or “comments”; neither is a good main resource for understanding code, so focus on the code itself)

And then, with those questions in mind, take a look at the tools at your disposal:

  1. conventions: not all conventions are of equal value; look for the ones that are widely shared, for example: if your code does something similar to things that are in standard library, use a similar interface, so that people can recognise it,
  2. vocabulary: variables are nouns, functions and methods are verbs, use them to explain what’s going on,
  3. control flow: if the reader can analyse code from top to bottom, following the control flow and choosing parts to read more closely, it will be easier for them than if they have to jump between multiple unrelated files to piece together the top-level behavior,

and all the other tools mentioned at the beginning of this text. Use them as a catalogue of ideas to address the questions. Things to try out and use where you notice they help.

Don’t stop when your code starts doing what you want it to do. Make it work first, but then keep editing it to make it readable. Take a break, return to the code after a while and see if you can edit it some more.

You may think you have no time for it, and are in the hurry to deliver functionality; in most cases that is not true. If you work on a real-life project, in a team larger than a single person, you have no time not to do it. Whatever you save here you and your team will repay in the future, with interest, when trying to maintain the code.

Empathise with the maintainer. It will make you a better programmer.

See also: My main code quality guideline