I've written before that software projects need sane, published deprecation policies, and I still believe that...
... but listen to a tale of two intertwined projects with a seemingly-sane and published deprecation policy that sounds great but doesn't actually work.
Parrot and Rakudo used to be much closer together. The Parrot repository had a subdirectory languages/ which held several language implementations running on Parrot. This was well and good (How did a lion get rich? It was the olden days!)—whenever Parrot underwent an API change, a simple ack of the source tree could find most places that needed to change, and running the test suites of the projects under languages/ could find the rest.
(Getting people to run the full test suite is a different story, but in that case at least guilt and a good bisection tool work post hoc magic.)
Then one day, Parrot left the Perl-centric world of perl.org and kicked
languages/ out of the nest. languages/perl6 went to
Rakudo.org and the troubles began.
You see, back in the olden days when a lion could get rich and the Perl 6 VM
was like the Perl VM but a little bit better, the way to extend Parrot was,
obviously, write a whole wad of C code. Want to add a new data type?
Write a wad of C code. Want to change how a core wad of C code works? Write a
whole wad of C code. If hacking on Perl had taught anyone anything (and I
can't believe I can write this with a straight face), it's that finding hackers
ready, willing, and able to write big wads of C code to work around or
customize or extend other big wads of C code so that someone else doesn't have
to write C code is a really easy prospect.
Anyhow, the Parrot execution and extension and embedding model has always
assumed, as does Perl, that the VM should expose a big wad of C functions
that extenders and embedders should use to manipulate big wads of C structs and
other data types defined in C.
On one hand, customization and flexibility is good. On the other hand,
people like me who know both C and Perl write in Perl when we can and C only
when we have to for several very, very good reasons.
Imagine a ventriloquist with a Parrot puppet, except he doesn't stick his
hand in the puppet to pull levers and flip switches and wiggle his fingers. He
has a robot hand, operated by a joystick, and that robot hand is half robot and
half organic and it tickles actual organs inside the puppet and the bird goes
off and does its thing. Oh, and he's operating that joystick with his toes.
Plus he's underwater, wearing a blindfold.
In other words, Rakudo has an intimate knowledge of the guts of Parrot and
does some strange and bizarre things. (If you look through the revision history
of those parts of Rakudo, you'll see I've fixed some of those strange and
bizarre things and I've perpetuated others.)
When Rakudo and other languages left the nest, a deprecation policy came about. Imagine this vaudeville act:
HLL Developer: "Hey, you changed this API!"
Parrot Developer: "You weren't supposed to use it."
HLL Developer: "No one told me that, but regardless, you broke my project."
Parrot Developer: "Oh, sorry. Well here's how to fix it."
HLL Developer: "Wish you'd told me that months ago."
Parrot Developer: "Yeah, we'll do better next time. By the
way, what in the world were you doing with that?"
HLL Developer: "No one told me to do it differently, so I
just threw together something that works."
You can see where this is going.
Thus began the great Parrot version numbering wars. The end result was that
Parrot would have two supported releases every year, one coming every six
months. The initial deprecation policy promised no backwards incompatible
changes without notification in at least one supported release.
In other words, if a project which was previously in languages/ had
moved somewhere else where a well-meaning Parrot developer didn't notice that a
simple change broke backwards compatibility, that project had the ability to
request a reversion of the change, a documentation of the change as a
compatibility change, and a delay of up to six months before making the
change...
... depending on when the project noticed.
I've mentioned the idea of technical
friction before, but get out your calendar.
This policy didn't only protect languages from unintended changes, it
protected languages from desired changes. For example, if Rakudo wanted a new
feature in Parrot, and if the best way to add that feature in Parrot meant
breaking backwards compatibility (changing the order of search paths, for
example), both Rakudo and Parrot developers had to mark their calendars.
The deprecation policy period is now three months, for hopefully obvious reasons.
Even so, now there are at least two projects on separate time tables with
separate goals for features and improvements and deprecations. When Rakudo
wants a feature from Parrot that Parrot doesn't yet support (or Rakudo
developers believe it doesn't support fully or at all or in the precise way
Rakudo wants), Rakudo tends to toe the joystick. When Parrot makes a change
that exposes the fragility of tickling bird gullets with robot fingers, Rakudo
gets to complain. The deprecation policy followed to the letter prevents Parrot
from adding the features Rakudo demands at the same time that it prevents
Parrot from improving the features that Rakudo uses.
You might read this and think that the problem is the deprecation policy.
It's not—at least, that's not the root cause.
I hate the polite fiction calorie-free slogan that "Perl 6 is a research
project", because it's a polite way to suggest that Perl 6 is an embarrassing
pipe dream no true Perl hacker takes seriously, but implementing Perl 6 has
required a lot of research and fits and false starts. (Before you complain next
that it's taking a while, you write a performant grammar engine which
allows lexical overriding of any grammatical rule or category.) When Parrot
began, no one knew exactly how a Perl 6 VM machine should implement several
important features. That was also true in 2005. That was also true of Parrot's 1.0 release in
March 2009.
Making an effective Perl 6 means continually thinking and rethinking and simplifying and inventing new things.
Now tie an anchor around the feet of a live bird and pretend you're a
ventriloquist.
Granted, Parrot's also long had the goal of running other dynamic languages
efficiently and effectively. Its endgame is more than merely Perl 6 (and Perl
5). Yet a Parrot that won't run Perl 6 well has failed at its primary goal, and
subsequent goals are less interesting.
The right solution is to invent a time machine and not kick Rakudo out of
the nest. (The rightest solution is to invent a time machine and start
implementing Perl 6 on Parrot from the first day as a first-class citizen and
not a rat's nest of Perl parsing madness which had, to my count, at least two
replacements before Rakudo.)
Rakudo's developers—who had commit access to Parrot, of course, so
they could fix things if they wanted—declared that the only real solution
would be to add another layer, another project external to both Parrot and
Rakudo, which will sit in between the two and abstract away the Parrot specific
parts in the hope that someday someone will add another VM backend so that
Rakudo doesn't have to be tied to Parrot forever. In other words, throw away
all of the work that went into Parrot, start over from scratch, and hope that
this rewrite will be the one that sticks.
That went over about as well as you'd expect. (Chasing
away Parrot's developers set Rakudo back by another couple of years.)
If the notion of working around a broken deprecation policy—broken
because of too little coupling between two projects so intimately
intertwined—is to add another layer of coupling to separate those two
projects seems a little strange to you, you're not alone. By my count, that's
three projects in a driver's seat built for one. If you can figure out which
project is upstream and which project is downstream and how to debug with
reasonable each which change where broke which test and who gets to fix it,
you're a far, far better developer than I am.