Master Branch Must Be Read-Only

Continuous integration is easy. Download Jenkins, install, create a job, click
the button, and get a nice email saying that your build is broken (I assume your
build is automated). Then, fix broken tests (I assume you have tests), and get a
much better looking email saying that your build is clean.

Then, in a few weeks, start filtering out Jenkins alerts, into their own folder,
so that they don’t bother you anymore. Anyway, your team doesn’t have the time
or desire to fix all unit tests every time someone breaks them.

After all, we all know that unit testing is not for
a team working with deadlines, right?

Wrong. Continuous integration can and must work.

What is Continuous Integration?

Nowadays, software development is done in teams.
We develop in feature branches and isolate changes while
they are in development. Then, we merge branches into master.
After every merge, we test the entire product, executing all
available unit and integration tests.
This is called continuous integration (aka “CI”).

Sometimes, some tests fail. When this happens, we say that our
“build is broken.” Such a failure is a positive
side effect of quality control because it raises a red
flag immediately after an error gets into master.

It is a well-known practice, when fixing that error becomes
a top priority for its author and the entire team. The error
should be fixed right after a red flag is raised by the
continuous integration server.

There are a few good tools on the market, which automate DevOps procedures.
Some of them are open source, you can download
and install them on your own servers. For example:
Jenkins,
Go, and
CruiseControl.
Some of them are available as a service in cloud, such as:
Travis,
Shippable,
Wercker, and
many others.

Why Continuous Integration Doesn’t Work?

CI is great, but the bigger the team (and the code base), the more often builds
get broken. And, the longer it takes to fix them. I’ve seen many examples where
a hard working team starts to ignore red flags, raised by Jenkins, after a few
weeks or trying to keep up.

The team simply becomes incapable of fixing all errors in time. Mostly because
the business has other priorities. Product owners do not understand the
importance of a “clean build” and technical leaders can’t buy time for fixing
unit tests. Moreover, the code that broke them was already in master and, in
most cases, has been already deployed to production and delivered to end-users.
What’s the urgency of fixing some tests if business value was already delivered?

In the end, most development teams don’t take continuous integration alerts
seriously. Jenkins or Travis are just fancy tools for them that play no role in
the entire development and delivery pipeline. No matter what continuous
integration server says, we still deliver new features to our end-users. We’ll
fix our build later. And it’s only logical.

What Is a Solution?

Four years ago, in 2010, I published an article in php|Architect
called “Prevent Conflicts in Distributed Agile PHP Projects.” In the article,
a solution was proposed (full article in PDF)
for Subversion and PHP.

Since that time, I used experimentally that approach in multiple
open source
projects and a few commercial ones with PHP, Java, Ruby and JavaScript, Git and
Subversion. In all cases, my experience was only positive, and that’s why
rultor.com was born (later about that though).

So, the solution is simple—prohibit anyone from merging anything
into master and create a script that anyone can call. The script will
merge, test, and commit. The script will not make any exceptions.
If any branch is breaking at even one unit test, the entire branch will be rejected.

In other words, we should raise that red flag before the code
gets into master. We should put the blame for broken tests on
the shoulders of its author.

Say, I’m developing a feature in my own branch. I finished the development and
broke a few tests, accidentally. It happens, we all make mistakes. I can’t merge
my changes into master. Git simply rejects my push, because I don’t have the
appropriate permissions. All I can do is call a magic script, asking it to merge
my branch. The script will try to merge, but before pushing into master, it
will run all tests. And if any of them break, my branch will be rejected. My
changes won’t be merged. Now it’s my responsibility—to fix them and call
the script again.

In the beginning, this approach slows down the development, because everybody
has to start writing cleaner code. At the end, though, this method pays off big
time.

Pre-flight Builds

Some CI servers offer pre-flight builds feature, which means testing branches
before they get merged into master. Travis, for example, has this feature and
it is very helpful. When you make a new commit to a branch, Travis immediately
tries to build it, and reports in GitHub pull request, if there are problems.

Pay attention, pre-flight builds don’t merge. They just check whether your
individual branch is clean. After merge, it can easily break master. And, of
course, this mechanism doesn’t guarantee that no collaborators can commit
directly to master, breaking it accidentally. Pre-flight builds are a
preventive measure, but do not solve the problem entirely.

Rultor.com

In order to start working as explained above, all you have to do is to revoke
write permissions to master branch (or /trunk, in Subversion).

Unfortunately, this is not possible
in GitHub. The only solution is to work through forks and pull requests only.
Simply remove everybody from the list of “collaborators” and they will
have to submit changes through pull requests.

Then, start using Rultor.com, which will help
you to test, merge and push every pull request. Basically, Rultor is
the script we were talking about above. It is available as a free cloud service.