Sometime last Winter Nelson Elhage’s essays on using lab notebooks for software engineering made the rounds on Hacker News. One item in the early essay struck a cord with me:
Computer scientists are taught to document the end results of their work, but aren’t, in general, taught to take notes as they go
This seems to be current standard protocol for software versioned with git where care is taken to currate the repository history. Rather then record every wrong step and dead-end branch we are taught to prune and re-write the record by squashing our merge histories. A half-dozen commits showing the history of iterating on a problem are, in an instant transformed into a single commit as though the solution sprung spontaneously from the prior commit.
The result is that there is little record of the wrong turns that you took or the iterative steps taken to disover and correct errors from the initial design. Without a record of what has already been tried, it is easy to unneccessarily repeat your own footsteps or forget the reason behind a particular design call.
How I Use It
I may work with a lot of folks with backgrounds in science, but I myself was not trained in the discipline. Likewise, most of my work is hardly novel or experimentive – I make bog standard business software. So the metaphor of an “experiment” can feel stretched at times.
Digging into the question of what is a lab notebook and how does one keep one, I stumbled upon an excellent document put together by the National Institutes of Health. I derrived from it a practice that I started last December and continue to refine.
What is a lab notebook anyway?
The lab notebook is a bound append-only volume that provides a record of design decisions, procedures, tooling, observation, and background/reasoning for a unit of development work. In my case, it is a Leuchtturm1917 bound notebook which already includes an index, numbered pages, and space to record the date of each entry.
What it is not: a journal, a record of communication, or a place to compile standard operation procedures.
What does an entry look like?
Each entry is composed in pen (remember it’s append-only!). I begin a new entry for each user-story, bug report, or task as I begin work on them. The entries themselves become intersparsed as they represent the flow of my attention through the work day. I may take up an entry for a user-story, on the next page start an entry for a bug, then pick up the user-story again on the following page.
Each entry must include the following:
- The date the work was done
- A reference title (to connect together intersparsed entries)
Each entry may have the following sections:
- The goal which states in one sentence our desired outcome
- A discovery section which states any pre-conceptions and predicates to our problem.
- A design plan which states, given our discoveries, the solution we believe will resolve the goal.
- A test plan which states how we will determine that the design plan has met the goal
- An observation section, which is perhaps the most important section and I will detail below
- A next steps section to highlight any new tasks that may have spawned from the completion and observation of the prior work
Any given section can contain UML or SQL schema diagrams, prose, or any other conceivable free-form diagraming of my thoughts.
Observations & Triggers
The observation section is an attempt to keep a running log of the development process as it takes place. In particular, this section:
- Records each step completed in the design plan
- Records each deviation from the original design plan and why
- Additional discoveries (references, unexpected findings in the code, external change requests, refinement of the concepts and domain language)
- Each test run (both automated and manual), the outcome (both failure and success), and any changes necessitated by observing the software in action
Once completed, the observation section is often the longest and most useful component of an entry as it comes to contain both a record of all tests completed (and implicitly all test branch we failed to examine) and a record of all design decisions along with the catalyst for making each of those deciions.
However, this only works if I remain disciplined. Thus, it is best to keep a mental list of “triggers” to activate pausing and updating the observations section. Mine include:
- Every time a design decision is made or rejected
- Every time we must reference an external source or look up a section of code
- Every time a test runs
Largely, I have found that keeping a lab notebook, if done with discipline, has been a wonderfully beneficial experiance as it:
- Cuts down on procrastination and time to mentally queue a design after a pause from a given task since where I left off is plainly recorded
- Provides a written record of work for clients, employers, and stand-ups
- Provides a written record should a coworker join or take over a task, not to mention your future self
- Reduces the possibility of “retreading” rejected designs or completed test cases
- Encourages continuous refinement and improvement of the design plan through the course of implementation
Cover photo: Trost, Fabian. “Leuchtturm” (CC BY-ND 2.0)