If you browse GitHub issues on popular projects long enough you’ll find a healthy argument about versioning eventually. A few years ago the topic got so hot that someone posted detailed rules for software version numbering called Semantic Versioning. A few years later someone else came up with a pretty good system they called Monotonic Versioning. Whenever I init a new npm module it always defaults my version to 1.0.0. That got me thinking about the silliness of some of these arguments over how to properly assign version numbers to your code so I decided I’d throw my (very small, humble, and insignificant) hat into the ring and come up with a common sense versioning system that I’m calling “Common Versioning”.
Common Versioning borrows a lot of ideas from both Semver and Monotonic Versioning. Most of those ideas were either common sense to begin with or were already being used by developers and the authors of these documents just codified what we were already doing. So here’s my system.
Common Versioning v1.0.0
The number one issue with version numbers is knowing when a new version of a dependency has breaking changes.
So we all know the X.Y.Z versioning pattern where:
- X = Major version
- Y = minor version
- Z = Patch
Okay so that all makes sense. From this you can assume that any time a major release number is bumped there are breaking changes. So what’s the issue? Why does there’s even need to be a document codifying these rules? There are edge cases. That’s why.
Let’s say you’re using Plugin v1.0.1 and it changes to v1.1.0. That means some feature was added but it doesn’t break anything it depends on. But let’s say there’s a bug, documented or not, and your app relies on Plugin’s bug and they fix it. Now you’re screwed when you go to update. Or maybe you’ve extended the code and a minor update adds exactly the feature you extended and the author of Plugin adds exactly the same method name you are using. Now you’re screwed again.
But here’s the thing – these are problems you should be seeing a mile away. So if you fall into these edge cases then yeah, you probably need some directions on how versioning works. Here’s how common sense Versioning works:
- Major versions contain breaking changes
- Minor versions add functionality in a backwards compatible way.
- Patch versions are bug fixes
Here’s where we flip the script on the more well known versioning schemes.
Major versions still contain breaking changes but the addition of new functionality in large batches (several new methods at a time) can also constitute a major version update. You need to feel it. Sometimes when you question what kind of change you’re making you just need to go with your gut feeling.
Minor versions add new functionality that a major version doesn’t already have. It fixes bugs in a backward compatible way. If users of your software don’t know about a new feature and it’s not important enough (maybe it’s rarely used or obscure) for a major version release then why make a big fuss and have everyone upgrade to a version they may not need?
Again, these are bug fixes. If your users are using undocumented or unexpected behavior in your module then a major version update may be the best way to go as this isn’t something you want to surprise your users with. Sure, they may not supposed to be using flaws in your software but if you know they are and fixing those bugs would cause compatibility issues then a major version release may be the best way to alert them to breaking changes.
The most important rule
Always document changes. Document all major, minor, and patch versions of your package in a change log that’s released with each version of your app. The change log should explain what’s changed why you decided that the change is considered major, minor, or patch.
It might seem simple and obvious to some. To others it may sound like bullshit. Either way, versioning is important but not so important that it calls for bike shedding. My point here is to use your best judgement as a software developer. For users of software, make sure you test the features of the modules you use and read the developer’s changelog. If they don’t provide one, read the commit messages. If those don’t exist, then maybe you don’t want to be using that package. Often times when I worry about the stability of a package or that it could be abandoned I’ll fork it myself, merging in changes as needed, and keeping my own version of the package safely stored so nothing can go wrong and we can always roll things back.