Progress is measured in lines of code broken.
Programming languages grow or stagnate. No language is created perfectly, and often it takes a few years to sort out the good ideas from the bad ideas. Earlier design decisions can hamper the evolution of a language, and often the core developers decide it’s time for a breaking change.
Breaking changes are good for language developers, they remove obstacles to newer features, and eliminate a slew of edge cases. Breaking changes aren’t so good for developers though, code requires rewriting, libraries require updating. A considerable amount of work to turn a working program into a working program. Backwards incompatibility is a tradeoff between existing and new developers, and existing and new projects too. It isn’t easy.
Perl 6, is probably the best example of what happens when you abandon backwards compatibility. Perl 5 was burdened with the earlier semantics of the language, and the codebase had the scars to prove it. Larry decided it was time for a breaking change, and left Perl 5 in the hands of community.
As Larry went on his merry way, creating one specification to unite coders of all backgrounds, Perl 5 languished and CPAN stagnated. Thirteen years later, Perl 5 is finally picking itself up again—with calls for a rewrite, but without changing all of the language at the same time. Perl 5 isn’t dead, but the days of being the duct-tape of the internet are behind it.
A stable and mature Perl 6 implementation may eventually appear within my lifetime, until then it stands as yet another example of the second system effect. Pretty much every other language has been more successful at managing change, and some don’t even wait for a major version number before making backwards incompatible changes.
Ruby added “Unicode Support” in 1.9, not by introducing a unicode type, but by attaching an encoding to every String object. Ruby 2.0 almost caused a mutiny by introducing refinements, but eventually the feature was toned down and the developers put down their torches and pitchforks. Despite Ruby’s charge forwards, some codebases remain on 1.8, without the care and attention needed to upgrade.
Meanwhile Python 3 obsoleted many old features, without substantially changing the language, and many changes were backported into 2.7. Unfortunately, Python 3 changed the language and the C API at the same time, creating a chicken and egg problem: Users wouldn’t move until libraries did, and library maintainers wouldn’t move until users did. The process was a bumpy one, but slowly the migration is happening.
When you write a new incompatible version of your language, library, or framework—you are forking it. If you don’t change enough, people will be reluctant to upgrade. If you change too much, it may be easier for developers to move to another language, library, or platform.
Alternatively you can be chained to backwards compatibility forever, and if you have enough money and time, this can work out. For the rest of us, the tradeoffs involved in changes are a very thorny path. It’s easy to poke fun at the problems in hindsight, but I’ve yet to see a language handle evolution gracefully.