I haven't blogged for a while, and I knew that I needed to get back into it - but the articles I've been thinking of have all been bigger pieces based on work I haven't quite finished yet. So I've been putting it off. But something that happened this week finally gave me a reason to just write up something a little smaller - ease me back into it. And it's on versioning.
I've got several little projects that I maintain, one of them is a .NET Library for Slack. Slack released an update this week, allowing applications to support video content, and (as I have a notification when these changes are published) I was straight on it the same evening working on an update.
A new type of content is a relatively small change for my library; write a test, update a JSON converter, create the class, job done. Except this time there was an error - there was a property that every type of content has...but not video.
Okay - checked documentation, definitely not there. Videos won't be sending information back to the server under any conditions so I can understand why this is the case. But this is a common property for all types of content, which means I've baked it into the interface for content. Breaking change....
Apply the rules - SemVer
Wasn't worried at this point. I've made breaking changes before. All of my libraries apply SemVer - Semantic Versioning so that the users of the library can easily understand the impact of updating the library to a new version.
This change was going to break the way I implemented content types - so I have to update the major version, ensuring that anyone who relied on the interface property I was removing would be required to update their code to get it working again (changing anything that said "all content" to "all content except video").
No problem - major changes happen. I updated from 4.x.x to 5.0.0 and carried on. All good.
I tweeted the update and expressed why it was a major bump. Went to sleep, thought nothing of it.
Next day - the world woke up and I got a new Twitter follower. One of the developers of the feature I tweeted about got hold of me and explained that actually there was no breaking change, and that he'd see what was going on. A while later I got confirmation - no breaking change, just inaccurate docs. No major bump required!
So now what?
And this is why I wanted to blog about this. I've been using SemVer for years but I was in a position now where I'd made a major bump incorrectly. The content change could have just been a minor bump, added to 4.x.x and available to anyone without concern.
In my head - 5.x.x was a mistake, and I wanted to remove that mistake!
But my library has been out for a day, people will have seen the update and possibly started using it. I mean not many, but that doesn't matter. I'm a developer first and I'd hate it if I started being messed about with a library I relied on.
So I felt I had a couple of choices
This was immediately where my head went. Unlist the package, update 4.x.x with the video content change, pretend it never happened. The few people that downloaded it will wonder what the hell happened, but it's a small number...surely that can't be a problem?
And I thought about this option for a while - but the more I thought about it the more the small number worried me. What if I was a dependency on another package? What if they'd already updated and now their code said the latest version was 4, but they're on 5? Am I screwing up their versions? Will versioning math say there's a -1 version update? All sorts of weird scenarios in my head.
This was the clear reaction to my dilemma, and yet was also clearly not the right choice. Shame!
Maintain two versions of the library
The change I made was only minor, so I could keep 4 and 5 going until 6 came along, then deprecate 4. I thought about this briefly - but in the same way the previous option worried me about my users, this one worried me about myself.
Would I have to automate the syncing of the two versions? Would I accidentally miss something in one of them and actually end up with some changes only being in one version and not the other? What about my beta versions? Would I need two beta versions as well? Suddenly this felt like every change - especially a large minor bump (which happens pretty regularly) could be a lot more work and I'm just one guy...so this was do-able, but not really viable.
So what does that leave me with?
Own the mistake - express it in SemVer
I still dislike this option, despite it being the one I went with. But I dislike it because it left my mistake visible. No cleaning up
I broke the interface with my change. That was a breaking change. That is a major version bump. Forget the fact it was a mistake, forget the fact that the release notes show what I did. If I can only move forward with my version numbers, that's an accurate representation of what happened regardless of context.
Slack have been awesome; investigated, found the docs were inaccurate and informed me all within a day. So I now have to update the library to represent the new information given to me. This means I can add functionality to the library. It is not a breaking change, but a minor bump. Forget the fact that it didn't need to have the major before it. I am where I am. I minor bump the library.
I create v5.1.x - I move on. That's now where the library is (okay, 5.1.1 because I messed up the release notes).
I unlisted 5.0.0 because anyone wanting the new feature from 4.x.x should go straight to 5.1.x without the hassle, but I didn't deprecate it and I've ensured that anyone who updated the library in the 24hrs where it was in flux is still going to have a working update mechanism without any weird errors.
It's not about me
For me as a little maintainer this was a really interesting evening. I know I made the right choice because it came from "what's best for my users?" not "what's best for me?". I create rules in the libraries I create so that I can be consistent - because as a developer I dislike it when other libraries aren't consistent and you have to learn to deal with those inconsistencies.
This whole thing happened in about an hour - it wasn't like I agonised over the problems I had in my head for very long, but it just wasn't something I'd ever thought of before. And, as I'm sure I'll make mistakes in the future, next time I'll be able to work through it that much faster.