History is not only the midwife of the universal, it is a bloody archive of the particulars…
Over on DEV @Ben asked the following: “What is your best skill as a developer? Whether it is a specific enough technical expertise, or just part of the craft you do well?”
In the rather lengthy conversation, one the common answers was writing documentation.
And @valenciawhite’s response stuck out:
I would love to read a blog post by you going over better documentation skills as a new developer! It doesn’t get talked about enough and there seems to be no clear cut way of how one should go about writing it.
First, in writing and communication, understand your audience; Hint it involves your present and future selfs. Writing is an opportunity to have a conversation with yourself, with the possibility of sharing with others.
Gathering for the Journey
Caminante no hay camino, se hace camino al andar.
Walker, there is no road, the road is made by walking.
What follows is not a “clear cut way” but instead a meandering.
I’ve been a professional software developer for almost 25 years, I am not the best person for speaking of specifics to a new developer; especially given that a younger me said “the docs lie.”
So perhaps what follows is a veiled apology and confession. For I have developed a love of writing documentation.
Documentation as a Matter of Inclusion and Accessibility
The map is not the territory.
Consider the goal of software: to automate tasks. At an abstract level, we do this by mapping inputs to different forms. (e.g., we map user input into database records into email messages)
Over time our understanding of the tasks we’re trying to accomplish changes, and we adjust the mapping (e.g. change the code).
In my experience, if we change our code without exposing the intent of the change, we eventually lose our understanding of system. Yes we can figure what it’s doing but not why it’s doing.
Let’s pull back for a moment: Code describes how we are automating the tasks. Documentation describes why we are automating the tasks.
One of the most cognitively expensive tasks I perform in software development is puzzling through why something is the way it is.
I’m going to craft an arbitrary chunk of code to use as an example:
if points < 5 do something else if points > 10 do something else else do something even differently end
The above code, o’ traveler, is the proverbial fork in the road. Here we have a conditional and our mapping begins to diverge. But why? That is where just a bit of documentation can help me orient to the decision the code is making.
Perhaps those “do something” portions are well named methods (that is a form of documentation). But why 5? Why 10? Why not 6?
If there’s no inline comment, I might dive into the commit history (e.g.
git annotate or
git blame). The commit history will show me the context of when we introduced 5 and 10 into the code-base; Commit messages are a form of documentation.
If the commit history doesn’t provide answers, it does create a paper trail of who to ask. And let me tell you, my heart sinks when the trail leads to me.
So, write documentation to help orient people to the why; and remember people includes your future selfs.
I am deliberately using the plural, because while you may write a line of code once, you’ll got back and read it several times. Each time you will be a different person.
Start with the End in Mind
Let’s pretend that you, a software developer, have just picked up an issue or someone assigned you an issue. You’re going to write code that resolves the issue.
Take a moment to state what done looks like. Where would you write that? Because that’s a form of useful documentation.
For myself, there are three probable places where I’ll write what done looks like:
- On the assigned issue, maybe edit the original or add a comment.
- In my personal knowledge base, this is often when the issue requires involved analysis.
- On a blank sheet of paper, as this is the most versatile of mediums for thinking.
In restating what done looks like you perform a mapping function; moving knowledge from the screen, through your brain, and out onto a different screen or paper.
With the end now in mind and “on paper”, I start laying the groundwork. Perhaps I’ll stub out an integration test. Maybe I’ll load the application and move around the “space” where I’ll be working. Or maybe I’ll first refactor some code related to the solution.
Refactoring to Understand
At one point, I thought that refactoring needed to make the code-base better. But have since learned that refactoring shouldn’t make things worse.
Let me reiterate this point refactoring need not make things better, it should not make things worse.
In that nudge of the equality operator, a world opens up. Now, I use refactoring as a way to orient to the new to me location in the code base or to have a conversation about a direction I’m proposing.
In other words, I use refactoring with an “exploratory” mindset. I use it to traverse a territory, and both my code and documentation are notes regarding that traversal. I bring that back to the shared knowledge base (our code, commit history, and surrounding conversations).
What is Good Documentation?
To describe something, sometimes you must say what it isn’t.
Good documentation is not a one for one restating of the how. In the following example I say the same thing twice:
## When dealing with less than 5 points if points < 5 ## Do the less than 5 points thing do something ## When dealing with more than 10 points else if points > 10 ## Do the more than 10 points thing do something else ## Otherwise else ## Do the within 5 to 10 points thing do something even differently end
Good documentation is not like a meal recipe page found on some website, where the details of what you want are littered amongst the chaos.
Good documentation is like good notes from a conversation: it isn’t a transcript but it’s a synthesis of what you heard. The goal of the documentation is to share context.
Good documentation is like a good book: on the spine you have a quickly scannable title, the front and back covers hint at what it’s about, the early pages include references to other works and maybe a table of contents, and when applicable the back section holds a glossary, index, and/or bibliography.
Good documentation is like road signs: when I’m approaching an interstate on-ramp it would be mind boggling to see all the city and towns that this interstate leads to. Instead I want to decide “Am I heading the Chicago direction or the Detroit direction?”
Good documentation is like labeling your boxes when moving: when you’re lugging something into your new place, you don’t want to open each box to decide where it goes. You want guidance so that you can set down that box in the right place and not move it multiple times.
Naming Things Is Hard
At my first professional software job, our system had a hard requirement: table names and field names had to be 8 characters or less. We tried to pack as much meaning into those 8 precious characters.
Nowadays, that is not a constraint for my work environment.
When naming something, I choose to be verbose and chatty. Sometimes in the early moments of coding my method or variables names look like sentences. As I work with the code, I refine the names. And it’s easier to replace a verbose name with a more terse name.
A recent example that I added Tag#accessible_name to the Forem code-base. It turns out the name isn’t quite right. But, at the time of writing “accessible_name” was a unique name for a symbol, so I have confidence changing it will be easy.
Path of Least Surprises
Good documentation uses domain terms of the application, framework, language, and industry.
Terms like caller, receiver, parameter, variable, instance variable, local variable, global variable, instance, class, module, etc.
Taking time to write documentation, shifts my thinking from solving the problem to communicating about the solution. I will often times look at the solutions and say “Well that named symbol could be better.”
Help Your Tools Help You
Good documentation should be where your tools expect it to be. And structured according to your tools expectations.
Does your editor provide IntelliSense functionality (e.g. auto-completion)? Does the auto-completion include more than just the symbol’s name?
These days, that means it’s likely interacting with Language Server Protocol (LSP 📖), which uses both the code and documentation to help enrich the auto-completion results.
In other words, if you provide method and class level documentation, everyone’s code editors will start sharing that with you and other developers of the project.
Maps and Atlases
As code-bases grow, organization of concepts becomes critical. Hence we see module spaces and classes paired with folder structures. And while those are all mechanisms for mapping concepts to containers, we should explain why you would put something in a container.
I once read “all computer science problems are mapping problems”, things really clicked. Because communication is a mapping problem.
When I speak, I map my thoughts into a string of words. And the receiver maps those string of words (plus all the other noise and body language) into their thoughts.
So consider how your documentation and code can help you build a map to help others to orient to the journey. I mean journeys. For there is the journey of understanding the current state as well as the journey of understanding how things arrived at the current state.
And as I think about documentation, I also drift into a famous passages from George Orwell’s 1984:
Who controls the past, controls the future: who controls the present, controls the past.
The above describes a strategy for propaganda and organizations maintaining power.
Let’s twist it just a bit into something less fascist and more collaborative:
Who shares the past, shares the future: who shares the present, shares the past.
That feels better, more hopeful, a pathway forward. And I’d like to mix that with a favorite proverb:
If you want to go fast, go alone. If you want to go far, go together.
Fast may sound great, you know “go fast and break things” but by now I hope many folk can look around at our tendency to go fast and say “This can’t be the best way.” Spoiler, the tortoise wins the race.
And by now, I hope it is clear, in software (and knowledge-work in general) you are always going together. On these journeys there will always be your past selfs, that most ephemeral present self, and the emerging future selfs. And that’s if you are working on your code alone.
So be kind and provide ample breadcrumbs for this journey. It’s a long road, and you’re going to want snacks.
Map Quality Deteriorates
Entropy is real and constant. You’ll write documentation and it will decay. The words might be the same, but the map—documentation—is not the territory.
The territory changes with each revision, the arrivals and departures of people. Fixity is a fools errand. Understand and accept that.
Also understand the further you carry documentation away from what it describes, the more energy that documentation will require to maintain.
In other words, inline method documentation is relatively cheap to maintain (it’s right by the method) compared to documentation describing the technicalities of a feature.
Understand and accept this. But don’t abdicate. Advocate for resources to write and maintain the various levels of documentation.
We learn through elaboration, not cramming, so take the time to write documentation, you will learn more about what you document.
And please remember code and documentation are malleable, hence the soft part of software. Treat them as such.