Like a diary, but for code
On a few occasions, I've started work on a brownfield project and found that, either due to poor programming practices or because there was no elegant way to implement a feature, it's been necessary to create documentation beyond what is normally included in a README. This documentation often took the form of glossaries, diagrams, or additional exposition on the codebase.
Your typical README contains all or some of the following: a short introductory paragraph, a list of dependencies, instructions on how to install the app, configuration, how to run the test suite, database creation and migration, services, and how to deploy the app. If it's an open source project, there will likely be sections devoted to listing authors, rules around contributing, the license under which it can be used, as well as additional documentation around how to use the package.
There are contexts in
this is inadequate. Let's describe such a situation in detail.
You've recently been assigned to a brownfield project. You're looking at the app for the first time, and you notice fairly quickly that the codebase looks like a Jackson Pollock painting.
For starters, the company pivoted a couple years ago, so large portions of the app remain that aren’t relevant anymore. Perhaps entities are named in a way that no longer accurately describes them. Maybe there are references to features that have long since been abandoned.
Obscured by all of this cruft are the operative parts of the app, which aren't much to look at, either. There are models named Article, Post, and Essay as well as models named Writer, Scribe, and Author. For business logic reasons, an Article can belong to an Author or a Writer, but not a Scribe, a Post can belong to all three, and an Essay can belong only to an Author.
There's a "God" model that encompasses multiple concepts, another model that should really implement a state machine, etc.
In short, this app needs a serious overhaul, but maybe you've been brought on to implement just a few features or the client simply doesn't have the budget to pay for an overhaul right now or the powers-that-be don't see the value in well-crafted software and aren't willing to pay you to refactor the code, no matter how many times you explain that substandard code actually costs more in the long run. Heck, maybe the client thinks the code is just fine and not in need of refactoring at all.
For whatever reason, refactoring this code is not something you'll be able to do right now and you suspect that the technical debt that's accumulated will only increase for the foreseeable future, making the app ever more brittle and difficult to add features to.
In an ideal world, this codebase would have been cleaned up ages ago. In the meantime, you still have to help your client.
Unfortunately, the mental overhead of working on this app makes development a grind. You're constantly having to remind yourself of the subtle differences between an Author, a Writer, and a Scribe and how they relate to the other models in the application.
What's needed is documentation that will reduce the mental overhead that comes with working with certain kinds of apps; basically, an augmented README. For simplicity, I'll refer to this 'superREADME' as a VELLUM ('cause it's fancy).* I’ll also stop screaming README(!) and VELLUM(!) at you from here on out.
A vellum and a readme are quite similar (really, the former could be thought of as a 'superset' of the latter). The difference is that a vellum adheres to a longer, more expository style of documentation than is typically found in readmes and there is an expectation that the author(s) of the vellum will reference it with some frequency. A vellum is the place where you document your confusion.
A vellum can serve as a guide through less-than-ideal codebases, but it also has a place in well-architected projects (brownfield or greenfield) that have complex domains and require a bit of explication. You would use the vellum as a way to note difficult or subtle concepts around the domain or the codebase that any developer coming into the project should know before starting work.
The remainder of this blog post will cover what should go into a thorough vellum file and some of the pitfalls of over-reliance on such documentation. It will be written from the perspective of a consultant working on a client application, but that isn't to say that vellums can't be carried over to open-source contexts. They can certainly be used to convey ideas that will help other or new contributors better understand the codebase.
The first part of the vellum should consist of an introduction to the application including its purpose, its audience, and details around the business itself, such as how the app makes money. At this point, you're providing context.
In the example below, we write an introduction to an application that helps users manage their stock portfolios.
This application is called SmartMoney. It enables the casual trader to manage their portfolio of stocks. The app makes money by charging traders a flat per month fee of $5, in contrast to the payment structure of other services where users are charged per trade. The app allows users to make a wide variety of orders. Namely:
- market orders
- limit orders
- stop-loss orders
- stop-limit orders
- bracketed orders
- trailing stop-loss orders
The app also provides a simple interface for shorting stocks. Some other key features of the app include the ability to set up notifications for when a particular stock's worth has dropped below or risen to a certain value. The app also has a recommendation engine that suggests stocks to invest in that are "hot" and in an industry in which the trader has shown interest.
When development first began, the app was geared towards only sending notifications based on changes in stock prices. However, the company decided to make a more fully featured app as customers wanted an experience that integrated setting up notifications with making orders.
You'll note that the introduction also lays out some terms (the types of orders that are available in the app) that the next developer should understand before writing any code (more about that soon). It also mentions the app's history, which helps to explain any cruft that might be left behind.
Your goal is to convey enough salient information about the app that any developer could just open the vellum and know who this app is for, why certain quirks of the app are present, etc. You don't want to go into excruciating detail. Save that for storyboarding.
You should follow this introductory paragraph with a glossary of terms. The glossary should include any terms you think a developer stepping into this project should be aware of, but reasonably might not know.
ETF (Exchange Traded Fund): a collection of assets (stocks, bonds, etc.) that can be traded like a common stock, i.e. partial ownership in a corporation.
P/E Ratio (Price-Earnings Ratio): measures the amount of money traders are willing to pay for each dollar of earnings from a single share. It is equal to the stock price divided by the earnings per share.
PEG Ratio (Price-Earnings to Growth Ratio): It is similar to the P/E ratio, except that it takes the growth of the company into account. It is equal to the P/E ratio divided by the growth rate of the company.
Stop-Loss Order: an order to sell a stock if its price drops below a specified value
Your vellum file should also contain a section devoted to potentially confusing aspects of the app. You want to provide clarification on things like
- complex models or relations between models
- confusing or unusual model names
- distinctions between similar or closely-related models
- non-obvious relations between what a developer is dealing with on the backend versus what the user sees.
Bracketed Order Model: This model corresponds to bracketed orders. On the backend, it is a bracketed order, but for the user, it is a 'One-Cancels-the-Other Order'.
You should also take the time to justify non-obvious architectural decisions. This may include talking about design patterns you used:
We opted for using the Factory design pattern to handle the creation of order objects. Order types are quite similar and we want the client to be order-agnostic.
The final section should discuss the topics you would cover in a readme, including things like configuration, how to run the test suite, database creation and migration, services, and how to deploy the app, etc. I assume you've already written your fair share of readmes. If not, read this brief tutorial on how to write a kick ass readme.
You don't want to over-rely on your vellum file to the extent that you abandon good consulting and programming practices.
If you're walking into a codebase badly in need of a refactor, this should be conveyed to the client. In such a situation, a vellum is stopgap documentation, i.e. explanation that makes up for code that is not self-explanatory.
If you're writing the application from scratch, a vellum is used to elucidate complexity. It's not an excuse to write opaque code or inexact stories.
Certainly, not every project requires a vellum file or all of the parts of a vellum detailed here. Simpler applications will generally not require one as there isn't enough mental overhead to justify having this extra, rather involved document you have to maintain.
A good rule of thumb is that if you don't have to consult the vellum very often, then you can go ahead and delete it. Conversely, if you constantly find yourself checking your notes while coding, your notes likely contain information that should be extracted into some kind of documentation.
Of course, all of the topics I described could simply be appended to a readme file. The important thing is to remember to reduce confusion for yourself and subsequent developers.
* Vellum is a high-quality parchment made from the skin of calves
Acceptable Technical Debt