Documentation is important for three main audiences:
- you, right now;
- you, six months from now; and
- everyone else.
Depending on the scope and timeline of your project, the last two audiences may not seem relevant—you may just be writing something short to meet an immediate need.
Immediate needs change. Something you thought would be a quick in-and-out-get-it-done turns into something larger: new features creep in and requirements evolve—the hack-job you wrote six months ago suddenly needs updating and you are the only person that can do it. Perhaps you ask a friend or teacher for help, and they now need to try to understand your abomination. You knew how it worked six months ago—why don't you know it now?
It's true that not every line of code will be read by anyone other than you right now. It's also true that we don't know which lines of code will come back to haunt us.
Given the unknowability of the future and the fallibility of our memory, we would be wise to assume that all our code will be read, at some point, by someone other than us right now. Hopefully, our code is [[Readability|readable]]—but no number of meaningful [[identifier|identifiers]] can guarantee future understanding of our present-day thinking.
Documentation serves two main purposes:
- [[#Documentation to explain|explaining code]] for future readers; and
- establishing a [[#Documentation as a contract|contract]] with future users.
When documenting, you should keep two golden rules in mind:
- [[#Documentation serves|documentation should serve the code]]; and
- [[#Proportional documentation|documentation should be proportional]].
## Documentation to explain
Effective documentation should assume competence with programming and the tools being used—but should not assume familiarity with the codebase.
There is no need to explain what an [[f-strings|f-string]] is—or your use of [[Keyword arguments|keyword arguments]]; there is a need to explain your program logic and bodges. Logic and bodges are the things that will come back to bite you in six months—they are also the things most inscrutable to everyone else.
Documentation to explain is commonly included as in-line comments.
### Assume competence
The following example assumes the reader knows nothing about Python and needs every line explaining:
```python
# define a subprogram called calculate_total, taking one parameter (p_prices)
def calculate_total(prices):
# create a variable called total and set it to 0
total = 0
# loop through each price in the list of prices
for price in prices:
# add the price to the total
total = total + price
# return the total
return total
```
The documentation duplicates meaning already evident from the code and gets in the way. It's visual noise. This is a good example of how excessive documentation can reduce readability—the code is already self-documenting, so no explanatory documentation is required:
```python
def calculate_total(prices):
total = 0
for price in prices:
total = total + price
return total
```
Much better.
### Explain logic
The logic of a Caesar cipher shift is familiar to cryptographers (and most Computing GCSE students) but not universally understood. This example unfairly assumes understanding and suffers from a lack of documentation:
```python
def encrypt_letter(letter, shift):
letter_position = ord(letter) - ord('A')
shifted_position = letter_position + shift
alphabet_length = ord('Z') - ord('A') + 1
wrapped_position = shifted_position % alphabet_length
return chr(wrapped_position + ord('A'))
```
For those in the know, the logic of the `alphabet_length` assignment and meaning of `wrapped_position` could be figured out—however, well-documented code should not need to be deciphered. Brief in-line comments to explain the logic serve this code well:
```python
def encrypt_letter(letter, shift):
letter_position = ord(letter) - ord('A')
shifted_position = letter_position + shift
# +1 because both 'A' and 'Z' are included in the alphabet
alphabet_length = ord('Z') - ord('A') + 1
# without this, shifting past 'Z' would produce a character
# beyond 'Z' instead of continuing from 'A'
wrapped_position = shifted_position % alphabet_length
return chr(wrapped_position + ord('A'))
```
### Explain bodges
Bodges are never desirable. Avoid them at all costs. Sometimes, they are unavoidable.
No amount of reading could explain why on Earth the username length is limited to 8 characters:
```python
def get_username(forename, surname):
return (forename[0] + surname)[:8]
```
If there is a reason, explain it:
```python
def get_username(forename, surname):
# the school's login system only accepts usernames up to
# 8 characters, so longer names must be cut short
return (forename[0] + surname)[:8]
```
You need to explicitly document facts about the world that your code can't self-document.
## Documentation as a contract
In addition to explaining logic, documentation can act as a contract between your code and the person using it. This contract establishes the behaviour of your code and sets the terms of this behaviour. Approaching documentation like this works particularly well for [[black box]] [[function|functions]].
An effective contract is fair for all parties: you promise your code will behave like _x_ only if _y_ conditions are met. An effective contract is also clearly expressed, using formal notation wherever possible.
The contract should be so comprehensive that there is no need for people using your code to actually read your code. Somewhere in your documentation, you should include:
- a list of parameters, their data types, and any requirements of them (e.g., must be a positive integer);
- a formal description of things that must be true before your code will even think about working; and
- a formal description of the things that will be true after your code has run.
With this information in hand, anyone using your code knows exactly what is expected of them and what to expect of your code. I argue for using [[6P documentation]] as it provides an effective framework for providing this key information.
### Header documentation
Documentation as a contract is typically included as header-documentation. Header documentation is documentation that appears at the 'head' of your subprogram. In Python, we use docstrings for this.
Here is an example of a Python function using a docstring for its 6P header documentation. You should be able to understand exactly what the subprogram does without reading the code or knowing how it works:
```python
def clamp(value, minimum, maximum):
"""
Procedure:
clamp
Parameters:
value, a number
minimum, a number
maximum, a number
Purpose:
Restrict value to lie within the inclusive range [minimum, maximum].
Produces:
result, a number
Preconditions:
minimum <= maximum (not verified)
Postconditions:
if value < minimum:
result = minimum.
if value > maximum:
result = maximum.
else:
result = value.
minimum <= result <= maximum.
"""
if value < minimum:
return minimum
elif value > maximum:
return maximum
else:
return value
```
In Python, we can use [[Type hints|type hints]] to make our subprogram [[signature|signatures]] more self-documenting which can reduce the burden on your header documentation slightly. I argue that, even with type hints, header documentation should still include [[data type|data types]] so that no reading of code is required. NB that type hints are not enforced at runtime—just as your documentation doesn't place hard limits on what your users can do.
## Documentation serves
Documentation should serve your code and your project's needs. The point of writing documentation is not to blindly complete a checklist but to serve the people reading and using your code.
### You, right now
Documentation serves you right now by forcing you to think through the problem you are solving. The process of writing clarifies thinking—writing header documentation before you start and explanatory comments as you go—forces you to engage with the inadequacies of your own understanding, and gives you confidence in your own correctness.
The process of verbalising what your code should do forces you to think about what your code actually should do. The act of writing documentation before code is an act of design. For this reason, even the simplest subprograms should have some header documentation: if you cannot put into words what your code should do, why are you even writing it? Stop wasting time blindly hitting your keyboard and think!
It is absurd to think that you will write good code without first understanding what it should do. Of course, we all do this—coding as experimentation has value—but we should not expect our first attempt to be any good. Just as writing a plan before your essay saves you time by front-loading your thinking, writing header documentation before code does the same.
### You, six months from now
Documentation serves future you because your memory is flawed and your brain is stupid. There is no worse writer, or programmer, than you six months ago. Effective documentation serves you by reminding you what your code does even when you tried the best you could to make your code self-documenting. You cannot assume that future you knows everything present you knows.
### Everyone else
Documentation serves everyone else as we have no idea of the contents of your brain. We need you to explain to us, clearly and precisely, what we are reading. We all speak the same language but you need to help us "come to terms"[^1] with you. When code is written, it dies on the page. As we read it, we bring it back to life by piecing it back together—and it helps a lot when we have instructions.
## Proportional documentation
The documentation needs of classwork are different to the needs of assessed work, which are different to the needs of the A-level [[H446 NEA Overview|NEA]], which are different to the needs of production systems. The amount and detail of documentation depends entirely on your project's requirements.
Do you need to write full 6P documentation for every single program you write in class? Definitely not. Is there educational value in doing it anyway? Absolutely. Is the time-cost worth the educational gains? This is up to you. Your documentation should be proportional to your project, and there are no absolute rules about what this should look like.
If you follow these general guidelines, you shouldn't go far wrong:
- be pragmatic—use your judgement;
- be proportional—scale your time to the stakes; and
- do what your teachers tell you—if I tell you to write 6P documentation, **do it**.
Finally, remember the direction of the relationship between documentation and code: documentation is the support act, not the main event. The support act is needed, but it shouldn't get in the way.
## Notes
[^1]: Mortimer J. Adler and Charles Van Doren, [[How to Read a Book]] (Simon & Schuster, 2011), loc. 1366.