The most trivial way we write documentation is sometimes unconscious, when we just progress in our everyday tasks. While it’s probably rarely perceived this way, how we write code, organize tests or comment our commits in VCS
, becomes documentation foundations.
(It’s not something I was planning to write about from the beginning, but once I started to organize materials, I realized it’s very relevant… so why not?)
Computer requires only logical bits, yet we spend our time inventing totalNumberOfAccounts
or files_sent_to_destination
. Most programming languages use English words for keywords, but even when it’s otherwise, we still control the way we write, or what we write. Sometimes we are even able to create mythical self-documenting code, when names we use and logic we create are the most obvious, free from unexpected surprises and everything is just right ™.
Usually, ⭕ne just not simply1 invent the best name on the first try… and it’s okay. When struggling to find a good name for a function, we ask for a second opinion. When it seems like we found a candidate with potential, other developers will confirm or deny it during code-review. And when dealing with legacy code, where variables are named x1
, tmp_new
and flag
, we scratch our head for a few hours, but finally, when we understand all this mess, we at least give those variables some good names, to make every next developer visiting this code a better experience.
On the basic level it’s very similar to previous kind of documentation: names in test are actually quite important. Even mythical 100% coverage, is only half the job, when inside it looks like this:
fun validateInput_test() {
let rslt = x.process(18, "m", 20000); assertThat(result, is(true));
}
Because we took our time to decipher it, we finally understood that what this function actually tests, then we update it to this form:
fun validate_WhenAdultMaleCitizen_GrantPromo() {
let modifier = modifiersTestUtils.calculate(Age.of(18), Sex.MALE, CitySize.getMedium());
assertThat(result, is(PriceModifier.PROMOTION));
}
Now we have additional piece of documentation, that contains some business knowledge.
Sometimes when I try to understand a piece of legacy code, just looking at it doesn’t give any answers. I’m sure that given enough resources everything is achievable, but projects rarely offers such luxury. Just staring at the code is also rarely productive (I’ve heard, gazing long enough into code may end with code gazing into your soul2).
There is Git (or other VCS-s) and we all use it in our projects. This tool contains history of all code changes. The best part is that, not only we can check files earlier versions, we can also read former developers reasons to introduce changes.
With something like:
2020-05-20 "COM-3242 update therapy rules according to meeting conclusion"
2020-05-19 "merge: feature/COM-3241_select_therapy_if_from_big_city"
,2020-02-12 "merge: feature/COM-3168_select_therapy_only_for_adults"
,2019-04-03 "merge: refactor/COM-2974_i18n_instead_if_logic"
,We get lots of useful information!
Given intervals, this code changes rarely. That usually means it’s stable… or just old (and stable …ish?). We know that recent changes apply to business logic (only earlier one was technical). Recent changes were features (not bugs), so we may assume that they are just following business decisions.
Now, if we’ve got some technical concerns, we may dig deeper and start analyzing recent merges. Maybe those rules have a bug since last release, and we can search for it in those diffs? Or if our concerns are more domain oriented, there are tickets numbers. By opening them in ticket system we may find descriptions or comments that unravel not so obvious logic in code.
All that thanks to content of comments in Git commits history – a documentation of code evolution.