Rich Hickey had a very interesting talk a while back about breaking changes titled Spec-ulation that I highly recommend watching.

HN discussion: https://news.ycombinator.com/item?id=13085952

I think it was he who talked about maintaining old versions not by backporting bug fixes but instead by rewriting the old version to be a thin layer that gives you the interface of the old version upon the code of the new version. That, my friends, is how you can provide the old interface without the engineering overhead of maintaining billions of different versions of your codebase IMO.

It goes like this: First you have v1. Eventually you make v2. Now you write a translation layer that you replace v1 with that has the same interface as did v1 but which transforms arguments passed to it as necessary before passing them on to v2 interfaces and likewise transforms the result as necessary.

Now when you get to v3 you don’t rewrite v1, only v2. So requests to the v1 endpoint will go v1 -> v2 -> v3 and back. This means a bit of extra computational overhead but it will certainly be worth it in terms of the engineering overhead saved.

You can sort of do this in rust too, from a programming language point of view: https://github.com/dtolnay/semver-trick