main (or any similar name) has never seemed like a great branch name. Like many developers, we navigate through different kinds of repositories all the time, from user-facing apps to Open Source libraries. It can be difficult to keep track of how code flows from development to its final purpose when merging our work into a codebase, making naming branches not just a matter of efficiency but affecting the clarity of the code as well.
Every project has its own goal and process, which means that merging code into 'main' has different meanings. For instance, it's not the same to merge a PR into a Jamstack blog that will immediately publish the new content as it is to merge a PR into a finance app that has to go through manual QA before getting published. The same applies to library maintenance: submit a PR to a private library with few users is quite different than to submit it to a widely adopted Open Source library.
That's why we propose naming the default branch of each repository such that it reflects its purpose. For instance, in
effection—a library we maintain—the default branch is
v0 because the code merged there is part of version zero, and we cut releases from it. Another example is our website: the default branch is called
production because everything we merge into it is automatically published to production.
The author and reviewers of a PR that will be merged into
production are now conscious of the aims and purpose in the code in question.
In this article, we present Frontside’s naming strategy for semver libraries and the guiding principle we use for apps repositories. We also share our ideas about changing the name of the default branch of a repository in the form of a helpful FAQ.
We follow the popular Semantic Versioning (Semver) format to issue releases. Semver distinguishes three types of releases:
patch. Although it provides a semantic framework for release names, Semver makes no comment on how each team goes about developing their libraries – there's still plenty of room to decide how you cut your releases. At Frontside, instead of curating each release manually, we tie major versions to branches so we can cut automated minor and patch releases, which ultimately leads to setting our default branch name to a major version branch instead of simply
Releases are often manually put together by the maintainers, who also judge the type of release they're issuing. That means that branches and releases are not coupled: their relationship is curated by the library maintainers on a case-by-case basis. This approach has proven itself effective for many projects, but is an added burden for the maintainers. It becomes especially problematic with monorepos, where several packages with codependencies have to be updated and released at once.
At Frontside, we instead tie branches to major releases: everything that is merged into a
v1 branch must be releasable in a
v1.x.x version. That enables us to use changesets to cut minor and patch releases with minimal effort – even within monorepos.
With our approach having a
main branch is just not helpful. And it's usually confusing in other approaches too: what can you expect to find in
main? the latest released version? a future version? the most popular version? not a release candidate at all? It's impossible to tell by simply looking at the name, and a lot of (unnecessary) work to dive into the code to find out.
Instead of using
main as a default branch, we set our default branch to be the latest major version branch that is currently under development. This means that the default branch will change over time, and that's a good thing, because the evolving name gives contributors clear directions when navigating the codebase.
Unlike libraries, there's no widely popular way of structuring releases for apps. Apps are wildly different across teams and industries not only in how they look, but also in the way they're developed and deployed. Our advice is to think of the branch as a pneumatic tube that takes the code somewhere and to ask what is the name of that place? If putting code in that tube means that it will be shuttled to production, then naming the default branch "production" is a sensible idea because (like with our suggestions about libraries) the name offers a clear indication of the code’s purpose instead of requiring you to fish around to find out what the codebase is trying to accomplish.
You'll soon become obsolete if you keep
master as your default branch. The industry standard for default branches is
main, so we advise you to at least move to that name if you haven't. But we think you’d be wise to shift away from generic names altogether and consider moving to purposeful branch naming.
Perhaps at the beginning. But it's not common for developers to push to
main: in fact it's generally discouraged. Interactions with the default branch are therefore limited: typically to just when cloning a repo, creating a PR, or syncing up with the latest developments.
Everybody working with your codebase will have a concrete understanding of the goal of the repository. From the very beginning when they clone your repo until when they create a PR, everyone clearly understands the purpose and goal of their work.
Github is making it easier for you to change the default branch of your repository. Check out their recommendations for renaming your default branch.
Naming is hard. It's been a challenge in computer science for decades. But what we call things conditions the way we think about them. At Frontside we think it’s time to move away from the “tried and true” generic names – especially when these naming conventions are actually causing inefficiencies and unnecessary work.