Dealing with "breaking changes" when evolving a Software

Hello all :slight_smile:

I would like to eventually launch a discussion which might go beyong BabylonJS, which is why I post in Off topic … Feel free to share your valuable experience :slight_smile:


I use to develop a lot of stuff on my free time, and I often struggle with the “breaking change” issue. Basically I feel like when it comes to updating a software or API (for example adding a new feature or correcting an old one) we often have the choice between :

  • Inserting a breaking change (and potentially break the tool for unknown N users)
  • Inserting (or keeping) useless code complexity for the only sake of not going “breaking change” for a few people

My last example in date is my project WebGPU AI Life :

  • I added a way to “save” a SEED by generating a custom URL
  • I added a custom URL parser to retreive a seed

Problem : Later on I added the ability to choose the number of families (from 1 to 8). So I had to edit my seed parser. But since it had already shared on tweeter, and people was already sharing to each other some seeds on the V1.0, I had to “trick” my new seed parser so that old seeds (not specifying the number of families, at the time fixed to 4) also work on the new server version

I call it a “problem” because just for the few people who might continue sharing to each other the obsolete seed URL, I’m keeping a “dirty” version of the code with lines I would love to get rid of, just to avoid breaking change.


And I guess I’m not the only one since I have seen already several times, after a feature request or modification request, @Deltakosh , @RaananW or @Evgeni_Popov (or others…) saying something like “Yeah but that would be a breaking change”. Which I’m totaly fine with, don’t worry :slight_smile:


But sometimes I feel like due to this, maybe a software is not “as good as it could be”. Because we are not perfect, and cannot think about all issues and usages by advance, and by a matter of fact, we often come to the conclusion that this or this function would make “more sense” to be called in another way, or be part of another class, or etc, etc…


How do you guys deal with this ?
I would say when it comes to a public software like BabylonJS, it’s generally agreed (I won’t speak about Blender :grinning: ) to use the version naming convention MAJOR.MINOR.PATCH with :

  • MAJOR = breaking change
  • MINOR = new feature
  • PATCH = debug

But I don’t know if it’s the case for BabylonJS… Do you guys follow this rule ? I think that following this rule also means tracking the “to be changed on next MAJOR” code, so exactly the list of little code patches which are known as “dirty”, remaining only for not breaking, and would be removed/fixed on next major, etc, etc…

We try to follow that rule:) but honestly my bar to accept a breaking change is really high as the pain on thousands of developer life vs having to accept some imperfections is most of the time not really worth a discussion:)

1 Like

It might (must ? :grin: ) be necessary to run such a big project :stuck_out_tongue:

To be frank, not everyone likes it (as is always the case with humans ;)), and it takes some convincing. But having been on the other side of the fence, losing countless hours dealing with APIs or services that screw over their users by not respecting backward compatibility, I can tell you I feel very strongly about that core principle.

Fair enough :slight_smile:

I can imagine ^^

To be clear : I’m ok with this. My goal with this discussion is not to relaunch any debate on BJS side, but more about calling for experience share on the subject. My event with updating the AI Life project reminded me about this, and I felt like “Aggh. If I’d thought of that before, I wouldn’t have had the problem.”.

I think developping a software, keeping in mind it will be updated in the future, and also putting in place a structure so that it can evolve without colliding with retrocompat, is almost “Art” :grin:

2 Likes

I’m a difficult consumer of the Babylon.js API, since I generate my library from the typings files on Babylon.js. So, things will “break” for me that don’t even really break the API (like moving a file or changing a constructor argument name). I wish that Babylon.js followed the @since more as decorators. That will make it easier for consumers to decide if we want to make a change without digging through code. One breaking change recently was that SceneLoader.ImportMesh stopped returning the name of the loader used (and void instead, so it was a compile time break). I switched over to ImportMeshAsync, but I had no idea in which version it was introduced. For web tooling we specify something like “top 1%” and then maybe stop worrying about the rest (ie: can i use %). In my case I have a peer dependency on Babylon.js >= 5.0. I strive to support that far back - in reality I know build tooling will fail from new Babylon.js additions. I didn’t take the time to see if that went so far back to see if ImportMeshAsync existed in 5.0. It likely did - maybe somebody using 5.0 will add an issue in my repo! :smile:

In that case I was still good with both the breaking change and the deprecated ImportMesh. We need progress and to trade off improvements with changes not anticipated when the original API was designed. In your case with the seed - you have added backwards compatibility. You don’t necessarily clean up all deprecated stuff in a major version, because that would be a major pain for end users. I used to design APIs with thousands of users, so we used versioning and sunset old versions on a schedule, but that doesn’t match the same with Babylon.js - although we have CSG2, Audio V2, etc - that direction I like.

Most of the answers in computer science start with “It depends”. I’d put this in that category and opinions will vary.

2 Likes

Yeah, at the end I think that’s the most common way to go. AWS, Google, X, all of them would update the entire API with a V2 ou V3 version, adding a “v2” string in the URL requests, keeping it running along side the original one ^^

That’s something we can do more! This is a great idea honestly!

2 Likes

Not to be too ceremonial, but would be nice to see as a contributing guideline perhaps.
Originally I posted it here 2020!! ( @since tag versioning - Questions - Babylon.js)

edit: just want to add that it’s helpful for new features as well, so people know min version to upgrade to get new feature.

1 Like

But there you have an example for the full consequences of neverminding breaking changes.

If you do breaking changes, most important thing is a standardised and transparent process of doing it. Like annoucement, deprecation warnings, giving it some time (!). Not like: “Hey we have just introduced a breaking change, hope you don’t mind”…

For the recipients of the breaking change, the change means extra work (or stopping to upgrade if it exceeds the benefits).

1 Like

Here is my take :slight_smile:

I’ll tell you what I find not 100% correct with this system (in general). Most open source projects (and Babylon included) will “abandon” past major branches once a new one was released. This is somewhat acceptable, but that means tht if you want to always be up to date you have to sustain back-compat issues. Every time you upgrade major frameworks, you know that it won’t run as smoothly as you expect it to be, and you also know that, unless there will be a major bug or security issue, the current major branch is no more.

So, major.minor.patch is a wonderful way of notifying your users what is expected from a certain release, but it also allows the developer (again, me included, i am not criticizing “others” :slight_smile: ) to move forward without thinking of the past.

For sure. I agree 100% with you. But a good piece of software that is usable and upgradable is sometimes more important that the perfect framework that is impossible to upgrade. There must be something in the middle!

2 Likes

I had missed this article, thanks ! :slight_smile: