For many people, both inside and outside the Python community, even mentioning the Python language brings to mind the Python 2-3 transition. Let us see what happened.
I first got involved with Python in a project to “port” my Physics Department’s undergraduate computing teaching to a new language, back in 2003. The computing practicals were taught in Pascal using the Free Pascal Compiler up until then. There were various choices for alternative languages, some more modern, some more useful, some both: eventually a bake-off was organised between Python (more modern, arguably useful in 2003) and C (less modern, definitely useful).
At the time, Python was thought of as a “scripting” language along with the Bourne shell, C shell, Perl, Tcl, and others. “Scripting language” does not actually mean anything. It is said by people who want to imply that a programming language is less worthy somehow because it is easier to use. LAMP was already in common parlance back in 2003 so it was already clear that real web applications could be, and were being, developed in Perl, Python and the like.
It is a tangent at this point but Python won out over C in that Physics teaching bake-off. For the kinds of problems we covered in undergraduate computing for Physicists, each language was as easy as the other to write, and students got just as far. But Python was way easier to read, as the enforced whitespace organisation unified, to some extent, the presentation of the code. Demonstrators reading over the shoulders of 20 different undergrads trying to debug their programs found it way easier to spot problems in the Python.
Anyway, all of this means that when I was new to Python, version 1 of the language was well-established, version 2 was fairly broadly adopted, and talk was turning to the future. The community had a name for this future: Python 3000.
Python 3000 was a project that took a number of different concerns into account. Firstly, that Python was broken. Not egregiously broken, but broken-ish. Various choices, made deliberately or accidentally, were seen to be suboptimal and Guido and the community saw that if they were allowed to break everything, they would do things differently.
Secondly, that even though things are currently broken, quite a few people seem to enjoy using them thank you very much. And that is why it was set in the future, version 3000. None of these breaking changes would come in version 2, and discussion of what would come down the line was happening in the open, via the Python Enhancement Process.
In fact, third, not only were the breaking changes discussed in the open, but they were implemented in Python 2 so that you could test and migrate. If new syntax was added that was simply erroneous for Python 2, it just went into Python 2 directly (the with keyword for context managers is such a case). You could carry on without knowing it was there, or change and get the modern stuff. For changes that made the language work differently, you could from future import breaking_change and live tomorrow’s Python, today.
This whole Python 3000 discussion probably started around the year 2001, when it was “intentionally vapourware” as Mark Lutz described it. Five years later, around the beginning of 2006, PEP-3000 was published documenting the process for implementing Python 3000, the fact that it would be synonymous with Python 3, and finally kickstarting the implementation phase. The future was becoming real!
Python 2 was still maintained alongside the development of Python 3, and even after it had stabilised. The last release of the 2.7 lineage was in April 2020, two decades after the first release, two decades after the discussions of py3k started, 14 years after the migration path was published, and 11 years after the release of version 3.
And people still felt that they had not had enough warning.
In fairness, some of them had not. They were not Pythonistas per se, they were computer users who happened to engage with Python when using a computer. Climate scientists, perhaps, who relied on their library vendors and their site administrators to keep everything ticking over. But they did not realise that their library vendors did not have funding for maintenance, and that their site administrators were relying on the operating system package maintainers.
The operating system package maintainers did not dare upgrade the default Python package, because that would break people’s scripts. Better to ship version 2 tomorrow so that everybody’s programs from yesterday carry on running.
Nobody was responsible for migrating to Python 3, so nobody did. It was not until 2.7, when people were finally told that this was the final release of Python 2, that these Pythonistas noticed the corner that they were painted into. They were not happy.
Of course, this story is not necessarily over. Somebody, or various somebodies, may well fork Python 2 and carry on releasing updates, becoming the Mate to Python 3’s GNOME.
This model has worked well for Perl. The Perl language, currently on version 5.34.0, is a different thing from the Raku language (formerly Perl 6), on version 6.d. Some people use both, some prefer one over the other, and they coexist peacefully. Raku started out as the successor to Perl, and gradually evolved into her sister. But still other people are turned off of Perl/Raku by confusion over whether they are “supposed” to use 5 or 6 (the answer, of course, being “you are not supposed to use either”: and besides which I expect they are put off Perl/Raku because it is not faddish enough and the version thing is a convenient smokescreen).
The model that has worked for C++ is never (or rarely, anyway) taking away people’s toys. They might get warnings on, saying “this toy is now known to be a choking hazard and you should never give it to your children”, but they never issue a manufacturer’s recall. You are not supposed to use new or delete any more, you are supposed to use smart pointers, but nobody turned off new and delete.
For a counter example, the model that has worked for Swift is YOLO. The language got changed once a year, people guessed what some of the changes were going to be based on reading the community proposals tea leaves. An automated tool rewrote some of your existing Swift in the new version of the syntax, and people were still excited to rewrite all the bits that the tool missed.
You can never give people too much warning about a change. Giving a fixed time notice of a change, however long that time, paints people into a corner. Prefer that the lead time be infinite, except where that paints people into a corner.