authors | last revised |
---|---|
Shon Feder | 2022-05-06 |
Table of Contents
- Summary (Overview)
- Context (Problem)
- Options (Alternatives)
- Solution (Decision)
- Consequences (Retrospection)
In the context of maintaining a changelog to communicate salient changes about
our software, facing the friction caused by frequent merge conflicts in our
UNRELEASED
changelog, we decided to find a lightweight, conflict free,
changelog process in order to achieve a smoother development process. We have
accepted the development cost and learning curve required by adopting a new
process.
We maintain a changelog
to make it easier for users and contributors to see precisely what notable changes have been made between each release (or version) of the project.
Any changeset that introduces observable changes to the behavior of our software
should include additions to the changelog. Currently, additions are made by
updating the UNRELEASED.md
file. The changes recorded there are then added to
the CHANGES.md
file during our automated release process.
This process creates a race condition over UNRELEASED.md
between any
concurrent pull requests. This consequently results in developers constantly
having to resolve merge conflicts. This busy-work slows down development and
adds no value.
We now enumerate and consider the various options we've conceived for addressing this problem.
With the exception of option (0), all of the following options would resolve the problem of merge conflicts and could be integrated into our existing automated deployment pipeline. However each option has different costs w/r/t dependencies, processes, and learning curve.
All of the following options will likely require ongoing maintenance, even option (0): this is because we already have a set of scripts that manage our changelog entries in order to support our fully automated releases. So this is not a space where we are introducing automation for the first time, but is instead a situation where we are changing an existing automated system.
We could do nothing. Developers would continue to resolve changelog conflicts manually.
- Requires no development time on a solution.
- Would require no additional maintenance.
- We'll continue having developer slow-down and busy work when merge conflicts arise.
There is a superabundance of tools for automatically generating changelogs. These tools extract changelogs from various different sources such as commit messages, issues, or pull requests.
- Requires no new development
- Would eliminate the need to add changelog entries in a separate file
- Developers have to learn the annotation format required by the tool, and they have to remember to deploy it in their commits/PRs/issues.
- Infrastructure maintainers have to learn how to configure and maintain the tool.
- We pickup a dependency external to both the company and our project
- Changelogs are meant to communicate a very specific kind of information to a
specific audience. Git commit messages, pull requests, and issues are for
communicating quite different information to an entirely different audience.
Requiring that these communication channels be overloaded risks degrading the
quality of communications in all the conflated channels.
- However, it's possible we could work out some conventions to add empty commits or designate special PR labels to work around this interference?
@thanethomson has written the unclog utility, which is used in both informalsystems/tendermint-rs and informalsystems/ibc-rs. The tool stores changelog entries in a directory structure, with each entry in its own file, ensuring merge conflicts can't arise.
Entries are added via the CLI. E.g.:
unclog add --id some-new-feature \
--issue 23 \
--section breaking-changes \
--message "Some *new* feature"
If the --messages
argument is omitted, it will open your configured editor for
authoring the message.
The tool has subcommands to generate and update the CHANGES.md
, and it supports
a variety of options via its TOML configuration.
- Requires no new development
- It is developed internally to informal
- Adds cognitive and procedural overhead to adding changelog entries
- Would add a rust dependency to our devenv
- Requires external contributors to either install a CLI tool or figure out a relatively complex file and folder structure to add changelog entries
If we wrote a custom git merge driver to work on the simple changelog format we could continue our current practice, and just fall back to the custom merge driver in case of merge conflicts.
- Allows us to keep our current process of simply updating a markdown file, with which, moreover, most devs are already familiar
- Could require external dependencies to be installed, depending on implementation.
- It's not possible to configure custom merge drivers for github, so we'd either need to develop a github action or bot to monitor for merge conflicts in the changelog and merge them automatically, or devs would be back to having to resolve merge conflicts locally.
We could author some simple tooling that would capture the core behavior of
unclog
, using files in directories, but neither require external dependencies
nor knowledge of a new CLI.
- No new tooling to learn
- No external dependencies to integrate
- Would require some development time (I estimate approx. 1 day, see the design sketch below for details on the implementation)
- Would require contributors to understand the folder structure
We have opted for option 4. Despite the added cost of development, we think the simplicity of use and maintenance will offset the development cost.
We will introduce a new directory tree to the project:
.unreleased/
├── breaking-changes/
├── bug-fixes/
├── documentation/
└── features/
Each change will be added as a single markdown file in the appropriate directory.
Two sbt
targets will be added to facilitate our continuous deployment:
releaseNotes
, merges the unreleased notes into a single new file of the expected format in ourtarget
directory. This can be used for upload to github releases.changeLog
updatesCHANGES.md
by prepending the output ofreleaseNotes
. It then removes all notes from the.unreleased
directory so that the next iteration of development starts from a clean slate.
TBD