When you say "Django is not RoR", are you saying Django doesn't break backwards compatibility but Ruby on Rails does?
If so, I have to very strongly disagree. It's almost mind boggling to me how much they break backwards compatibility as a framework. They usually warn users with a deprecation warning in one version and then they make the backwards incompatible change in the next version, but the sheer amount of these kind of changes makes upgrading an infuriating process. Especially when upgrading a large codebase.
That is, by definition, breaking backwards compatibility. I.e., the code that I wrote against version X will not run against version X + n. They just give a warning about it in version X + (n - 1). Backwards compatibility means that my code will run against X + n for all values of n >= 0 without modifications.
> That's exactly how _not_ to break backwards compatibility.
No, no, no. Backwards compatibility means that there are no breaking changes at all - announced by deprecation warnings or not. Think Linux's public API.
The documentation was telling something like: Ensure you applied all previous migrations and then start from scratch. Thats not an upgrade path. It may work for standalone web applications, but I have a python project (packaged as deb/rpm package and using the packaged django version of different distributions) that should work with multiple django versions and the user may upgrade the django version at any point.
I had to write raw sql statements to get the mirgration status of south (after the upgrade, without a working version of south anymore) and "fake" apply the migrations of the new native solution.
If you were supporting applications which needed to work with or without South at the time of the transition, the best thing to do was probably ship two separate sets of migrations (one set written for South, one written for Django's built-in migration framework).
I've upgraded a large (~700K LOC) Django project across several versions (1.5-1.9). I think Django does a reasonably good job of maintaining a good upgrade path, though they frequently break backwards compatibility, and at a pretty fast rate (now 8 months release cycles, which barely gives the community to catch up). Some of the changes like module renames seem pointless from an end user perspective, so it feels like the django core team shovels busywork onto users. I also find their method naming policies annoying, since they don't annotate "unsupported" features in any way (eg with an _attribute, and also some _attributes ARE supported). This makes it difficult to figure out whether you're "staying within the lines" of their support policy. I keep a checkout of the django repo around so I can grep the docs for mentions of a method before I use it.
That said, having started on updating that same codebase to Python 3, the Django team tries hard to provide the KEY thing you need to seamlessly update a codebase: a version which can support both incompatible features. Without this, you need to have one giant branch with a bunch of renames and other changes. They actually introduce deprecation warnings, and have something non-deprecated you can use in the same version. I think if Python core had introduced a version of Python including both versions of all the module renames with deprecation warnings, the community would be much further along on the upgrade path.
How else would you introduce backwards incompatible changes?
Especially with the LTS upgrade path that alasdairnicol mentioned I wonder what there is still to improve.
If you want to be backwards compatible, then you "simply" do not introduce such changes, there's no "how", you don't.
A platform that works hard to be backwards compatible does only additions and extensions, and keeps maintaining the old API as well so that old apps run without changes; possibly keeping around multiple depreciated ways to do the same thing (e.g. as win32 does).
It is debatable whether backwards compatibility is worth that cost (and it often isn't), but it's certainly a choice.
Mark it as deprecated, change the documentation to tell the user to use the new function Y instead and the most important part: keep the old api even if it's crufty. Wait for a few years until everyone upgraded and no longer uses any deprecated functions. Maybe make a final LTS release for those who don't want to upgrade. Then and only then should you ever break backwards compatibility.
Wait for a few years until everyone upgraded and no longer uses any deprecated functions.
Then view HN threads of people saying they have thirty-million-line codebases which utterly rely on deprecated functionality and they never ever plan to upgrade or do even basic maintenance ever for any reason ever, and will abandon your platform and completely rewrite in something that treats them "better".
I worked in a C shop for like 8 or 10 years and we never introduced a back-wards incompatible API release. There were a few changes that required all servers to be restarted (not at once) to change network protocol (16-bit fields overflowing, that sort of thing). But it was inconceivable that you'd break something that is working. To be sure, we took a couple of weeks before designing an API, and for no one was the API they wrote and shared with others the first API they designed as an adult. Even when the implementation mutated out of all recognition, the API owner would just do whatever re-coding/re-implementation was needed behind the existing API.
> How else would you introduce backwards incompatible changes?
Not at all. That would have the added benefit of core developers thinking twice before introducing a new API - exactly like it happens with Linux syscalls.
breaking changes get a 2 major version release timeline. that's like 18 months (on average) of warning to update your shit. how could you possibly do this better?
Agreed 1.4 LTS to 1.8 LTS was tricky, but future LTS to LTS upgrades like 1.8 to 1.11 should be easier. From 1.8 onwards, if your code runs without depreciation warnings in one LTS, then it should work on the next.
If so, I have to very strongly disagree. It's almost mind boggling to me how much they break backwards compatibility as a framework. They usually warn users with a deprecation warning in one version and then they make the backwards incompatible change in the next version, but the sheer amount of these kind of changes makes upgrading an infuriating process. Especially when upgrading a large codebase.