Loading...
 
Skip to main content

Backport Policy

Why we need a backport policy

The objective

In early 2026, the community at least agreed on two things:

  1. The number of backports must be reduced
  2. The MRs that do get backported need much better, explicitly documented justification before being backported

Context

Tiki will soon be 25 years old. It was initially released in a very different era. Right after the first release of the SVN (not git!) and PHP 4. Well before Test Driven Development (TDD)(https://en.wikipedia.org/wiki/Test-driven_developmentQuestion) or even Continuous testing(https://en.wikipedia.org/wiki/Continuous_testingQuestion) was commonplace in the PHP development community.

This poses considerable difficulties:

  • A codebase not designed to be easily tested, and for tests to run continuously, locally and fast makes test writing more difficult to learn for developers.
  • Improving the situation requires architectural changes that can themselves introduce regressions, which are very hard to work on politically if the developer community isn't convinced it will improve their life, and can only be done by developers already familiar with architectural patterns necessary.

The purpose of tests is to reduce regressions and make refactoring and working as teams easy and cheap.

Trunk based development is now the nearly uncontested standard branching model(https://trunkbaseddevelopment.com/branch-for-release/Question) for new software. Unfortunately, it is predicated on extensive automated testing which we don't have yet.

This is compounded by the fact that:

  • Like programming languages, code runs on top of Tiki, and makes assumptions that the developers do not.
  • Unlike programming languages, behaviour in Tiki is not fully specified. And to a point can never be fully specified: people writing wiki pages, custom templates and pluginlists and custom code will always code against the UI in front of them.

Important definitions

Concurrent releases

Definition: A project has concurrent releases when it maintains (releases new versions for end user consumption) from multiple branches at any given time. Ex: releasing a 27.5 after a 30.0

It is important to note that having concurrent releases is quickly disappearing as a general engineering practice for user facing software products. This is driven by three main factors:

  1. The rise of https://trunkbaseddevelopment.com/branch-for-release/Question as the generally accepted best practice
  2. The strong (if actively contested) trend back to the modern equivalent of static linking(https://en.wikipedia.org/wiki/Static_libraryQuestion) for software distribution in the form of sandboxes (ex: snap(https://en.wikipedia.org/wiki/Snap_(softwareQuestion)) and flatpak(https://en.wikipedia.org/wiki/FlatpakQuestion)) and containers (ex: docker and lxc)
  3. Entire influential languages (namely python and javascript/node) actively discouraging using the OS provided runtimes for both development and production.

This has led to two opposing trends:

  1. The repositories (pip, npm, etc) associated with the runtimes above caused both developers and users to expect to have the latest version of software available at all times.
  2. The decreased dependency on system software has made it politically workable for sysadmins and cloud providers to run LTS versions of OS and libraries many years behind the latest release.

Unfortunately, just like web browsers, Tiki is in the awkward position of being both:

  • User facing (keeping up to date with latest features and UI trends is expected)
  • A base upon which software systems are built (unanticipated changes in behavior and UI are a problem)

So for better or worse we maintain multiple releases.

Maybe we always will; Upgrading can require change to the end user's own code, and cannot practically be done constantly. This is the reason programming languages, frameworks, and some libraries still have concurrent releases (but for shorter and shorter periods).

LTS releases

The point of running a Long term support (LTS)(https://en.wikipedia.org/wiki/Long-term_supportQuestion) release of anything is a tradeoff: for a long period of time, you knowingly choose not to get:

  1. all (or any) new features
  2. all bug fixes

In exchange for, when upgrading within the release, having:

  1. A lowered chance of new regressions
  2. A lowered chance of having to modify your own code running on the system

Of course you could decide not to upgrade at all. But in practice it is normally not workable: One cannot forego security patches, adaptations to the evolution of SSO standards, etc.

Like the term "free software" (meaning freedom but often understood as cost-free), the naming of debian LTS releases as "stable" brought much confusion: a LTS release is stable as in "not changing much", not as in "more bug free than a normal release".

If the only changes in a specific piece of software were coming from independent new features, all else being equal, an LTS release would indeed become more bug free than the latest. But that is not how the real world works. Most new features require changes to existing code, and software degrades from the mere passage of time, a concept called bit-rot(https://en.wikipedia.org/wiki/Software_rotQuestion).

Regression

Definition: There are different definitions, all superficially simple; Wikipedia offers(https://en.wikipedia.org/wiki/Software_regressionQuestion): "A software regression is a type of software bug where a feature that has worked before stops working correctly". Ok, something that used to work no longer works or no longer works as well as before. Simple enough.

The problem in this case is what's a bug, and what's a feature? That's much harder to agree upon!

Not all code changes causing "regressions" are mistakes. They may break someone's undocumented and untested assumption. Fixing a regression may cause a new one, or even the same one for someone else! Security-related changes frequently prevent things that people wanted to do.

Why backports are especially dangerous

Regressions can of course happen in master. Or between successive releases. What makes backports so special?

They are special because they do not normally exist in most modern software release processes. They only exist because we run concurrent releases. And the purpose of those concurrent releases (in fact their only reason for existing) is that some users have a need to minimize not only regressions, but all changes at unanticipated times, above most other considerations.

Backports are also costly!

Some of these costs are direct, such as the extra coding, review and modification of the same code in multiple branches.

Some are indirect. The expectations that backports will exist remove a lot of opportunities for paying off technical debt. It can even slow down or stop outright all continuous architectural improvements in the main branch out of a general fear of change.

The backport policy

Rule 1: Fill the template in the description of the backport MR:

This will eventually be part of the gitlab template(https://docs.gitlab.com/user/project/description_templates/#create-a-merge-request-templateQuestion). I really need help with the content of those templates, in the form of suggested text, or specific situations you expect to encounter name=benoitgcolor=red

Previous MR(s) and commit hash(es) this is based on

What is the final target branch for this backport

ex: 27.x

Where/How/By whom was this code tested, specifically since it was merged in the previous branch

ex: Dogfooding on dev.tiki.org and checking xxx. Unit test coverage has been reviewed and deemed to cover everything this is likely to impact.

Who will be harmed if this doesn't get backported?

ex:

  • If this isn't backported, Intertiki in LTS will not work if linked to a newer Tiki.
  • This is necessary to upgrade library xxx, which has a crucial security problem. Or cannot install on...
  • The client financing that feature runs tiki xx.x

Note: One never backports things "just in case". A specific organisation, or a realistic persona with a realistic scenario is required. "All users running X in context Y will suffer consequence Z if this isn't backported"

What makes this, in the author's opinion, safe, or worth the risk

Ex:

  • Self contained new code unlikely to impact anything else (ex: new wikiplugin).
  • Doesn't touch code execution (ex: Error string corrections or return code corrections, translation updates).
  • Without this law xxx will not be respected.
  • Incomplete or not fully tested fix, but the alternative is worse because xxx.
  • This code already wasn't working in branch xx.x

Note: Once again, this is easy if there was a clear rationale in master. In any case, "Someone asked me to do it" isn't a sufficient reason (except in the form of a hyperlink to that person explaining why it's safe / worth the risk).

Proposed release note entry for the point release that will contain this backport:

The proposed release note entry should be part of any merge request description on master that warrants one. That way everyone can see, and hopefully discuss, what issues are communicated to people upgrading.

For a backport it becomes mandatory:

  • No fix worth the risk of a backport is not worth a point release note entry to inform tiki operators.
  • The reason operators run older releases is to avoid unexpected surprises, so they want to see all changes.

Rule 2: Follow the backport chain

Option A: The normal process

Ex: master => 30.x => 29.x => 27.x LTS

The normal process is to:

  • Develop your MR in master
  • Successively cherry-pick into ALL previous active branches until the target branch is reached.

The entire point of this is for the changes to be tested before being backported to a previous branch.

Unless you are running real-world like testing of your changes between your backports, or you are reasonably certain the whole of your code is covered by automated testing, it's nonsensical to backport in the same day, as you are skipping the entire dogfooding testing process!

If you feel you have to backport the same day anyway, look to the alternate process.

Note that for backports, the original developer may target 29.x, and later another developer decides to backport to 27.x.

Option B: The alternate process

Ex: (Optional: vendor branch) => 27.x LTS => master => 30.x => 29.x => (Optional: 27.x LTS)

The alternate process is to:

  • Develop in a branch (possibly a vendor branch, or a past release branch).
  • Either before the MR goes through the review process (typically in a vendor branch), or after it was merged in the mainline release branch, deploy in production, in whatever context you had a problem to fix. Perform real world testing.
  • Cherry-pick directly to master, making all necessary adaptations.
  • Like in the normal process, backport successively until reaching the branch above the initial branch in Tiki mainline.
  • Compare the changes in the previous branch with the initial changes in the initial branch (problems/relevant changes may have been done in the different reviews along the way.)

Advantages:

  • Will get the fix to your client faster
  • Will usually be safer (real world testing at your production or staging site in addition to the review and dogfooding process)
  • If problems are found, less demanding on the review process (the iterating mostly happens in the same branch).

Drawback:

You have the obligation to cherry-pick in a timely manner your MR to master and follow the backport chain to every active branch higher than the branch the fix is developed in no matter how much time and effort that takes.

So why not do this all the time? Because the review process in your branch is likely to be quite context specific. The review in master may lead to considerable architectural changes which may lead to a patch that is considerably different than your original one. For example, if there are newer APIs in master, you cannot just say "Oh, but this didn't exist in branch xx.x, so I don't want to use it"

As a rule of thumb, the larger the change, the less suitable the alternate process is.

Rule 3: Keep backport changes auditable and atomic

Atomic in the context of backports means that they contain everything they need to work on the target branch.

So for example:

  • MR 1 is merged in master with separate commits
    • commit 1
    • commit 2
  • After testing, follow-up MR 2 is merged to master with
    • commit 3 (squash of multiple commits)

Then the developer wants to backport to 30.x.

It MUST be done as:

  • MR 3 containing
    • commit 1 (cherry-picked)
    • commit 2 (cherry-picked)
    • commit 3 (cherry-picked)
    • Optionally: new commit(s) 4 if significant adaptations are necessary for the MR to work on branch 30.x (ex: API changes). "Simple" conflict resolution can be done during the cherry-pick process and does not require its own commit.

The MR must be set NOT to squash commits in merge options (so one can find if any single commit was backported from the commit message)

Show PHP error messages