Please do the opposite. Let all deprecation warnings last at least a decade, just include in the warning that it is not maintained.
But more to the point, go out of your way to avoid breaking backwards compatibility. If it's possible to achieve the same functionality a different way, just modify the deprecated function to use the new function in the background.
My biggest problem with the whole static typing trend is that it makes developers feel empowered to break backwards compatibility when it would be trivial to keep things working.
edit:
Not that it is always trivial to avoid breaking backwards compatibility, but there are so many times that it would be.
> just include in the warning that it is not maintained.
I'm convinced this isn't possible in practice. It doesn't matter how often you declare that something isn't maintained, the second it causes an issue with a [bigger|more important|business critical] team it suddenly needs become maintained again.
I don't agree. Some programming languages started supporting a deprecated/obsolete tagging mechanism that is designed to trigger warnings in downstream dependencies featuring a custom message. These are one-liners that change nothing in the code. Anyone who cares about deprecating something has the low-level mechanisms to do so.
I don't know that I see why/how this is a problem? You would do the same with any other thing in your life?
More, in many things, we have actively decided not to do something anymore, and also highly suggest people not mess with older things that did use it. See asbestos. Removing it from a building is not cheap and can be very dangerous.
It also keeps slowing down development as getting a green global compile will make you still update "deprecated" functions that face breaking API changes.
> My biggest problem with the whole static typing trend is that it makes developers feel empowered to break backwards compatibility when it would be trivial to keep things working.
Disclaimer: I’m a strong advocate for static typing.
I absolutely see the connection. One of the advantages of static typing is that it makes a lot of refactoring trivial (or much more than it would be otherwise). One of the side effects of making anything more trivial is that people will be more inclined to do it, without thinking as much about the consequences. It shouldn’t be a surprise that, absent other safeguards to discourage it, people will translate trivial refactoring into unexpected breaking changes.
Moreover, they may do this consciously, on the basis that “it was trivial for me to refactor, it should be trivial to adapt downstream.” I’ll even admit to making exactly that judgment call, in exactly those terms. Granted I’m much less cavalier about it when the breaking changes affect people I don’t interface with on a regular basis. But I’m much less cavalier about that sort of impact across the board than I’ve observed in many of my peers.
I could see some maintainers being encouraged by static typing to introduce breaking changes, as long as the changes ensure compile-time breakage. However, that’s only an option in languages without dynamic linking. So it’s the combination of static typing and static linking that would encourage that.
As with all things, this can be pushed too far. Microsoft was suffering a maintainability crisis before the transition to Windows XP; their years of bending the API to support customers (which, in the short run, did keep customers to their benefit) was making for a fairly unmaintainable mess of an API surface that then screamed under the strain when the Internet era hit and all those open, under-observed APIs became potential worm attack vectors.
Actually, I think backwards compatibility is the reason Windows still dominates. It isn't the best OS at anything and it's actively user hostile, but if you want to run Windows apps... there are decades of Windows apps and they basically all work.
I was hoping to spin it as an environmental concern. You change something "for reasons". If you're doing this in a popular tool/library you probably just burnt down a small forest of trees worth of energy from all of the work people will have to put in deal with it. 100s of thousands of users missed dinner with a kid or loved one because of the extra work you created. A product that would of shipped on time didn't. Things you wanted to exist didn't because 100k people were distracted dealing with your breakage.
Wow. Why even remove it? It's just the thinnest wrapper around the dict and since the dict is now part of the public API these methods will work forever unmodified.
While I am happy to see types in Python and Javascript (as in Typescript) I see far more issues how people use these.
In 99% of the time, people just define things as "string" or if doesn't cover, "any". (or Map/Object etc)
Meanwhile most of these are Enum keys/values, or constants from dependencies. Whenever I see a `region: string` or a `stage: string`; a part of me dies. Because these need to be declared as `region: Region` or `stage: Stage`. Where the "Region" and "Stage" are proper enums or interfaces with clear values/variables/options. This helps with compile (or build) time validation and checking, preventing issues from propagating to the production (or to the runtime at all)...
Stripe do this in a cool way. Their REST API is version based on date, and each time they change it they add a stackable compatibility layer. So your decade old code will still work.
One thing I've always hated was this idea of "bitrot." That software just spontaneously stops working after time and loses backwards compatibility. Like it's some force of nature.
It's not a force of nature. Bitrot is: many software developers deliberately choosing to break backward compatibility in very small ways over and over. Software written in 1995 should still work today. It's digital. It doesn't rot or fall apart. It doesn't degrade. The reason it doesn't work today is decisions that platforms and library maintainers deliberately made. Like OP. Deprecate like you mean it. That's a choice!
If we want to solve bitrot, we need to stop making that choice.
Progress is often only possible by breaking things. It's not a choice, it's the only way forward. We have to optimize for the future being better, even if it makes the present a little worse occasionally.
This is a huge reason why open source projects are often so much more successful than corporate clones: they actually iterate and innovate, something corporate america has forgotten how to do.
> That would probably not trigger anyone’s midnight pager, but it would make it clear that relying on the deprecated functionality is a bug lurking in the code.
How do you know? This is a wild assertion. This idea is terrible. I thought it was common knowledge that difficult to reproduce, seemingly random bugs are much more difficult to find and fix than compiler errors.
If you're ready to break your api, break your api. Don't play games with me. If more people actually removed deprecated APIs in a timely manner, then people will start taking it more seriously.
> In case the sarcasm isn’t clear, it’s better to leave the warts. But it is also worthwhile to recognise that in terms of effectiveness for driving system change, signage and warnings are on the bottom of the tier list. We should not be surprised when they don’t work.
Yeah, I agree - this sort of intermittent failure could be incredibly hard to track down, and will absolutely fuck with people's faith in their CI systems as well - a flappy test is the absolute worst kind of test.
I agree, maintainers should just break the API if they're going to do it.
At the same time, it's crazy that urllib (the library mentioned in the article), broke their API on a minor version. Python packaging documentation[1] provides the sensible guideline that API breaks should be on major versions.
This isn't a good idea regardless of why it's being deprecated.
If it's no longer being maintained then put a depreciation warning and let it break on its own. Changing a deprecated feature just means you could maintain it but don't want to.
Alternatively if you want to aggressively push people to migrate to the new version, have a clear development roadmap and force a hard error at the end of the depreciation window so you know in advance how long you can expect it to work and can document your code accordingly.
This wishy-washy half-broken behaviour doesn't help anyone
Having a deprecated API just randomly return failures is an awful idea!
Better to give an actual timeline (future version & date) for when deprecated functionality / functions will be removed, and in the meantime, if the language supports it, mark those functions as deprecated (e.g. C++ [[deprecated]] attribute) so that developers see compilation warnings if they failed to read the release notes.
Yep. I'll admit I've acted faster to hard set dates than some "in the future" message. I've also seen some tools become really noisy about deprecation spanning many lines AND repeating. Please don't log the same message over and over for each instance. Color or add emoji if you must to grab attention, but once is enough. It's annoying when you can't do anything about it at that time and have to sift through this extra noise when hunting down another issue in the CI log. Add a link that goes over it in more detail and how to migrate for that specific deprecation.
How are you supposed to deal with recalcitrant users? I work for an organization that is ending support for several long running APIs. And by support I mean turn off the servers, you must move to an entirely new platform.
We’ve sent out industry alerts, updated documentation and emailed all user. The problem is the contact information goes stale. The developer who initially registered and set up the keys, has moved on. The service has been running in production for years without problems and we’ve maintained backwards compatibility.
So do we just turn it off? We’ve put messages in the responses. But if it’s got 200ok we know no one is looking at those. We’ve discussed doing brownouts where we fail everything for an hour with clear error messages as to what is happening.
Is there a better approach? I can’t imagine returning wrong data on purpose randomly. That seems insane.
> How are you supposed to deal with recalcitrant users?
Keep the servers running, but make the recalcitrant users pay for the costs and a then some more. It is actually a common strategy. Big, slow companies often have trouble with deprecation, but they also have deep pockets, and they will gladly pay a premium so that they can keep the API stable at least for some time.
If you ask for money, you will probably get more reactions too.
Step 1: Stop thinking of them as "recalcitrant". They're not recalcitrant. They bought (presumably for money) a product, and expect that product to keep working as long as they need it to! They don't expect the vendor to pull the rug out from under them and break it just because the API is old and icky and their software engineers are sad to keep it around.
Instead of "deprecate like you mean it" the article should be: "Release software like you mean it" and by that, I mean: Be serious. Be really, really sure that you are good with your API because users are going to want to use it for a lot longer than you might think.
> They bought (presumably for money) a product, and expect that product to keep working as long as they need it to!
This depents on the terms of the contract. Typically, termination of service is covered in a license. If the license terms are okay in the respective jurisdiction, there is no fundamental ethical obligation to run a server beyond that. There might exist specific cases where it would be inappropriate to follow the terms by the letter, but that also has its limits.
Nothing lasts forever. The second you decide to use a new 3P API you have to understand it might disappear one hour after your production launch, and that's okay.
Software evolves over time, along with business needs. What seemed like (or even was!) a good idea at some point will almost surely cease to be a good idea at some point in the future. Breaking the API is totally fine if there's a good reason and it's carefully managed.
A technique I used on a project was to change the URL, and have the old URL return a 426 with an explanation, a new link, and a clear date when the moved API. This reliably breaks the API for clients so that they can't ignore it, while giving them an easy temporary fix.
Clients weren't happy, but ultimately they did all upgrade. Our last-to-upgrade client even paid us to keep the API open for them past the date we set--they upgraded 9 months behind schedule, but paid us $270k, so not much to complain about there.
Sleep()s that increase exponentially every month seem like a good solution. When the API has a 10 second latency hopefully someone starts asking questions. If not I think brownouts are a decent idea.
It's loud, there's an out if you need your code working right now, and when you finally act on the deprecation, if anyone complains, they don't really have legs to stand on.
Of course you can layer it with warnings as a first stage, but ultimately it's either this or remove the code outright (or never remove it and put up with whatever burden that imposes).
Love this idea, as it is already implemented in a $FAANG company tool. (used company wide by 80k+ SDEs). I got used to seeing these in the logs and terminal. So much that my brain now automatically ignores it from my view/seeing like it does for my nose.
Instead, if you must, add a sleep within the function for 1 ms in the first release, 2 ms in the second release, and so on. But maybe just fix the tooling instead to make deprecations actually visible.
I get the intention but it's a bad idea, same with the article
if people are meant to depend on your endpoints, they need to be able to depend on all of them
you will always have ppl who don't respond to deprecation notices, the best you can do is give them reliable information on what to expect -- if they hide the warnings and forget, that's their business
but intentionally making problems without indication that its intentional results in everyone (including your own team) being frustrated and doing more work
you cannot force ppl to update their code and trying to agitate them into doing it only serves to erode confidence in the product, it doesn't make the point ppl think it makes, even if the court of public opinion sides with you
cover your bases and make a good faith effort to notify and then deal with the inevitable commentary, there will always be some who miss the call to update
this actually seems like an reasonable technical solution to the non-technical problem that causes deprecations to be ignored in the first place
Degrading performance exponentially (1ms, 2ms, 4ms, 8ms...) WILL create a 'business need', without directly breaking critical functions. Without this degradation, there is no reason to remove the deprecated code, from a business perspective.
But more to the point, go out of your way to avoid breaking backwards compatibility. If it's possible to achieve the same functionality a different way, just modify the deprecated function to use the new function in the background.
My biggest problem with the whole static typing trend is that it makes developers feel empowered to break backwards compatibility when it would be trivial to keep things working.
edit: Not that it is always trivial to avoid breaking backwards compatibility, but there are so many times that it would be.
I'm convinced this isn't possible in practice. It doesn't matter how often you declare that something isn't maintained, the second it causes an issue with a [bigger|more important|business critical] team it suddenly needs become maintained again.
If it's important, they'll pay. Often you find out it wasn't that important, and they're happy to figure it out.
I don't agree. Some programming languages started supporting a deprecated/obsolete tagging mechanism that is designed to trigger warnings in downstream dependencies featuring a custom message. These are one-liners that change nothing in the code. Anyone who cares about deprecating something has the low-level mechanisms to do so.
More, in many things, we have actively decided not to do something anymore, and also highly suggest people not mess with older things that did use it. See asbestos. Removing it from a building is not cheap and can be very dangerous.
I don't see the connection you're drawing here.
I absolutely see the connection. One of the advantages of static typing is that it makes a lot of refactoring trivial (or much more than it would be otherwise). One of the side effects of making anything more trivial is that people will be more inclined to do it, without thinking as much about the consequences. It shouldn’t be a surprise that, absent other safeguards to discourage it, people will translate trivial refactoring into unexpected breaking changes.
Moreover, they may do this consciously, on the basis that “it was trivial for me to refactor, it should be trivial to adapt downstream.” I’ll even admit to making exactly that judgment call, in exactly those terms. Granted I’m much less cavalier about it when the breaking changes affect people I don’t interface with on a regular basis. But I’m much less cavalier about that sort of impact across the board than I’ve observed in many of my peers.
Because it lays out the contract you have to meet on the interface. No contract? No enforced compatibility.
In this case it was 2 functions with 1 line of code each. https://github.com/urllib3/urllib3/pull/3732/files
While I am happy to see types in Python and Javascript (as in Typescript) I see far more issues how people use these.
In 99% of the time, people just define things as "string" or if doesn't cover, "any". (or Map/Object etc)
Meanwhile most of these are Enum keys/values, or constants from dependencies. Whenever I see a `region: string` or a `stage: string`; a part of me dies. Because these need to be declared as `region: Region` or `stage: Stage`. Where the "Region" and "Stage" are proper enums or interfaces with clear values/variables/options. This helps with compile (or build) time validation and checking, preventing issues from propagating to the production (or to the runtime at all)...
https://stripe.com/blog/api-versioning
No matter what others say, the pipelines are long.
The delays for release, getting into a distribution, then living out its lifetime... they are significant.
It's not a force of nature. Bitrot is: many software developers deliberately choosing to break backward compatibility in very small ways over and over. Software written in 1995 should still work today. It's digital. It doesn't rot or fall apart. It doesn't degrade. The reason it doesn't work today is decisions that platforms and library maintainers deliberately made. Like OP. Deprecate like you mean it. That's a choice!
If we want to solve bitrot, we need to stop making that choice.
This is a huge reason why open source projects are often so much more successful than corporate clones: they actually iterate and innovate, something corporate america has forgotten how to do.
Granted, modern coroutines do bring up some nostalgic feel for the days I had to support cooperative multitasking...
How do you know? This is a wild assertion. This idea is terrible. I thought it was common knowledge that difficult to reproduce, seemingly random bugs are much more difficult to find and fix than compiler errors.
If you're ready to break your api, break your api. Don't play games with me. If more people actually removed deprecated APIs in a timely manner, then people will start taking it more seriously.
> In case the sarcasm isn’t clear, it’s better to leave the warts. But it is also worthwhile to recognise that in terms of effectiveness for driving system change, signage and warnings are on the bottom of the tier list. We should not be surprised when they don’t work.
At the same time, it's crazy that urllib (the library mentioned in the article), broke their API on a minor version. Python packaging documentation[1] provides the sensible guideline that API breaks should be on major versions.
[1] https://packaging.python.org/en/latest/discussions/versionin...
If it's no longer being maintained then put a depreciation warning and let it break on its own. Changing a deprecated feature just means you could maintain it but don't want to.
Alternatively if you want to aggressively push people to migrate to the new version, have a clear development roadmap and force a hard error at the end of the depreciation window so you know in advance how long you can expect it to work and can document your code accordingly.
This wishy-washy half-broken behaviour doesn't help anyone
Better to give an actual timeline (future version & date) for when deprecated functionality / functions will be removed, and in the meantime, if the language supports it, mark those functions as deprecated (e.g. C++ [[deprecated]] attribute) so that developers see compilation warnings if they failed to read the release notes.
But yes, that would be the worst idea ever.
We’ve sent out industry alerts, updated documentation and emailed all user. The problem is the contact information goes stale. The developer who initially registered and set up the keys, has moved on. The service has been running in production for years without problems and we’ve maintained backwards compatibility.
So do we just turn it off? We’ve put messages in the responses. But if it’s got 200ok we know no one is looking at those. We’ve discussed doing brownouts where we fail everything for an hour with clear error messages as to what is happening.
Is there a better approach? I can’t imagine returning wrong data on purpose randomly. That seems insane.
Keep the servers running, but make the recalcitrant users pay for the costs and a then some more. It is actually a common strategy. Big, slow companies often have trouble with deprecation, but they also have deep pockets, and they will gladly pay a premium so that they can keep the API stable at least for some time.
If you ask for money, you will probably get more reactions too.
Instead of "deprecate like you mean it" the article should be: "Release software like you mean it" and by that, I mean: Be serious. Be really, really sure that you are good with your API because users are going to want to use it for a lot longer than you might think.
This depents on the terms of the contract. Typically, termination of service is covered in a license. If the license terms are okay in the respective jurisdiction, there is no fundamental ethical obligation to run a server beyond that. There might exist specific cases where it would be inappropriate to follow the terms by the letter, but that also has its limits.
But, perfection isn't realistic. If you don't have a plan for when you get things wrong, you're failing to plan for the inevitable.
Clients weren't happy, but ultimately they did all upgrade. Our last-to-upgrade client even paid us to keep the API open for them past the date we set--they upgraded 9 months behind schedule, but paid us $270k, so not much to complain about there.
That sounds like the best option. People are used to the idea that a service might be down, so if that happens, they’ll look at what the error is.
Of course you can layer it with warnings as a first stage, but ultimately it's either this or remove the code outright (or never remove it and put up with whatever burden that imposes).
Instead, if you must, add a sleep within the function for 1 ms in the first release, 2 ms in the second release, and so on. But maybe just fix the tooling instead to make deprecations actually visible.
if people are meant to depend on your endpoints, they need to be able to depend on all of them
you will always have ppl who don't respond to deprecation notices, the best you can do is give them reliable information on what to expect -- if they hide the warnings and forget, that's their business
but intentionally making problems without indication that its intentional results in everyone (including your own team) being frustrated and doing more work
you cannot force ppl to update their code and trying to agitate them into doing it only serves to erode confidence in the product, it doesn't make the point ppl think it makes, even if the court of public opinion sides with you
cover your bases and make a good faith effort to notify and then deal with the inevitable commentary, there will always be some who miss the call to update
Degrading performance exponentially (1ms, 2ms, 4ms, 8ms...) WILL create a 'business need', without directly breaking critical functions. Without this degradation, there is no reason to remove the deprecated code, from a business perspective.
But intentionally breaking my users runtime in a way that's really hard and annoying to find? Is the author OK? This reads like a madman to me.