Here’s a question we answered in the Interrupt Slack channel after the OTA updates panel. We’re sharing the answer on our blog to make it available to a wider audience.
What’s the best strategy to introduce incompatible changes regarding update-functionality, transport protokol, etc.?
For starters, you need to version everything that might change so you can actually tell that there’s a version change and can take the appropriate actions. There’s a lot of complexity here depending on what, exactly, you’re changing and how backward compatible you need to be.
In the live discussion, David Shoemaker mentioned adding support for pre- and post-update actions (e.g., running scripts). This can be extremely useful for supporting actions like data migrations or file removals that need to be executed after an update has completed. You could also use this capability to implement post-update sanity checks to make sure that the update processes completed successfully. If the checks do not pass, roll back to the previous version.
Depending on the incompatibility that you’re introducing, you may need to split updates into distinct stages that are rolled out independently.
For example, let’s say you have an upcoming release that depends on a modification to the update process. You first need an update that would add the necessary support to the new updater. Then, once deployed, you’d roll out the dependent application update.
Another example of this might be rotating the signing key. Let’s consider the specific case where the public key is baked into your bootloader. You’d create a bootloader with a new public key baked in, and sign it with your old private key. You’d deploy the bootloader update. Then you’d sign your future updates with the new private key, and the bootloader could decode it.
