Today I learned that A/B-Upgrades are not enough to make sure your setup is resilient from upgrade errors. What you actually need are A/B/C-Upgrades. A/B-Upgrades leave one possible error-case in the box, that is hard to counter: A broken Updater. While there are potential mitigations, such as checking the you can do a update dry-run on boot, to make sure you can in theory upgrade, if your upgrade process breaks in the last step (e.g. when writing your last bits to slot B) and you want to make sure you run all upgrades unsupervised, you can end up in a dead situation, where you can no longer rollback, since your update slot B is already overwritten, but unfinished. And you can’t write a new update due to the inability to finish the write process completely. One of the most straight-forward way to mitigate this problem is to provide an additional update slot, to create an A/B/C-Upgrade. Allowing your device at all time, to be able to update to a new version or rolling back to a version where upgrades have already successfully worked. An alternative would be of course to do a lot more excessive testing, and accepting the remaining risk.
I came across this thought, while trying to install the latest LineageOS upgrade on my phone, which resulting in the updater app crashing. After writing my university thesis about robust upgrade mechanisms, I have a special look at all kinds of upgrades.