`
158 | 1. Make it pass (Green)
159 | 1. Commit with `. f` or `. F`.
160 | 1. Refactor.
161 | 1. Commit each refactoring.
162 | - If the refactoring is executed with a known safe tool or recipe, use `. r`.
163 | - If this is new, un-called code and you have been doing TDD since the start, you probably have the test coverage to use `^ r`.
164 | - If you are "triangulating", converting special-case code to a generalized algorithm, use may need to use `@ r`.
165 |
166 | ### Example commit history
167 |
168 | ```
169 | ^ r replace algorithm
170 | ^ F FizzBuzz of 3 is "Fizz"
171 | ^ F FizzBuzz of 2 is "2"
172 | . r rename parameter
173 | ^ F FizzBuzz of 1 is "1"
174 | ```
175 |
176 | # Alternatives
177 |
178 | ## All-at-once
179 |
180 | If working as an ensemble with a highly skilled and capable technical coach, then bring in all of the practices at once and let the coach guide the team to use them as the work requires.
181 |
182 | ## Safety First
183 |
184 | Start by introducing `. r` and suggesting incremental work in a branch. Allow these kinds of changes a fast track through review and release. Build from there.
185 |
186 | ## Categorize first
187 |
188 | This approach scales up to larger multi-team organizations where there's not enough hands-on coaching capacity to do it all at once.
189 |
190 | Make the requirement that all changes must be categorized in to one of Feature (`@ F`), Bug Fix (`@ B`), or Refactoring (`@ r`) and tagged as such. No other change to developer behavior is required. `@ @` is an escape valve, e.g. for checkpointing work to move it to another machine.
191 |
192 | If a change contains both feature and bug fix work, use `@ F`.
193 |
194 | If a change contains both refactoring and non-refactoring work, use the appropriate non-refactoring tag.
195 |
196 | Invite developers/pairs/ensembles to use other risk and intent annotations as they see fit, and praise when that is done, but don't require it at first.
197 |
198 | Roll out in these increments:
199 |
200 | 1. Categorize (`@ F` / `@ B` / `@ r` / `@ @`)
201 | 2. Corral Risk (`! F`, `! B` / `! r`)
202 | 3. Validated (`^ F` / `^ B` / `^ r`)
203 | 4. Safe (`. r`)
204 |
--------------------------------------------------------------------------------
/Migrating from V1 to V2.md:
--------------------------------------------------------------------------------
1 | # Migrating from V1 to V2
2 |
3 | Where `x`/`X` is an intention code:
4 |
5 | | V1 | V2 |
6 | |--------|-------|
7 | | `x - ` | `. X` |
8 | | `X - ` | `^ X` |
9 | | `X!!` | `! X` |
10 | | `X**` | `@ X` |
11 |
12 | ## Notes
13 |
14 | - We now split risk and intention into 2 different characters.
15 | - The new system uses consistent casing for the intention.
16 | * The old system's lower case becomes `.` and upper case becomes `^` (or higher).
17 | - Risk is always before intention
18 | - Risk is a single character
19 | - `*` becomes `@`
20 | - There are no more doubled characters, so we avoid shell interactions (old `!!`) and the appearance of cursing (old `**`).
21 |
--------------------------------------------------------------------------------
/Movement Branches.md:
--------------------------------------------------------------------------------
1 | # Movement-Oriented Branches
2 |
3 | This branch model is designed to clarify intention and speed up code review. It also improves safety by separating goals.
4 |
5 | Consider our code history like a piece of classical music. It is long, but there are themes. It explores one idea for a period of time, then moves on to the next idea. These ideas are called movements in classical music. In code, we convey them using branches.
6 |
7 | We convey movements by using the following behaviors.
8 |
9 | 1. Make a new branch for each idea. Keep the ideas small.
10 | 2. Use the Risk-Aware Commit Notation on the branch.
11 | 3. Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) in the merge commit. Convey the core idea for the movement.
12 |
13 | There are also optional behaviors:
14 |
15 | * (optional) Indicate risky commits in the merge commit, using a `Risky changes:` commit trailer.
16 | * (optional) Use rebase + a no-fast-forward merge, to make the side branch visible in history
17 |
18 | ## Example
19 |
20 | Commit history looking at main with side branches collapsed (`git log --oneline --merges --first-parent` to only show merge commits):
21 | ```
22 | feat: authenticate with multiple social auth providers.
23 | feat: calculate sales tax using third-party service
24 | chore: update dependencies
25 | refactor: isolate payment processing from checkout flow
26 | ```
27 |
28 | Merge commit details:
29 | ```
30 | feat: authenticate with multiple social auth providers.
31 |
32 | Authorization now prompts the user in two phases. First we ask for
33 | their username or email, which we use to find the right provider.
34 | Then we send them through that provider's auth mechanism.
35 |
36 | Risky changes:
37 | a5de73: ! r Switched algorithm for email categorization.
38 | e67c8e: ! F Split login UX into 2 steps.
39 | ```
40 |
41 | Commit history for a side branch (`git log --oneline --graph`):
42 | ```
43 | * feat: authenticate with multiple social auth providers.
44 | | \
45 | | * ^ F Add Github and Facebook OAuth providers
46 | | * ^ F Add Microsft OAuth provider
47 | | * ^ F Allow / require the user to choose which provider to use when creating a new account - still only one option.
48 | | * . r convert 1 to many-of-one for providers
49 | | * ! r Switched algorithm for email categorization
50 | | * . t add tests for email categorization
51 | | * . r extract method
52 | | * . r rename
53 | | * . r extract method
54 | | * ! F Split login UX into 2 steps.
55 | | * . t test account creation failure
56 | | * . t test account creation success
57 | | * . t test login failure
58 | | * . t test login success
59 | | * . f LocalMemoryAuthenticator allows easy testing for code that uses auth providers (simulator)
60 | | * . t extract commonalities to test providers through an interface
61 | | * . t test the default provider (Google). Add these to the platform test suite.
62 | | * . r extract method; move method
63 | | * . r extract method; move method
64 | | * . r introduce parameter object (oauth provider)
65 | | * . r extract method
66 | | * . r extract method
67 | |/
68 | * feat: calculate sales tax using third-party service
69 | ...
70 | ```
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Risk-Aware Commit Notation
2 |
3 | (aka Arlo's Commit Notation)
4 |
5 | This commit notation allows developers to convey 2 critical pieces of metadata about each commit:
6 |
7 | 1. How risky is it? What has the original author done to mitigate risk?
8 | 2. What was the intention? When the original author changed the code, what was s/he attempting to accomplish?
9 |
10 | This information is conveyed in the first 3 characters of the commit summary line. That way a receiving developer can quickly scan the commit log in order to determine risk and intent for any incoming change set.
11 |
12 | This is particularly useful when:
13 |
14 | 1. As a reviewer, deciding whether to approve a pull request
15 | 2. As a developer, getting your pull request approved faster and more easily
16 | 4. Reading `main` — just the pull request commit summaries to understand the history of changes for a release.
17 |
18 | ## The Four Risk Levels
19 |
20 | We divide all behaviors of the system into 3 sets. The change is intended to alter the *Intended Change* while not altering any of the *Invariants*. The *Risk Levels* are based on correctness guarantees: which invariants can this commit guarantee did not change, and can this commit guarantee that it changed the intended change in the way the authors intended?
21 |
22 | | Risk Level | Code | Example | Meaning | Correctness Guarantees |
23 | |-----------------------|------|--------------------------------------------|----------------------------------------|-------------------------------------------------------|
24 | | **(Proven) Safe** | `.` | `. r Extract method` | Addresses all known and unknown risks. | Intended Change, Known Invariants, Unknown Invariants |
25 | | **Validated** | `^` | `^ r Extract method` | Addresses all known risks. | Intended Change, Known Invariants |
26 | | **Risky** | `!` | `! r Extract method` | Some known risks remain unverified. | Intended Change |
27 | | **(Probably) Broken** | `@` | `@ r Start extracting method with no name` | No risk attestation. | |
28 |
29 | Behavior categories:
30 |
31 | * **Intended Change:** The 0 or 1 behavior change intended in the commit. Could be verified by one test assertion. By default, a commit with more than 1 behavior change cannot be represented at any risk level better than *Probably Broken*.
32 | * **Known Invariants:** All behaviors known to the development team at the time the change was made. Automated tests can greatly increase the size of this set, thus enhancing safety when commits are at a risk level that guarantees correctness for Known Invariants. However, this set also includes behaviors that are known but not tested.
33 | * **Unknown Invariants:** All behaviors not known to the development team at the time the change was made, including behaviors that were once known but have been forgotten. These behaviors are guaranteed to be untested and untestable, as the development team does not know they exist.
34 |
35 | Risk levels:
36 |
37 | * **Safe:** Developer performed the task in a way that prevents all potential risks, even to invariants that developer is not aware of.
38 | * **Validated:** Developer performed the task in some way that includes validation for the intended change and all invarants the developer thought of. The most common technique is developer-written automated tests.
39 | * **Risky:** Developer is aware of risks and attempted to mitigate them as much as possible, but only the intended change is formally verified. Commonly this includes a manual change that the developer could not fully verify.
40 | * **Broken:** Either known to be broken, or developer couldn't even check to see if it works. May not compile. Used when the developer cannot see the results of the work without checking in, or as a savepoint when the developer is about to switch tasks or direction.
41 |
42 | ## Core Intentions
43 |
44 | These developer intentions exist on every project. They are always allowed in commits that use this notation.
45 |
46 | Each intention can appear at any of the 4 risk levels. Each intention's full details section includes the potential risks inherent in that kind of change, as well as common approaches to attain each risk level.
47 |
48 | | Prefix | Name | Intention |
49 | |------------|---------------|-------------------------------------------------------------------------------------------|
50 | | `F` or `f` | Feature | Change or extend one aspect of program behavior without altering others. |
51 | | `B` or `b` | Bugfix | Repair one existing, undesirable program behavior without altering any others. |
52 | | `R` or `r` | Refactoring | Change implementation without changing program behavior. |
53 | | `D` or `d` | Documentation | Change something which communicates to team members and does not impact program behavior. |
54 |
55 | ### Casing
56 |
57 | Each intention may be expressed in UPPERCASE or lowercase. The team uses this distinction to mean roughly "pay more attention to the ones in uppercase". Teams commonly use this for one of 2 different meanings.
58 |
59 | * **Intended behavior change**. UPPERCASE means the commit intended to change 1 behavior; lowercase means it intended to change 0 behaviors. With this approach, functionality changes would always be `F`, bugfixes `B`, refactorings `r`, and documentation `d`.
60 | * **User-visibility**. UPPERCASE means the commit altered something user-visible. It should appear in the changelog or other documentation (perhaps in summarized for combined with other uppercase changes); lowercase means the behavior change is entirely internal. With this approach all letters can have both forms. Even a refactoring could be `R` if, for example, it adds to an internal API and you want to communicate that to other development teams.
61 |
62 | ### Feature or Functionality
63 |
64 | **Intended Change:** 1 behavior. Could be described by a single unit test assertion.
65 |
66 | **Known Risks**
67 |
68 | * May alter unrelated feature (spooky action at a distance).
69 | * May alter a piece of this feature that you intended to remain unchanged.
70 | * May implement the intended change in a way different than intended.
71 |
72 | | Code | Known Approaches |
73 | |-------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
74 | | `. F` | Meets all criteria for `^ F` and developers are the only users of the feature. For example, extends build tooling for your own build or adds debug logging. |
75 | | `^ F` | Meets all of:- Change is <= 8 LoC[5]
- Feature was fully unit tested prior to this change.
- Change includes new or changed unit tests to match intended behavior alteration.
|
76 | | `! F` | Change includes unit tests for new behavior. |
77 | | `@ F` | No automatic tests, or unfinished implementation. |
78 |
79 | ### Bugfix
80 |
81 | A bugfix is a lot like a feature. However, the intention is to change an undesired — and usually unintentional — behavior of the current system. The risk profile is similar but the intention is different, so there are often more operational risks.
82 |
83 | **Intended Change:** 1 behavior. Could be described by a single unit test assertion.
84 |
85 | **Known Risks**
86 |
87 | * Intended change may have unintended consequences in the market. For example, customers may be depending on the bug.
88 | * May alter unrelated feature (spooky action at a distance).
89 | * May alter a piece of this feature that you intended to remain unchanged.
90 | * May implement the intended change in a way different than intended.
91 |
92 | | Code | Known Approaches |
93 | |-------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
94 | | `. B` | Meets all criteria for `^ B` and developers are the only users of the changed functionality. For example, fixes build tooling for your own build or corrects debug logging format. |
95 | | `^ B` | Meets all of:- Reviewed current and new behavior with customer representative.
- Change is <= 8 LoC[5]
- Bug's original (buggy) behavior was captured in a unit test prior to this change.
- Change includes 1 changed unit test, matching intended behavior alteration.
|
96 | | `! B` | Change includes unit tests for new behavior. |
97 | | `@ B` | No automatic tests, or unfinished implementation. |
98 |
99 | ### Refactoring or Remodeling
100 |
101 | A Refactoring or Remodeling intends to alter the program in some way without changing any behavior. The risk levels indicate the probability of the commit living up to that intention, based on how the code change was executed.
102 |
103 | **Intended Change:** 0 runtime behaviors; 1 code structure.
104 |
105 | **Known Risks**
106 |
107 | * May cause a bug.
108 | * May fix a bug.
109 | * May change a behavior in a way that doesn't impact a user.
110 | * May force a test update.
111 |
112 | | Code | Known Approaches |
113 | |-------|----------------------------------------------------------------------------------------------------------------------------------------------------|
114 | | `. r` | One of: - Provable refactoring[2]
- Test-supported Procedural Refactoring[3] entirely within test code
|
115 | | `^ r` | Test-supported Procedural Refactoring[3] |
116 | | `! r` | Identified single, named refactoring, but executed by editing code or without whole-project test coverage. |
117 | | `@ r` | Remodeled by editing code, even in small chunks. |
118 |
119 | ### Documentation
120 |
121 | **Intended Change:** 0 behaviors.
122 |
123 | Changes that don't impact the code, but do change documentation around the code. The team should decide whether end-user documentation changes are `D` or `F`[1].
124 |
125 | **Known Risks**
126 |
127 | * May mislead future developers.
128 | * May mislead other stakeholders.
129 | * May alter team processes in ways that have unintended consequences.
130 |
131 | | Code | Known Approaches in source files | Known Approaches in other files |
132 | |-------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
133 | | `. d` | Developer-visible documentation verified to generate byte-identical compilation. | Any developer-visible documentation that does not change a process |
134 | | `^ d` | Verified by running tests, or things like changing text on a dev-only screen. | Dev-impacting only, but changes compilation or process. E.g. changes code-review checklist. |
135 | | `! d` | Verified only by compiling and launching the application. | Alters an important process. |
136 | | `@ d` | Not verified. | Trying out a process change that is intended to gain info, not to necessarily work. |
137 |
138 | ## Extension Intentions
139 |
140 | The basic intention annotations are comprehensive to describe any kind of change, but it may be useful to extend the notation to your project to provide additional detail that is useful in your context. Read more about [Extension Intensions](Extension%20Intentions.md).
141 |
142 | # Provable Refactorings
143 | [2]:#provable-refactorings
144 |
145 | If you can get a series of commits that is all lowercase commits, you can deploy without the need for Regression Testing or lengthy conversations about accepting the pull request to trunk.
146 |
147 | A provable refactoring requires a burden of proof. The main methods of proof are
148 | * automated refactoring via tool, with knowledge of tool bugs.
149 | * scripted manual refactoring, using the compiler to verify each step. [Recipes Here](https://github.com/InnovatingTeams/provable-refactorings)
150 | * use formal methods to prove the before state and after state are equal.
151 |
152 | With discipline these can prove bug-for-bug compatibility. They demonstrate safety for unknown bugs, even guaranteeing that you do not accidentally fix a bug you don't know exists (but your customers may be depending on).
153 |
154 | All of these recipes use static analysis to demonstrate safety. As such, they work equally well on code that lacks tests. They can be a good way to make code testable. Their downside is that they are language-specific.
155 |
156 | # Test-supported Procedural Refactorings
157 | [3]:#test-supported-procedural-refactorings
158 |
159 | These are refactorings with a lower standard of proof:
160 | 1. Commit contains only a single refactoring.
161 | 2. Refactoring is named and published (e.g., in [Fowler's refactoring catalog](https://refactoring.com/catalog/)).
162 | 3. Either:
163 | 1. Your entire product is very highly tested, or
164 | 2. you are working on new code that is not yet called.
165 | 4. You followed the published steps, including running full-suite test runs when indicated.
166 |
167 | Note that this cannot prove bug-for-bug compatibility. It can only demonstrate that you didn't cause any problems that have been thought of before; it does not demonstrate safety for novel bugs.
168 |
169 | Requirement 3 is there because many refactorings can have non-local effects. It is not sufficient to have great tests on the code you are changing. You also need great tests on the code that you are not intending to change, to demonstrate that you didn't. Therefore, until your entire codebase is very highly tested, you will only be able to use the `.` risk level on new code that is uncalled by your product.
170 |
171 | # End-User Documentation
172 | [1]:#end-user-documentation
173 |
174 | Changing end user documentation changes behaviors, so can be considered a feature, bugfix, or refactoring, depending on its nature. Most teams use those codes (including levels of risk) accordingly.
175 |
176 | However, teams that use case to distinguish user-visible and user-invisible behavior changes may wish to use `D` for end user documentation changes instead. This loses the ability to see the full intention of the change, but highlights that it was focused on documentation. Do whatever is most expressive for that commit.
177 |
178 | # Small Features and Bug Fixes
179 | [4]:#small-features-and-bug-fixes
180 |
181 | Features and bug fixes intentionally change behavior. This makes them much riskier than refactorings. It is not possible to prove that they have only the intended effect. However, small changes are much lower risk for three reasons:
182 |
183 | 1. It's easy to see the possible side effects of small chunks of code.
184 | 2. It's easy to code review, so you are likely to get good reviews.
185 | 3. It's only possible when the code is well-organized already.
186 |
187 | Therefore, we treat any feature or bug fix as high risk if it changes more than 8 lines of code in one commit. This includes test changes.
188 |
189 | One good approach to enable small features is to refactor until the feature change is easy. Then add the feature one piece at a time, with a test for each.
190 |
191 | # Tool Support
192 | ## Git CLI
193 | Git allows you to specify a user-specific commit message template that shows up
194 | when you run `git commit`. [`.gitmessage`](./.gitmessage) contains a plaintext
195 | version of this document formatted for use as a git commit template. You can use
196 | it by following these steps:
197 |
198 | 1. Download `.gitmessage`
199 | 2. Move `.gitmessage` to your home directory (`mv path/to/your/Downloads/.gitmessage ~/.gitmessage`)
200 | 3. Configure `git` to look for this message: `git config --global commit.template ~/.gitmessage`
201 | 4. Try it out -- in any git repo, make some change, stage it, then run `git
202 | commit`. You'll know it's working if you see the contents of `.gitmessage` in
203 | your commit message editor.
204 |
205 | Note that you won't see this template when you are `--amend`ing a commit, when
206 | you provide `-m` to `git commit`, during interactive rebases, and any other time
207 | where the commit message already exists or is provided by another tool.
208 |
209 | # Living Documentation
210 |
211 | We invite you to submit pull requests to help evolve this notation and methodology.
212 |
213 | LoC: Lines of Code
214 |
215 | [5]:#lines-of-code
216 |
--------------------------------------------------------------------------------
/v1/Extension Intentions.md:
--------------------------------------------------------------------------------
1 | ## Extension Intentions
2 |
3 | Each project can define a set of extension intentions. Each project should define which extension codes it uses. It is up to each project to define the approaches for each of the 4 risk levels.
4 |
5 | These are some common intentions, each used in several projects. Each also lists alternatives used in projects that don't use the code.
6 |
7 | | Prefix | Name | Intention | Alternatives |
8 | | --- | --- | --- | --- |
9 | | M | Merge | Merge branches | Use `F`, `B`, or `R`, based on the main intention of the branch, with risk level based on maximum for any individual commit in the branch. Optionally leave blank for merge from main to a feature branch. |
10 | | T | Test-only | Alter automated tests without altering functionality. May include code-generating code that just throws a `NotImplementedException` or similar approaches. | Use `f` or `b`, depending on which kind of work this test is going to validate. Use `r` if this is a refactoring purely within test code. It is a lower-case letter unless you also change product code. |
11 | | E | Environment | Environment (non-code) changes that affect development setup, and other tooling changes that don't affect program behavior (e.g. linting) | Consider the environment to be a product where the users are team members, and code it accordingly. |
12 | | A | Auto | Automatic formatting, code generation, or similar tasks. | Use the intention that matches the reason you are performing the action, almost-certainly as a lower-case level of risk. For example, code cleanup would be `r`, and generating code to make a test for a new feature compile would be `t` or `f`. |
13 | | C | Comment | Changes comments only. Does not include comments that are visible to doc-generation tools. | Use `D`. |
14 | | P | Process | Changes some team process or working agreement. | Any of: - Use a tacit, informal process.
- Use `D`.
- Keep your process definition outside of source control.
|
15 | | S | Spec | Changes the spec or design. Used when team does formal specs or design reviews and keeps all such documents in the main product source, perhaps in the product code itself. | Any of: - Use informal specs.
- Use `D`.
- Use your test suite as your only spec.
- Keep your spec / design outside of source control.
|
16 | | * | Unknown / multiple | Made a bunch of changes and are just getting it checked in. No real way to validate safety, and may not even compile. Usually used at the highest risk level (`***`). | Don't allow this. Require each commit to do exactly one intention and document itself accordingly. |
17 |
--------------------------------------------------------------------------------
/v1/Learning Path.md:
--------------------------------------------------------------------------------
1 | # Incremental learning and adoption path
2 |
3 | For a team that has never tried disciplined refactoring there is a steep learning curve to adopt this system.
4 |
5 | To reduce that challenge, here we describe the tiniest increments to learning and adopting Arlo's Commit Notation. This way you can get used to one idea before getting overwhelmed by the next idea, and get a quicker return on the learning investment.
6 |
7 | Expect some disagreement and confusion in the team throughout this process, as people shift their thinking. As you find agreement, write down your new norms in your team agreements. Give feedback to Arlo's Commit Notation about how it could be been clearer for you.
8 |
9 | Hint: this all goes much more smoothly with Mob/Ensemble Programming ([Promiscuious Pairing](https://csis.pace.edu/~grossman/dcs/XR4-PromiscuousPairing.pdf) is also pretty good) to share knowledge, increase "insights per hour", and shift norms more quickly.
10 |
11 | Hint: a good technical coach can be a huge help for your team to learn and adopt these skills.
12 |
13 | ## 1. Working in a short-lived branch
14 |
15 | While Trunk-Based Development directly in `main` is good for keeping everyone's work in sync and reducing merges, a short-lived branch lets you document the steps in your development process, telling a story to reviewers and future readers of your changes. 1 day is a good maximum lifetime for a branch (shorter is better), enabling incremental commits without incurring most of the risks of long-lived branches.
16 |
17 | If you're in a context where code review is part of the flow of work, teach reviewers how to look at the individual branch commits as an option that may be easier than viewing the whole change all at once. Mention this option in the description of the final Merge Request, e.g. "see the individual commits in the branch for more details".
18 |
19 | Depending on your VCS solution, you will need to find a way to keep these details visible after merging to main, such as `rebase` + `merge --no-ff`.
20 |
21 | ### Example commit history
22 |
23 | ```
24 | Implement automatic log-off
25 | Clean up the login module
26 | ```
27 |
28 | One benefit of working this way is that it's easier to provide rich, detailed descriptions for the smaller increments in a branch than for the whole ball of changes.
29 |
30 | In legacy code, we often see code that looks "weird" but we don't know if was deliberately made this way for a subtle reason, or the dev just didn't get around to cleaning it up. Incremental work with rich descriptions can be really helpful for future readers trying to understand why the code ended up like this.
31 |
32 | ## 2. Tag refactorings with `R**`
33 |
34 | If a change contains only refactoring, indicate that by prefixing the change description with `R** `.
35 |
36 | The refactoring need not be especially disciplined. This is just about separating refactoring from non-refactoring in your commit history.
37 |
38 | Folks on your team probably have [multiple working defintions of refactoring](https://jay.bazuzi.com/DefinitionsOfRefactoring/), and this is an invitation to examine those defintions and find a shared understanding.
39 |
40 | Refactorings often have a large diff even though they don't change behavior and are out of proportion with the conceptual size of the change. For example "rename A to B" is one small idea but every line that references `A` will be affected. If combined with deliberate behavior changes that will make reading the total diff difficult. Separating refactorings into their own commits will make code review easier. You should be able to scan the commit history and easily see which are refactorings and which are not.
41 |
42 | ### Example commit history
43 |
44 | ```
45 | Implement automatic log-off
46 | R** Remove duplication in login module
47 | R** Rename a bunch of stuff for clarity
48 | ```
49 |
50 | For a future reader of the code trying to understand how the code got this way, if you're looking for a deliberate behavior change you know you can ignore `R**` changes; if you see a behavior change in a commit marked `R**` you know it was accidental.
51 |
52 | ## 3. Pick up easy wins
53 |
54 | Once the team has learned the above practices, the following tags are easy to adopt.
55 |
56 | - `t` for test-only changes
57 | - `a` for auto-formatting
58 | - `R!!` for named refactorings
59 |
60 | ### Example commit history
61 |
62 | ```
63 | t - fill in missing tests for existing login code
64 | R!! Extract Method
65 | a - autoformat with prettier
66 | ```
67 |
68 | If you haven't already, this is a good time to add automatic code formatting to your CI checks, and bring all existing code in to compliance with that formatting.
69 |
70 | ## 4. Tag Features and Bug Fixes with `F**` and `B**`.
71 |
72 | Make a team agreement to categorize commits and tag with one of the above.
73 |
74 | It may be a good idea to allow `***` (uncategorized) in certain contexts. One example is "checkpointing", where you have some in-progress experimental changes on one machine and want to try them on another machine or share them with a coworker.
75 |
76 | At this point you can considering adopting a regex check to ensure that all commits are tagged. See https://github.com/RefactoringCombos/ArlosCommitNotation/issues/29.
77 |
78 | ### Example commit history
79 |
80 | ```
81 | F** Implement automatic log-off
82 | R** Remove duplication in login module
83 | R** Rename a bunch of stuff for clarity
84 | ```
85 |
86 | ## 5. Refactor to prepare code for behavior change
87 |
88 | > Make the change easy (warning: this may be hard), then make the easy change. -- Kent Beck
89 |
90 | For each feature or bug fix, practice replacing "how can I get this behavior change to fit in to the current design?" with "what design would make it really easy to implement this feature in a natural way?" and "what small refactorings would it take to get from here to there?". You're looking for `R!!`-style, single named refactorings. Commit histories will start to look like a series of refactorings followed by a deliberate behavior change.
91 |
92 | ### Example commit history
93 |
94 | ```
95 | F** Automatically log-off when idle
96 | R!! Introduce parameter
97 | R!! Import nodatime
98 | R!! Merge duplicate code
99 | R!! Extract Method
100 | ```
101 |
102 | This also gives you the option of merging to `main` before your feature or bug fix is complete. For example, on one day you might do a bunch of `R!!` refactorings, get them approved and merged quickly thanks to the above practices, then continue towards your feature or bug fix the next day.
103 |
104 | ## 6. Safe Refactoring
105 |
106 | Can your IDE safely execute a refactoring? If so, you can use `r`. This is great for code review, as reviewers can now skim some changes instead of examining them carefully for correctness issues. This means means you can get your code review results back even more quickly.
107 |
108 | Where your IDE is unable to provide the required level of safety, look at [recipe-based refactorings](https://github.com/InnovatingTeams/provable-refactorings).
109 |
110 | Once your team has gotten comfortable with `r`, consider changing your code review and delivery protocols to allow lower-case (safe) changes to skip some/most/all of those requirements. This has several benefits, including creating an incentive for developers to work in this safe way.
111 |
112 | Note that this level of safety is hard to get in dynamic languages. If that's your context, you may need to instead make the investment in comprehensive test coverage to unlock `R`.
113 |
114 | ### Example commit history
115 |
116 | ```
117 | r - Merge identical methods
118 | r - Rename local variables
119 | r - Extract Method
120 | ```
121 |
122 | ## 7. Small features and bug fixes
123 |
124 | Once you get in familiar with refactoring in preparation for a feature, you can further reduce risk by refactoring to the point where a feature or bug fix only requires a small code change. That unlocks `F` and `B`.
125 |
126 | ### Example commit history
127 |
128 | ```
129 |
130 | F - Automatically log-off when idle
131 | r -
132 | t -
133 | r -
134 | r -
135 | R!!
136 | r -
137 | t -
138 | r -
139 | R!!
140 | r - Merge duplicate code
141 | r - Extract Method
142 | ```
143 |
144 | # TDD with ACN
145 |
146 | In a strict Test-Driven Development cycle almost all commits are either a new test or a refactoring. When using ACN:
147 |
148 | 1. Write a new failing test (Red)
149 | 1. Make it pass (Green)
150 | 1. Commit with `F - `.
151 | 1. Refactor.
152 | 1. Commit each refactoring.
153 | - If the refactoring is executed with a known safe tool or recipe, use `r`.
154 | - If this is new, un-called code and you have been doing TDD since the start, you probably have the test coverage to use `R`.
155 | - If you are "triangulating", converting special-case code to a generalized algorithm, use may need to use `R**`.
156 |
157 | ### Example commit history
158 |
159 | ```
160 | R - replace algorithm
161 | F - FizzBuzz of 3 is "Fizz"
162 | F - FizzBuzz of 2 is "2"
163 | r - rename parameter
164 | F - FizzBuzz of 1 is "1"
165 | ```
166 |
167 | # Alternatives
168 |
169 | ## All-at-once
170 |
171 | If working as an ensemble with a highly skilled and capable technical coach, then bring in all of the practices at once and let the coach guide the team to use them as the work requires.
172 |
173 | ## Categorize first
174 |
175 | This approach scales up to larger multi-team organizations where there's not enough hands-on coaching capacity to do it all at once.
176 |
177 | Make the requirement that all changes must be categorized in to one of Feature (`F**`), Bug Fix (`B**`), or Refactoring (`R**`) and tagged as such. No other change to developer behavior is required. `***` is an escape valve, e.g. for checkpointing work to move it to another machine.
178 |
179 | If a change contains both feature and bug fix work, use `F**`.
180 |
181 | If a change contains both refactoring and non-refactoring work, use the appropriate non-refactoring tag.
182 |
183 | Invite developers/pairs/ensembles to use other risk and intent annotations as they see fit, and praise when that is done, but don't require it at first.
184 |
185 | Roll out in these increments:
186 |
187 | 1. Categorize (`F**` / `B**` / `R**` / `***`)
188 | 2. Corral Risk (`F!!`, `B!!` / `R!!`)
189 | 3. Validated (`F` / `B` / `R`)
190 | 4. Safe (`f` / `b` / `r`)
191 |
192 | ## Safety First
193 |
194 | Start by introducing `r` an suggesting incremental work in a branch. Allow these kinds of changes a fast track through review and release. Build from there.
195 |
--------------------------------------------------------------------------------
/v1/index.md:
--------------------------------------------------------------------------------
1 | # Arlo's Commit Notation
2 |
3 | This commit notation allows developers to convey 2 critical pieces of metadata about each commit:
4 |
5 | 1. How risky is it? What has the original author done to mitigate risk?
6 | 2. What was the intention? When the original author changed the code, what was s/he attempting to accomplish?
7 |
8 | This information is conveyed in the first 3 characters of the commit summary line. That way a receiving developer can quickly scan the commit log in order to determine risk and intent for any incoming change set.
9 |
10 | This is particularly useful when:
11 |
12 | 1. As a reviewer, deciding whether to approve a pull request
13 | 2. As a developer, getting your pull request approved faster and more easily
14 | 4. Reading `main` — just the pull request commit summaries to understand the history of changes for a release.
15 |
16 | ## The Four Risk Levels
17 |
18 | | Risk Level | Code | Example | Meaning |
19 | | --- | --- | --- | --- |
20 | | **Known safe** | lowercase letter | `r - Extract method Applesauce` | Addresses all known and unknown risks. |
21 | | **Validated** | uppercase letter | `R - Extract method Applesauce` | Addresses all known risks. |
22 | | **Risky** | uppercase followed by 2 bangs | `R!! Extract method Applesauce` | Known risks remain unverified. |
23 | | **(Probably) Broken** | uppercase followed by 2 stars | `R** Start extracting method with no name` | No risk attestation. |
24 |
25 | * **Known safe:** Developer performed the task in a way that prevents the potential risks, even for situations that developer is not aware of.
26 | * **Validated:** Developer performed the task in some way that includes validation for all risks the developer thought of. The most common technique is developer-written automated tests.
27 | * **Risky:** Developer is aware of risks and attempted to mitigate them as much as possible, but there is no formal verification. Commonly this includes a manual change that the developer could not fully verify.
28 | * **Broken:** Either known to be broken, or developer couldn't even check to see if it works. May not compile. Used when the developer cannot see the results of the work without checking in, or as a savepoint when the developer is about to switch tasks or direction.
29 |
30 | ## Core Intentions
31 |
32 | These developer intentions exist on every project. They are always allowed in commits that use this notation.
33 |
34 | Each intention can appear at any of the 4 risk levels. Each intention's full details section includes the potential risks inherent in that kind of change, as well as common approaches to attain each risk level.
35 |
36 | | Prefix | Name | Intention |
37 | | --- | --- | --- |
38 | | F | Feature | Change or extend one aspect of program behavior without altering others. |
39 | | B | Bugfix | Repair one existing, undesirable program behavior without altering any others. |
40 | | R | Refactoring | Change implementation without changing program behavior. |
41 | | D | Documentation | Change something which communicates to team members and does not impact program behavior. |
42 |
43 | ### Feature
44 |
45 | **Known Risks**
46 |
47 | * May alter unrelated feature (spooky action at a distance).
48 | * May alter a piece of this feature that you intended to remain unchanged.
49 | * May implement the intended change in a way different than intended.
50 |
51 | | Code | Known Approaches |
52 | | --- | --- |
53 | | `f - ` | None known |
54 | | `F - ` | Meets all of:- Change is <= 8 LoC[5]
- Feature was fully unit tested prior to this change.
- Change includes new or changed unit tests to match intended behavior alteration.
|
55 | | `F!!` | Change includes unit tests for new behavior. |
56 | | `F**` | No automatic tests, or unfinished implementation. |
57 |
58 | ### Bugfix
59 |
60 | A bugfix is a lot like a feature. However, the intention is to change an undesired — and usually unintentional — behavior of the current system. The risk profile is similar but the intention is different, so there are often more operational risks.
61 |
62 | **Known Risks**
63 |
64 | * Intended change may have unintended consequences in the market. For example, customers may be depending on the bug.
65 | * May alter unrelated feature (spooky action at a distance).
66 | * May alter a piece of this feature that you intended to remain unchanged.
67 | * May implement the intended change in a way different than intended.
68 |
69 | | Code | Known Approaches |
70 | | --- | --- |
71 | | `b - ` | None known |
72 | | `B - ` | Meets all of:- Reviewed current and new behavior with customer representative.
- Change is <= 8 LoC[5]
- Bug's original (buggy) behavior was captured in a unit test prior to this change.
- Change includes 1 changed unit test, matching intended behavior alteration.
|
73 | | `B!!` | Change includes unit tests for new behavior. |
74 | | `B**` | No automatic tests, or unfinished implementation. |
75 |
76 | ### Refactoring or Remodeling
77 |
78 | A Refactoring or Remodeling intends to alter the program in some way without changing any behavior. The risk levels indicate the probability of the commit living up to that intention, based on how the code change was executed.
79 |
80 | **Known Risks**
81 |
82 | * May cause a bug.
83 | * May fix a bug.
84 | * May change a behavior in a way that doesn't impact a user.
85 | * May force a test update.
86 |
87 | | Code | Known Approaches |
88 | | --- | --- |
89 | | `r - ` | One of: - Provable refactoring[2]
- Test-supported Procedural Refactoring[3] entirely within test code
|
90 | | `R - ` | Test-supported Procedural Refactoring[3] |
91 | | `R!!` | Identified single, named refactoring, but executed by editing code or without whole-project test coverage. |
92 | | `R**` | Remodeled by editing code, even in small chunks. |
93 |
94 | ### Documentation
95 |
96 | Changes that don't impact the code, but do change documentation around the code. Note that this does not include end-user documentation[1].
97 |
98 | **Known Risks**
99 |
100 | * May mislead future developers.
101 | * May mislead other stakeholders.
102 | * May alter team processes in ways that have unintended consequences.
103 |
104 | | Code | Known Approaches |
105 | | --- | --- |
106 | | `d - ` | Developer-visible documentation, not in a source file, or verified to generate byte-identical compilation. |
107 | | `D - ` | Dev-impacting only, but changes compilation or process. E.g., changing text on a dev-only screen, or changes code-review checklist. |
108 | | `D!!` | Alters an important process. |
109 | | `D**` | Trying out a process change that is intended to gain info, not to work. |
110 |
111 | ## Extension Intentions
112 |
113 | The basic intention annotations are comprehensive to describe any kind of change, but it may be useful to extend the notation to your project to provide additional detail that is useful in your context. Read more about [Extension Intensions](Extension%20Intentions.md).
114 |
115 | # Provable Refactorings
116 | [2]:#provable-refactorings
117 |
118 | If you can get a series of commits that is all lowercase commits, you can deploy without the need for Regression Testing, or lengthy conversations about accepting the pull request to trunk.
119 |
120 | A provable refactoring requires a burden of proof. The main methods of proof are
121 | * automated refactoring via tool, with knowledge of tool bugs.
122 | * Scripted manual refactoring, using the compiler to verify each step. [Recipes Here](https://github.com/InnovatingTeams/provable-refactorings)
123 |
124 | With discipline these can prove bug-for-bug compatibility. They demonstrate safety for unknown bugs, even guaranteeing that you do not accidentally fix a bug you don't know exists (but your customers may be depending on).
125 |
126 | All of these recipes use static analysis to demonstrate safety. As such, they work equally well on code that lacks tests. They can be a good way to make code testable. Their downside is that they are language-specific.
127 |
128 | # Test-supported Procedural Refactorings
129 | [3]:#test-supported-procedural-refactorings
130 |
131 | These are refactorings with a lower standard of proof:
132 | 1. Commit contains only a single refactoring.
133 | 2. Refactoring is named and published (e.g., in [Fowler's refactoring catalog](https://refactoring.com/catalog/)).
134 | 3. Either:
135 | 1. Your entire product is very highly tested, or
136 | 2. you are working on new code that is not yet called.
137 | 4. You followed the published steps, including running full-suite test runs when indicated.
138 |
139 | Note that this can not prove bug-for-bug compatibility. It can only demonstrate that you didn't cause any problems that have been thought of before; it does not demonstrate safety for novel bugs.
140 |
141 | Requirement 3 is there because many refactorings can have non-local effects. It is not sufficient to have great tests on the code you are changing. You also need great tests on the code that you are not intending to change, to demonstrate that you didn't. Therefore, until your entire codebase is very highly tested, you will only be able to use the `R` commit designation on new code that is uncalled by your product.
142 |
143 | # End-User Documentation
144 | [1]:#end-user-documentation
145 |
146 | End user documentation is a feature, bugfix, or refactoring, depending on its nature. Use those codes (including levels of risk) accordingly.
147 |
148 | # Small Features and Bug Fixes
149 | [4]:#small-features-and-bug-fixes
150 |
151 | Features and bug fixes intentionally change behavior. This makes them much riskier than refactorings. It is not possible to prove that they have only the intended effect. However, small changes are much lower risk for three reasons:
152 |
153 | 1. It's only possible when the code is well-organized already.
154 | 2. It's easy to see the possible side effects of small chunks of code.
155 | 3. It's easy to code review, so you are likely to get good reviews.
156 |
157 | Therefore, we treat any feature or bug fix as high risk if it changes more than 8 lines of code in one commit. This includes test changes.
158 |
159 | One good approach to enable small features is to refactor until the feature change is easy, then add it. Then add the feature one piece at a time, with a test for each.
160 |
161 | # Living Documentation
162 |
163 | We invite you to submit pull requests to help evolve this notation and methodology.
164 |
165 | LoC: Lines of Code
166 |
167 | [5]:#lines-of-code
168 |
--------------------------------------------------------------------------------