But when it comes to software version numbers, the current leader in version numbering schemes is SemVer (or Semantic Versioning). Don't be fooled, though! Many people claim to know how SemVer works, but have never read the specification. Since this is a critical piece of what we are about to talk about, here is a summary of the spec:
Version numbers take the form X.Y.Z
, sometimes augmented with additional pre-release and build information: X.Y.Z-AAA#BBB
. And each of those fields means something well defined and specific.
SemVer Summary
X
is the major number. Changes in this indicate breaking changes to the API (and/or behavior).Y
is the minor number. Changes to this number indicate that new features were added, but that no APIs are broken as a result.Z
is the patch version. Changes to this indicate that internal changes were made, but that no changes (even compatible changes) were made to the API.
Countless projects use a format that looks like SemVer, but many of them ignore the semantics behind the version number. Often, it seems that version numbers are incremented by "gut feel" instead of any consistent semantic: "This feels like a minor version update."
Why use versioning
Version numbers help your users understand something about the nature of the changes they can expect. If you don't follow a pattern, they are left guessing. And this frustrates people.Following SemVer introduces rigor on two fronts:
- It sends clear signals to users about the depth of changes they can expect in a release.
- It sends a clear signal to your developers about what is, and what is not, allowed when it comes to changing the code.
I cannot understate the importance of (2). SemVer helps us impose self-discipline, which in turn minimizes both internal and external disruption.
Reorganizing, Refactoring, and Renaming
If you reorganize the package structure of your public API, or if you do a major renaming, or if you choose to change the methods/structs/classes/etc of your public API, you must increment the major version number.Such changes mean that anyone who's using your code will experience breakage.
It's okay to make internal changes that don't touch any public API items. So minor internal-only refactoring can be done in minor, and even patch, releases (though we don't recommend doing it in patch releases).
So in effect, the following are not be be changed except during major updates:
- Package structure
- Public class, struct, enum, trait, interface, etc. names, nor the names of any of the items on these
- Constants or public variable names or values
- Function/method names
- Function/method signatures for existing functions except where the change is additive and the added argument is optional. Return value types and exceptions must also not change.
Introducing New Features
Minor versions may introduce new features, but features must be introduced without breaking existing APIs.Features are additive in nature: They bring new things, but do not modify or delete existing things.
Deprecating
- Mark a thing as deprecated as soon as it is considered deprecated, even if that is a patch or minor release. Deprecation, after all, is a warning condition, not an error condition.
- Do not change the behavior of the deprecated thing during minor or patch releases
- Remove deprecated things only at major version changes. Until that time, you're still on the hook for supporting them.
Deprecation is a signal that in the future a thing will be removed. But it is not an excuse to change, delete, or ignore the functionality of that bit of code outside of the SemVer constraints.
Errors and Exceptions
Do not make casual changes to what exceptions are thrown and when.Bugs and Security Fixes: How To Handle Real Life
When the real world comes crashing in, we make exceptions. But professional software developers make them wisely and carefully.The important concept here is the minimally invasive change. That is, when patching bugs or security releases, we may need to change the API, but we should do it by changing the absolute minimum number of things we can get away with. And we do that even if it means sacrificing our "architectural purity".
Conclusion
The professional software developer has long-term usability and stability as a goal. Yes, well-architected code is important. But there is a time and place for making that your focus. And maintenance releases (minor and patch versions) are not an occasion to refactor, re-organize, or make sweeping modifications.Be conscientious about how much effort the users of your code put into using your code. I can tell you from experience what we do when the maintenance burden you impose on us gets wearying: We stop using your tools (or we fork them).
SemVer is a communications tool. But to use it well, we must use it accurately. And that means writing code focused on stability.
0 comments:
Post a Comment