The Enterprise

Backward compatibility

Michael Pankov •

I often come across a software engineering practice of retaining as much backward compatibility as possible. This strikes me as a poor decision, leading to a lot of problems, which I'll try to describe in this post.

First, let's introduce some context. I work as a system programmer, the team is mostly (but not totally) located in one office and is around 2 dozens of people. There's local issue tracker, but not a lot of formalized planning — to the point that the tasks are often only set informally in a talk between a supervisor and an implementor, so features in development are often invisible for others. We also do code-review, but it's essentially "commit-wise". The project is moving out of research stage into production.

So, what sort of backward compatibility am I talking about? It's the one when a developer implements a replacement feature because of change of requirements. The existing feature is clearly going to be deprecated. And the developer hesitates to deprecate it and wants to leave the old feature in place as well. They don't make an atomic change of replacement of old feature with the new one — they rather add one more way to do things.

What does it mean in the long term?

That the old feature may be not deprecated at all ever — because of priority shift, lack of time, reorganization of team. The initial implementor may get sick, or go to a vacation, and forget about removing the old feature on return. Even if it's in the issue tracker — because issue tracker doesn't bother you. You may have tens and hundreds of tasks there and still not care. The old feature may get finally removed, say, in a year — a great time-span, in which it can cause a lot of confusion and trouble.

And by confusion I mean the following. If the feature isn't deprecated — that means essentially removed, at the moment a replacement is ready — you have to support both options. Someone may build their work off the old feature — because they may not know about replacement at all. It gets even worse with growth of team and geographical distribution of developers — considering little formalized planning, there's no way to know that the old feature is meant to be deprecated, when both options are present.

Moreover, it's much more likely that the developers know how to work with old feature and will be lazy to move their code to use the new one. To them, it will look like the old feature is going to be here for a while and they may rely on it. And this will bite them at the very last moment — because the new feature was implemented exactly to mitigate some problems, about which this particular developer, who wants to use the old feature, may be not aware of. Not knowing about these problems, a developer sticks with the compatibility — and right before shipment it turns out this old, compatible feature stopped working. It was known to the implementor of replacement feature that it will stop working at some time, that's why the replacement feature was implemented. But the user of the feature didn't know it. And they might not use the old feature, if it weren't there — because they use what's there. And out of general laziness and common sense — why care, if a feature exists and apparently does it's job? No comments or documentation are going to stop a developer — because reading them is optional. The way the feature should be used may be apparent just from the other code that also uses the old feature. And the developer will just copy and paste. Because the old feature was there, and the "example code" using it also was there. Only the compiler spitting out errors may stop them.

Don't get me wrong — I don't consider every developer a stupid ignorant schoolboy with sky-high ambitions. But occasionally all of us are stupid, or ignorant, or thinking "oh it's all clear from this code, I won't go ask the guy who implemented this", or tired of 12-hour drag because of burning deadline, or stressed because of pressure from the customers. And we, programmers, should protect programmers, including (maybe most importantly) ourselves, from making mistakes. This thought isn't new at all (see e.g. Pragmatic Programmer; it's great read, by the way). But it's worth repeating.

Apart from all the issues described above, the subject practice also violates one good principle, which may sound like Python's "there's one right way to do stuff", or like "don't create unneeded entities", with the latter probably known to reader as principle of Occam's razor. Being lazy to replace the feature, removing the old one, leads to code bloat, increasing the compilation time and executable size — because there are two ways of doing stuff.

It's also interesting to consider this love of backward compatibility, taken to the extreme — what will happen, if no features will ever get removed? I'll leave this thought experiment to the reader.

Don't retain the backward compatibility. Make your changes atomically, replace the old features, and make other people learn about them. It's better to let them know sooner rather than later.

It's also better to let them know through a non-buildable project, than a compilation warning or a email notification. But, this is a topic of another post, which I hope I'll publish soon.
comments powered by Disqus