Revisiting semantic versioning in Drupal Commerce

Developer Desk

Drupal modules are primarily maintained in Git repositories controlled by the Drupal Association on Drupal.org. Before the DA added support for semantic versioning in March 2020, our modules had version numbers like 7.x-1.0 or 8.x-2.1, with the first portion identifying Drupal API compatibility and the second identifying the major and minor version of the module.

However, with modern Drupal (Drupal 8+), a module can support multiple major versions so long as it does not depend on any deprecated APIs in Drupal, its dependencies, or PHP itself. Using the Drupal version number in module version numbers means many of our releases that support Drupal 9, 10, or 11 still start with 8.x-, never mind the fact that we don't support Drupal 8 at all any more.

Semantic versioning allows us to use branch names and tag releases for our various modules based on their own lifecycles. We don't need a new branch for each new major version of Drupal, and we can distribute more predictable updates to end users through Composer with major, minor, and patch versions of our modules.

When we first switched to semantic versioning, we created new branches based on the minor version. Thus, we went from an 8.x-2.x branch off of which we tagged releases to a 3.0.x branch. We never quite knew when to tag a patch versus a minor release in this scenario (i.e., 3.0.1 vs. 3.1.0) and created a situation where we'd have more branches to maintain than we really cared to (3.0.x, 3.1.x, 3.2.x, etc.).

We determined instead to start creating branches named only for the major version (3.x, 4.x, etc.) with release tags that still primarily use minor versions (3.0.0, 3.1.0, 3.2.0, etc.). This will permit us to focus on maintaining a single branch for each module until we need to introduce breaking changes while accommodating the occasional need to backport bug fixes to previous minor versions.

Backporting critical fixes

Thus, if we need to resolve a security issue impacting multiple minor versions of a module, we would apply the fix to a major version branch (2.x) to incorporate it into its next minor version release (2.2.0) and create new branches from any relevant minor version commit hashes to backport the fix into patch releases (2.0.1 and 2.1.1).

This approach allows developers to require our modules with version constraints that accept critical fixes through patch release updates vs. minor releases, which often have new features or potential UX changes they would need to review before deploying to production.

Updating existing modules

As we package new releases of our contributed modules, we will update the branch names in those projects until we achieve the same pattern across our entire ecosystem. We'd love for other contributors to do the same.

Essentially, if you have new commits in a 2.0.x branch that have not been packaged into a release yet, you would tag one final release (e.g., 2.0.3) then checkout a 2.x branch for subsequent development. From that branch you would then tag a 2.1.0, 2.2.0, etc. release as you make improvements to the module, using the strategy outlined above for backporting critical fixes to previous minor versions.

Thanks to Tom Ashe, Jakub Piasecki, and Jonathan Sacksick for their direction and implementation of this plan.

Add new comment