Two simple techniques to make your release process more asynchronous and decentralized

How following simple conventions can remove the need for unscheduled synchronous meetings to release new versions of your apps.

The Automattic Apps Division has team members distributed across 22 timezones. When it comes to shipping new versions of our apps, that spread makes the traditional synchronous and meeting-driven approach impossible.

Being a distributed team forced us to put processes in place that allow work to happen in an asynchronous and decentralized way without the need for everyone to be online simultaneously and with no ad hoc decision-making.

In this post, I want to share two techniques we use to enable release managers to start the release process, a phase we call code freeze, for a new version on a regular schedule without synchronous input from the feature teams: milestones and labels.

Note: we host the code for our open-source apps on GitHub, so the implementation details will be specific to that platform. GitLab and Bitbucket (via Jira) have similar tools.


How can a release manager know if all the pull requests scheduled for the release they’re about to code freeze have been merged?

PR authors add the milestone for the version in which their work is supposed to ship, and GitHub lets us filter pull requests by milestone. This way, the release manager can immediately know if all the necessary work already landed in the main branch. No need to ask people, “is everything ready to go”?

We use Peril, a hosted version of Danger, to remind authors to add a milestone to their PR.

What happens if there are pull requests still open on the code freeze day? That’s when labels come into play.


Alongside a milestone, each PR should have at least one label specifying the kind of change it introduces.

Like for milestones, we have a Peril check to remind authors to add labels.

The combination of a milestone with different labels allows the release manager to decide whether to delay the code freeze or reschedule open pull requests to the next version.

Let’s consider a simplified example with only two possible labels, “bug” and “enhancement,” and let’s say we’re about to code freeze version 1.2.3.

milestone \ labelbugenhancement
1.2.3wait before code freezeno need to wait
next versionno need to waitno need to wait

Only when an open PR has the current version milestone and the “bug” label will the release manager ask the author for extra input. If the fix is on its way, they’ll wait for it. Otherwise, they’ll go ahead without it and ship a new beta once it’s ready.

Sometimes, the author of a PR that’s getting close to the code freeze date will mention the release manager in a comment with a rough ETA to give them a heads up and enable them to decide whether to wait or not. We also have a cron job that, a couple of days before a code freeze, will look for open PRs for the scheduled milestone and post a Slack message to nudge the team to review and merge them before the deadline.

These workflows are always evolving, adapting to our changing needs, and becoming more and more sophisticated. Recently, we introduced a new Peril check to post a warning comment when a developer opens a PR with a milestone that’s within four days of its code freeze date.

Wouldn’t it be easier to cut the release at the predefined time regardless of open PRs, and ship the left-overs in subsequent betas? Shipping a new beta comes with an unavoidable overhead. We have automated the deployment work to the point that we only need to run a single Fastlane command, but our Excellence Wranglers still need to go through the app from the start, and every new build is a new download that our beta testers have to make. If an open PR will be ready in a few hours, it’s worth waiting for it.

We developed our milestones and labels process because our distributed setup demanded an asynchronous way to wrangle decision making when preparing an app release, but you don’t have to be a distributed team to put these kinds of systems in place.

Every team, distributed or co-located, can benefit from the introduction of asynchronous processes. Decentralizing decision-making with clear guidelines and shared context removes bottlenecks and enables individual team members to work effectively regardless of whether their peers are online.

What are your favorite tactics and strategies to streamline internal processes, such as publishing a new version of the app? Leave a comment below. I’d love to hear from you.

Published by Gio

Mobile DevOps Engineer at Automattic / Aspiring writer. Author of Test-Driven Development in Swift (Apress, 2021). When I'm not playing with my kids or working, I like reading and writing, both software and words.

Leave a comment