You can have the world's most sophisticated software product, split into microservices, built with the best design patterns, using the most advanced AI running on all the latest and greatest hardware built on the most advanced technology nodes, but chances are that at some point — in fact, many times — something as blunt and trivial as the login button of your application will simply stop working.
That's a reality based on the fact that for every advanced algorithm or any secret sauce in any system, there are hundreds of thousands of the most mundane, even trivial details that all have to work, and have to work together for the whole thing to deliver — and that's at least just as hard to do correctly as the advanced, "cool" stuff.
So let's look at that very common situation as a motivating example. Every time it happens, it is very tempting, especially by force of personal and industry habit, to open yet another issue that says something like Login doesn't work, which is pretty much exactly what happens on most projects because that's sort of the accepted, unquestioned way of doing it. QA finds a bug, PM has another issue to put into circulation for a few weeks, and developers have something to fix. Everybody has work to do. Some type of progress is being made and reported. Life as usual.
And I guess there's nothing wrong with it if you're enjoying such a process or using it to keep the money flowing and economy going, like planned obsolescence of sorts, or perhaps more like the famous shovels vs spoons story. But there are also plenty of people who feel quite uncomfortable with the obvious inefficiencies here visible even to the naked eye. Just count all the duplicate issues in your current project.
That's a problem with issues: a bad standard at best.
Now, the situation with commit messages is even worse. Although there are some private attempts to standardize commit messages, the reality is that most projects do not follow any standard for commit messages. But if you think about it, they are one of the most important, if not the most important indicators of progress — which is what everyone wants to measure — for any project.
This is an unfortunate reality because not only is there enormous benefit in creating a good standard for both the issues as well as commit messages, but also, I believe, this has the potential to fundamentally improve the approach to what we loosely call "processes" in software development.
And while at first glance coming up with good standards for issues and commit messages may not sound like a big deal, I think it will not be an overstatement to say that even something as blunt as a standardized shipping container has probably done just as much good for our overall productivity and well-being as the highly sophisticated microchip.
Good standards are important. And we shouldn't underestimate the simple things. In fact, quite often it's precisely the simple things that are missing from making a project successful and a pleasure to work with.
So, at around 2020, I've started refining and formalizing a method for standardized issues & commit messages that I've been applying to my own projects like Deel and Kaizen. I call this method RAPID Practice because it is an integral part of the RAPID engineering methodology.
To me personally, ever since I switched to this method, it has been nothing short of liberating in terms of freeing up mental energy for the work proper. But standardizing the way we approach and describe issues and commit messages goes way beyond any personal comfort.
The most valuable result is actually that it generates a list of testable (whether manually or automatically) statements, or what I call test units (not to be confused with and to distinguish from unit tests), as a by-product (requiring almost zero additional work) of the main activity that grows in lockstep with the primary code, essentially creating code coverage. If we assume that an average size of a function from a C-family programming language is about 20 lines of code, a good coverage would be one where you have at least one test unit per 20 lines of code. This method delivers that.
Issues
Perhaps the biggest difference between RAPID issues and existing practice is that in RAPID, titles of "bug" issues should always be described not in terms of what's broken (i.e. the "actual result", as is usually done), but in terms of how things should be (i.e. their expected results). For example:
Ensure login works instead of Login doesn't work
This guarantees that all issue titles conform to a common standard, which has several important benefits:
1. First and foremost, this makes it seamless to turn all titles into (or at least treat them as) test units whose PASS / FAIL status tells exactly what happens. For example:
TEST PASS: Ensure login works
TEST FAIL: Ensure login works
Both of the texts above mean exactly what they say at face value and require zero extra mental parsing, as opposed to their mind-bending doulbe-negative alternatives like:
TEST PASS: Login doesn't work
TEST FAIL: Login doesn't work
Which, in addition, also require the viewer to open the corresponding issue in the issue tracker and find the expected result, i.e. what the test really checks, that can be anything.
2. Reduces interaction with an issue tracker since there’s no need to click in every time to see the most important line — the expected result. If you think about it, why would you hide the most important line behind an entire click? Again, it may not seem much, but when performed multiple times by multiple people over and over again (which it usually is), it adds up.
3. Deduplicates issues and their detailed descriptions:
Issues: Same-essence issues like Login button is not green, Login button is blue, Login button is red and so on for all the other wrong colors would never even be created and, therefore, never put through a heavy circulation in an issue tracker, wasting everybody's time. Instead, just once, usually at design or development phase, an issue is created with a title like Add login button (if created at design or development) or Ensure login button is as designed (if created by QA as a result of a bug), after which any time the color is not green, or there is a crash, or the button disappears altogether, the corresponding test unit fails, and the issue is simply reopened with maybe a comment that says "button is red" or, better yet, simply fixed and pushed up immediately. Over time, this also has a nice side effect that everything that ever went wrong with the log-in button is not spread across a myriad of related or duplicate issues but rather is accumulated under a single issue. This, too, reduces interaction with the issue tracker because it brings the number of duplicate issues close to zero.
Descriptions: Most of the time, the “expected result” becomes unnecessary in the detailed description (if you're doing QA, I bet you've thought why on earth you must repeat the title in the "actual result" every single time). Of course, sometimes there's a need to elaborate, which is fine.
4. Creates faster manual testing cycles because it’s easier to memorize (which, importantly, happens as a side effect) what needs to be tested if, for whatever reason, you haven't automated your tests.
5. When you scroll through issues, you only see positive (i.e. expected) scenarios — you see how things are expected to work instead of what is (or, when it's already fixed, what once, possibly a very long time ago, was) wrong.
Commits
Start all commit messages with a capital letter and describe them in imperative phrases with future tense verbs (just like issues). For example:
Make vars const instead of made vars const
Update lint rules instread of lint rule updates
Update docs instread of docs
This ensures that all commit messaging is consistent and conforms to the same standard as issue titles.
Always try to use non-technical descriptions so that they are understandable by the widest stakeholder audience, for example:
Center login button instead of Add padding
As a highly desirable side-effect, this essentially eliminates any perceived need for scheduled ceremonial meetings like daily stand-ups where everyone shares their progress since anyone can simply open the commit history at any time and read the progress for themselves. Many commits will naturally indicate an intensive coding phase, whereas fewer commits will indicate a design phase, maintenance phase or an impediment. Everything is integrated into the primary daily workflow, becoming automatic and, therefore, fast.
Avoid lumping together lots of changes into one big commit. Instead, break them down into smaller, coherent logical units (usually by staging them separately, like shown here for Visual Studio Code or here for Visual Studio, both using Git's interactive staging underneath), which guarantees two things:
1. Code diffs show changes that are actually relevant to the commit message title. As a result:
2. Anyone reading the commit history can have a more granular overview of the progress just by reading the titles (i.e. without delving into the actual diffs).
Therefore, all minor changes should be committed separately and described with at most three words, using common signs instead of words (i.e. +, -, &, etc.) to keep the messages short and still understandable. Avoid naming commits "Minor" — whether individually or collectively by combining all minor changes under an umbrella title. Instead, characterize them with more specific, short titles, ideally single-word, but up to three words is okay too. This way, the length of the commit message essentially encodes the magnitude of the change, so anyone glancing through the commit history can quickly find and focus on the essential changes, whose code diff will only contain the actual changes because smaller ones are committed separately.
For example:
Refactor - if the change only refactors code
Rhyme - if the change only adds code rhyme
Rename - if the change only includes renaming
Comment - if the change only includes comments
Doc - if the change only includes documentation
Const - if the change only includes adding const
Space - if the change is only in spaces with no rhyme included
Log - if the change only includes modification to a log message
Reuse - if the change simplifies the code by reusing existing code
UFT - if the change only includes modification to a user-facing text
Move - if the change only includes moving code from one location to another
Swap - if the change only includes swapping locations with symmetrical functionality (like function arguments)
Cleanup - if the change only includes removing unused/irrelevant/old/etc text (whether code or comments)
Simplify - if the change simplifies the code, for example reduces the number of lines of code, shortens a line, etc.
Minor - if the change cannot be characterized by a more specific word like the examples above
If necessary, use common signs (+, -, &, etc.) instead of words to keep the messages short and still understandable. For example: Rhyme & Rename
Commits that complement immediately prior commits (for example, when having forgotten something or to do a more granular logical split of a change) are a special frequent common case and should use the following messaging unless it's more natural to use Git's own amend feature:
PC+ - if something is added
PC* - if something is changed
PC- - if something is removed
Commits that complement older commits (for example, when refixing or undoing something) should use the following messaging:
[ISSUE-NUMBER / COMMIT-ID] [+/-/*] [optional description], for example #389* or a1e2345+
Commits that close an issue should all follow this exact scheme:
[ISSUE-TITLE] Fix [#N], for example: Stop spinner if image upload fails Fix #426
In other words, just copy the issue title exactly as it appears on top of GitHub into the commit message field and add "Fix" before the issue number at the end. This enables you to deal with all issue-related commits in a standard way, freeing up mental energy even further. The word "Fix" is chosen over the other alternatives like "Closes", "Close", etc. simply because they all do the same, but "Fix" is shorter.
Of course, these are just guidelines, but they've been tested over several projects with thousands of commits and hundreds of issues. As is always the case with RAPID, you can bend and adjust any rule in favor of obvious common sense and measurably better results in all the right circumstances specific to your project.
Pull Requests
Name all pull requests that contain only minor changes Groom.
Code
RAPID is agnostic when it comes to the coding standard except for the obvious, common sense recommendation of being consistent. However, it does have an important addition called code rhyme.
Code rhyme, or simply rhyme, is any alignment of code that emphasizes textual patterns. Any code is read much more often than it is written and rewritten, which in developer time over the years adds up to astronomical numbers. Therefore, it is absolutely essential to minimize the time required to mentally parse a piece of code in order to understand and work with it. Rhyming is one of the most important techniques toward this end. It allows for much easier spotting of bugs & inconsistencies, which ultimately translates to more efficient development, higher developer satisfaction due to faster progress with their work and, therefore, greater developer productivity. Here's a real-life example of code before and after rhyming:
Notice how hard it is to spot what's wrong in the first, unrhymed example. It requires special focus and more time to be identified, and certainly can't be picked up by casually glancing over it while working on something completely unrelated. All of that wasted time is entirely unnecessary and adds up significantly for code that is frequently worked on by many developers.
But if the code is rhymed as in the second picture, even a casual glance over it can easily catch the attention of the viewer that the code is misaligned (does not "rhyme") in a couple of places. In this case, in fact, both of the long logging lines have bugs: the dlog() call on the first line needs to be cdlog() and the [ObjectView] designator on the second line needs to be [ObjectDetails]. In real-life, the person who rhymes a piece of code is the original author himself, who is familiar with the context and will most likely notice any inconsistencies and fix them immediately. Therefore, no time is ever wasted by anyone working on it down the line.
Always Welcome Optimizations
At any time, anyone is welcome to contribute ideas and solutions that, while preserving or improving existing functionality, have a clear and measurably positive impact (the higher the impact, the higher the priority), including, but not limited to these common optimizations:
- Reduction of app / binary size
- Reduction of app / binary speed
- Reduction of assets / resources (like images) used by the app
- Reduction of the number of manual tests
- Reduction of communication (like database calls)
- Reduction of LOC (common sense - without loss of non-controversial and non-subjective readability)
- Reduction of static analysis warnings & errors as well as improving any other overall metrics