On the twentieth of March a dependency we run everywhere changed to a source-available licence, and a foundation-backed fork was announced within the week. The reflex is to migrate immediately; the useful first step is working out whether anything applies to us at all.
The symptom
what actually happened, in a day:
09:00 the announcement
09:40 three messages in the team channel, two of
them proposing a migration
11:00 a fork announced by a foundation
14:00 a blog post arguing the fork is the only
responsible choice
16:00 a second blog post arguing the opposite
and at no point did anybody read the licence.The commentary arrives faster than the analysis and it is not a substitute for it. Every argument in circulation was about what the change means for the ecosystem, and none of it was about whether our specific use is affected — which is a question with a short answer.
Why it happens
A licence change is a business decision by a company that owns the copyright, and it is legitimate whether or not it is popular. The reaction it produces is proportional to how many people depend on the software rather than to how many of them are affected.
The fix
Four questions, in order
1 does the new licence restrict our use?
the restriction is on offering the software as a
managed service to third parties. we run it as
internal infrastructure behind our own
application.
→ it does not apply. read from the licence text,
not from a summary.
2 is the version we run affected?
the change is not retroactive. every release up to
and including 7.2.4 remains under the old licence,
permanently.
→ no.
3 what changes if we do nothing?
future releases, under the new terms. security
patches for the version we run: unclear, and that
is the actual risk.
4 is there a fork, and is it real?
six weeks old at the time of asking.
→ not yet answerable.The third question is the one with teeth. The licence not applying to our use is comforting and irrelevant if the version we run stops receiving security patches, which is a question about the maintainers’ behaviour rather than about the licence text.
Reading the licence rather than the commentary
the operative clause, paraphrased and then checked
against the text:
you may not make the functionality of the software
available to third parties as a managed service.
our use:
one instance, on our own hosts, used by our own
application, not exposed to anybody.
the grey area we do NOT have:
a multi-tenant platform where customers get their own
cache. that would need a lawyer.
time spent: 40 minutes, including finding the licence
text rather than a summary of it.What a migration would actually involve
the fork is protocol-compatible, which sounds like a
drop-in replacement and is half of one:
the protocol identical. our client works
unchanged.
the container image available, different name.
the configuration identical.
the client library ours works. the one used by
a package we depend on has
its own fork, at a different
version.
the managed offering
at our host does not exist yet.
the tooling a monitoring exporter we use
has no fork support.
so: an afternoon, plus two unknowns.Protocol compatibility means the migration is a configuration change and the ecosystem around it is what takes time. The monitoring exporter is the concrete blocker — it identifies the server by an INFO field that the fork changed, and a dashboard that reports nothing is not an acceptable outcome of an unnecessary migration.
The decision, and the trigger
# ADR-023: Stay on the current cache server
## Status
Accepted, 2024-04-19.
## Decision
Pin 7.2.4. Do not migrate. Revisit 2024-10.
## Triggers that change this before October
- a CVE in 7.2.x with no patch under the old licence
- our hosting provider dropping the managed offering
- the fork reaching a stable release with exporter
support (checked monthly, automatically)
- a business requirement to offer per-tenant caches
## What we are NOT deciding
Whether the licence change was justified.
The triggers are what make this a decision rather than a deferral, and the last section is what keeps the record readable — a decision document that takes a position on the ethics of the change is a document that will be argued with rather than acted on.
The compatibility test we added
/**
* Runs the full cache integration suite against both
* servers. Scheduled monthly; not part of CI.
*/
public function testTheCacheContractHoldsAgainstBothServers(): void
{
foreach (['redis://cache:6379', 'redis://fork:6379'] as $dsn) {
$store = new PhpRedisStore($this->connect($dsn));
$this->assertCacheContract($store);
}
}
Running the contract suite against both monthly turns an assumption about compatibility into a measurement, and it costs a container in a scheduled job. By October it had run six times with no failures, which is what made the eventual answer — still not migrating, and the option is real — something more than a guess.
Verifying it worked
$ grep -n 'image: redis' docker-compose.yml
image: redis:7.2.4@sha256:8c1f4a7e...
$ ./bin/check-adr-triggers ADR-023
CVE in 7.2.x unpatched: no
managed offering dropped: no
fork stable + exporter: fork stable yes,
exporter no
per-tenant requirement: no
→ no trigger fired. next review 2024-10.
$ vendor/bin/phpunit --group=cache-compat
Tests: 41, both servers. OKA script that checks the triggers is what stops a six-month review being a calendar entry somebody dismisses. Three of the four are answerable automatically; the fourth is a business question and is a prompt rather than a check.
What this costs
A decision that is deferred rather than made, which is the honest description. Pinning a version means the pin is now a thing that has to be justified at every review, and the pressure to just migrate and stop thinking about it will grow rather than shrink.
The monthly compatibility run is also a container and a scheduled job that exists to keep an option open, which is a cost with no benefit until the option is exercised. If it never is, that is six years of a job nobody needed — and the alternative is discovering the incompatibility during the migration that a CVE forced.