Following the principles below will help you become more risk aware by:
- Reviewing code in minutes instead of hours/days.
- Knowing which code reviews/commits require attention.
- Introducing fewer bugs on the most common refactorings.
- Becoming more aware of the risks that lead to bugs.
- Work in movements. Make smaller commits inside a movement (8-10 LOC).
- Merge commit should state the purpose of the overall movement/PR.
- Communicate risk with a one-letter prefix (e.g.
F - Added new feature).
- Increase risk sensitivity with bangs (e.g.
F!! - Added a potentially risky feature. Reviewer beware.)
- Use provable refactorings (e.g.
r - Extracted method)
|May Change Behavior||Won't Change Behavior||High Risk|
|F - Feature (< 9 LOC)||t - Test||F!! Feature (> 8 LOC)|
|B - Bug (< 9 LOC>)||d - Documentation||B!! Bug (> 8 LOC)|
|R - Test Supported Refactoring||a - Auto-tool||R!! Non-provable Refactoring|
|r - Provable Refactoring|
:::info Conventional commits
Yes, our team loves conventional commits, but found that they do not communicate risk
Accomplish each purpose with a sequence of micro-commits. Put a series of micro-commits on a branch, then merge them in with a commit that states the purpose you achieved.
Make safer commits by checking in smaller chunks of code per commit. 8-10 lines of code.
- Step 1: 3+ commit chain within 5m at least 1x day
- Step 2: 5+ commit chain within 3m at least 3x day
- Step 3: 10+ commit chain within 2m at least 8x day
Most of this value comes from increasing your focus. You can deeply focus on one tiny thing at a time, then come back up to re-gain your context and pick the next thing to do. Each commit allows you to finish one step, the branch provides your context, and the final merge commit allows you to verify that you completed your objective. You can enhance this effect by naming the branch after your intention and renaming it each time you learn something that shifts your intention.
These branches also allow you to make commits smaller while conveying intention more clearly. Smaller commits reduce your error rate, and both small commits and clear intention speed up code review. Now you can start each review by understanding the intention and then assess whether the steps make sense. You can also focus more on the ones that need individual review (risky or surprising steps).
Communicate the intention behind each change with your team. You will use a simple letter code in each commit message to indicate which one type of change you made in that commit.
Tagging each commit provides 3 distinct pieces of value:
- You write fewer bugs.
- You become more aware of risk.
- Code review is faster and more useful.
Every time you commit, start the commit message with one of these six letters followed by
space dash space.
F - Feature B - Bug R - Refactor t - Test d - Documentation a - Auto-tool
Just writing the risk codes provides significant value. It makes you stop to think about the intention of your commit (either before making the change or before committing). This will usually cause you to separate refactorings and test-only changes from bug fixes and features. Small commits that each complete one clear intention are less likely to have bugs than larger commits with multiple intentions.
Risk codes also provide value when you see your own commit log. Before it was not always obvious when you were taking on a small (or large) risk. Now each commit shows the risk, and your log shows how risky your coding approach is. Later shifts in this habit will build on this new awareness, helping you do something about the now-visible risks.
Finally, the codes help you quickly understand others’ code. Knowing the author’s intention allows you to assess each commit differently. You can have one mindset when checking a refactoring, another for a behavior change, and a third for a test-only change.
Use new tags to denote high risk commits. Start using F!!, B!!, and R!! to denote commits, unless you have evidence to prove you did it in a lower-risk way.
Tagging high-risk commits provides two important but higher-level sources of value:
- Make better choices about risk.
- See opportunities to increase safety.
Perform the following tasks when committing:
- Replace all
R!!unless you have strong test support and follow the written steps for that refactoring.
- Replace any
B‘s that have more than 8 lines of change with
- Any times you have to notate with a
X!!(high risk), consider alternatives that avoid the
There is only one space after a
X!! code, so your log lines up like this:
R - Extract Method F!! I should have refactored more R!! Edited a bunch trying to refactor
It might take you and your teammates time to really start using
X!! whenever it applies. However, even as you get started, each
X!! gives the team a chance to find and fix an obstacle. Something in your code, process, or skills makes it hard to do the lower-risk route, so the higher-risk option made sense in this case. Fix that obstacle so everyone will take lower-risk options in the future.
There will always be some
!! commits in the log, and there will always be some commits that are marked with a capital letter that should be !!. Both of these provide ongoing opportunities for your team to make its practice and environment safer.
Perform safe and provable refactoring. Use provable
r refactorings instead of test-supported
R or unprovable
Using provable refactorings makes refactoring safe:
- Refactor without writing bugs.
- Get untestable code under test without writing bugs.
R commits, and try a safe refactoring
- Recipe Option: Extract Method, Rename, others, or extend the catalog.
- Tool Option: JetBrains IDEs and plugins, Visual Studio, or some others.
In addition to solving this critical problem, provable refactorings are the foundation of Read by Refactoring. Read by Refactoring is a technique for reading code by first safely refactoring it to make it easy to understand, and then reading the result. This works because you can prove that your refactorings are safe even though you don’t understand the code you are refactoring.
The provable refactorings fundamentally prevent bugs, allowing an entirely different approach to coding. Right now we will use that to get code under test without introducing bugs in the process.
Future habits will apply provable refactoring to address a wide variety to architectural and design problems. This shift familiarizes you with a widely-applicable technique.