The problem
Releasing the desktop app was a manual routine. I built the installers, signed them, uploaded them and cleared the content delivery cache by hand, on an older laptop that sometimes overheated or crashed partway through. A manual process invites mistakes and slows every release, so getting out a fix meant worrying about whether it had been sent properly instead of the code itself. Users also heard about a new version only when I told the product owner and he passed it on.
What I measured
I did not time the manual routine before replacing it, so there is no before figure and I do not quote a ratio. What I did measure is the pipeline. I took the durations of 19 successful production releases between 22 September and 8 October 2026 from the pipeline history. Each one includes the type check and the full unit test suite.
What I built
A release starts when a change is merged to a release branch. Each run goes through three stages.
- The first stage installs exactly the locked dependencies, runs the type check and the unit tests, and builds the app. A failing test stops the release here.
- The macOS and Windows builds then run in parallel, so the total time is that of the slower build and not the sum of both.
- The last stage uploads the installers to fixed download locations and clears the cache, so users get the new version straight away.
Two constraints shaped the design. Signing the macOS app and having Apple notarise it needs Apple’s toolchain, and signing the Windows installer needs a certificate held on a physical hardware token. Neither can run in a cloud container, so those two builds run on build machines that I set up and run. The macOS signatures are checked before that stage finishes, the update information is verified on both platforms, and the error tracker receives debugging information for the production build.
Each failure I met became a written runbook, including a locked signing token, an operating system update that broke notarisation, and a build machine that would not start.
How releases reach users
Before this, users learned about a new version only when I told the product owner and he passed it on. I built an in-app updater to remove that step. The app checks for a new version, shows an update button with download progress, and installs the update when the person clicks, so nobody has to switch to a browser and find a download page. It works on both macOS and Windows, and it is how the team has been getting each build for some time.
The last pipeline stage publishes the files the updater reads, so a release is only finished when those files are right. In October 2026 I made the updater refuse overlapping checks and cancel a stalled download, after reproducing both failures against a throttled local server.
The result
A full release takes a median of about 17 minutes over those 19 runs, with a range of 15 minutes 37 seconds to 21 minutes 10 seconds, including the type check and unit tests. Once the build machines are ready, a release runs without me watching it, so my time goes into the code that is being released.
What is still manual
Two things still need me before each release. The macOS build machine has to be started and the Windows signing token unlocked. Rollback is also manual, by uploading the previous installers again and clearing the cache. After each release I check the published installers on a clean machine. The Electron tests are still being written, so they are not in the pipeline yet, and the October updater changes were tested on macOS only. Some dependencies are held at their current versions on purpose, because upgrading them caused conflicts or stability problems.
What I would do differently
Time the manual routine before changing it, so the improvement can be stated as a ratio and not left as an estimate. An earlier version of my own documentation gave an hour before and twelve minutes after. Neither number was measured, so I replaced them with the median above.