Major releases are disasters, and should be avoided whenever possible

If you use semantic versioning in your project — and you should — then you fix bugs in patch releases (e.g. going from v2.4.6 to v2.4.7), and add new features in minor releases (e.g. from v2.4.6 to v2.5.0). These are both unambiguously good things to do: downstream projects that use your project can happily and blindly upgrade to your new versions knowing that everything is compatible and nothing will break.

But when you issue a new major release (e.g. from v2.4.6 to v 3.0.0), that’s because you made an incompatible change. Now the maintainers of downsteam packages have to stop and think and read your release notes before they can be confident whether it’s safe to upgrade, or whether (as with all the various React-related libraries’ major versions) they’re going to have to rewrite their code first. Most often, they won’t have the time or energy to do this for all the many dependencies their project has.

That’s bad, because it means either (if you are diligent) you have to take on the responsibility of maintaining bugfix releases in multiple major versions; or (if you are not) you just leave users of the previous major version high and dry, with no security fixes.

In semantic versioning, minor releases are where you add functionality; major releases are where you take functionality away (or change it, which is the same thing, because you’re taking away the old functionality to provide new). Or, in short, major releases are where you break things.

Breaking things is bad. Don’t do that. Every major release you have to make should be considered a wretched defeat — a failure to maintain the backwards compatibility that you owe your users. The perfect project would stay on version 1.x.y forever.


11 responses to “Major releases are disasters, and should be avoided whenever possible

  1. > The perfect project would stay on version 1.x.y forever

    Sure, the perfect project chooses the perfect design, architecture and API on the first try, and never needs to change. In the imperfect real world the odds of that happening aren’t great (effectively zero for big or complex stuff). Carrying the burden of sub-optimal decisions forward has it’s own cost.

    If semantic versioning is followed, release notes are available, and migration instructions are provided for major version transitions, then your project is doing alright as far as I’m concerned.

  2. The perfect project doesn’t have to choose the perfect design and API right out of the gate; it just needs to move from its initial version to the perfect version in a series of backwards-compatible steps. (Which may involve getting a lot of the architecture right from the day 1. But that’s the work.)

  3. Oh, and to clarify: my point here is not that no project should ever release a version 2. I do realise that that is sometimes necessary. My point is that this is an occasion for mourning, not celebration. We should think of it like a death in the family rather than a birth.

  4. Would other upgrades, such as performance or visual enhancement, be a minor release, a patch release, or would you just include it with the next regular minor or patch release?

  5. David Starner

    PHP had mysql_escape_string, but it was realized that to properly escape a string, you also had to pass the database connection to it. So instead of replacing it, they added mysql_real_escape_string. So all the existing code continued to work and didn’t have to address their SQL injection holes. Success?

    From another perspective, that which does not change, dies. You can use Perl 5 as long as you want, but it seems likely the support for it was going to dwindle off with or without Perl 6. You can take the time to move to a revamped version or not, but simply not changing the system may well not be an option.

    Lastly, if you are HP, you can spend the $650 million a year it took to keep the Itanium on life support. For a tiny fraction of that, HP will support your system designed to work on HP Unix on the Itanium. But if you’re not willing to shell out the money to keep what you need supported supported, that’s nobody’s problem but yours.

    I’m not arguing that systems shouldn’t be reasonably stable. But if you want really stable, many businesses are paying IBM to run their 1960s-era software in a pile of emulators so they don’t have to change anything. You don’t want an endless stream of “bug fixes” destabilizing things, changing things your system might depend on. This idea that you can reap all the advantages of change and none of the downsides, especially without incentivizing the developers to walk that thin line, seems unrealistic.

  6. Tagore Smith

    Yeah, this is one of the things that just drives me nuts about the Javascript ecosystem. I’d go so far as to say that instead of breaking your API you might want to make an entirely new library, Looking at you, React router.

    I’m about to release a new commercial product that has an API we named the “predeprecated” API. Like literally in Python you have to import from predeprecated to use it- you have been warned.

    That said, I don’t plan on breaking its contract for a decade or so. I just plan on eventually providing a better API that you really ought to use instead. I would be terribly ashamed if I broke callers once I’d let code into the wild, even if I named it “predeprecated..”

  7. Ron, performance enhancements are backwards compatible, so they can be minor (or even patch-level) releases. Visual enhancement is a really interesting variant case. When we’re talking about user-facing apps, the usual semantic versioning approach doesn’t quite apply, as the software is not (or not only) about consumption by higher-level software that is consuming a defined API. You could make a case that, for user-facing software, the UI is the relevant interface, and that therefore any changes to the UI that “break” the user experience should be punished by being made major releases. But in practice, if it’s a small change (like moving “Import” from the File menu to the Image menu), that’s something people can quickly adapt to and the fuss and bother of a major release may not really be warranted.

  8. David, your mysql_escape_string example is possibly a good one (I don’t know enough about it to know for sure). If they had to remove the original functionality because it was a security hole (as opposed to because it contained a security hole, which they should have fixed) then, sure, that’s the way it had to be.

    I don’t really buy the “that which does not change, dies” thing, though — as we can see from the continued existence and relevance of FORTRAN (1957) and LISP (1958) and arguably COBOL (1959). That all of these continue to find uses is testimony to the If It Ain’t Broke Don’t Fix It principle, which a lot of software designers could stand to learn from — especially those who are less than half the age of those languages!

    Yes, most programmers will eventually move to a newer and more powerful platform — just as I am on my own fourth Language Of Choice (BASIC -> C -> Perl 5 -> ES6). But that’s not an argument for making backwards-incompatible changes to BASIC, C or Perl 5.

  9. Finally, Tagore, yes it is indeed the JavaScript ecosystem I have in mind — which seems to suffer from this syndrome much more painfully than other programming environments I’ve used. Notably, I don’t think I have ever had a library-version problem in Perl. Admittedly this is in part because JS programs lean more heavily on libraries than Perl programs do, but I don’t think that’s enough to explain the discrepancy.

    Meanwhile, I predict that in 20 years we will all get to enjoy the irony of your “predeprecated” library continuing to work just fine in backwards-compatible minor releases :-)

  10. David Starner

    I understand why you wouldn’t like non-backward compatible changes in libraries, but I don’t think any of your examples of languages support your case; between Fortran 66 and Fortran 77, there were massive incompatible changes, such as a complete change in string syntax. I don’t know what you mean by backwards incompatible changes to BASIC; compatible to which BASIC?

    Is there an advantage to having a bunch of Python developers start working on Boa instead of Python 3? Boa would probably be less compatible with Python 2. Programmers working on these systems want to work on the newer and more powerful platforms just like the programmers using them. Therein lies my problem with the broadest statement; a lot of times, the line is going to be between making changes with some backward compatibility, or taking the chance to rewrite everything from the bottom up.

  11. You make a fair point that languages like FORTRAN and BASIC have changed a great deal in the sixty or so years that they’ve been around. But then they have the excuse of having been around from the very start of computer science, before any of the lessons about backwards compatibility had been learned. I think it’s true that FORTRAN 77 is still FORTRAN 77, 42 years on. I know that Perl programs I wrote in 2000 still run just fine now — unlike programs written in certain other languages, they have not “decayed” as libraries have changed in careless ways. It can be done substantially right — but it takes a certain mindset, and that mindset does not seem to be fashionable.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.