Back to Ideas 6 min read

A Branch Is Inventory

E
EkoHacks Team
·

We shipped a feature in three slices. Plan columns and a resolver first. Then a route letting an administrator set a cohort's plan. Then the quota itself, refusing a sixth member on a free cohort. Each slice was small, each was reviewable, each was independently useful. As a piece of work breakdown it was good.

The second and third slices were both cut from the first, and they were open at the same time. The second merged. The third, when its turn came, was conflicting.

Nothing was done carelessly. The slicing was sound and the branches were short by most standards, alive for about a day. And one day of divergence was enough to produce a conflict in a file neither author had opened. That is the number worth sitting with, because it tells you the tolerance is much tighter than it feels.

The word for code on a branch

Manufacturing has a term for material that has been paid for, is not yet a product, and is sitting in a building: inventory. It looks like an asset on a naive reading, because money was spent to create it. It behaves like a liability. It occupies space, it must be tracked, it hides defects until someone opens the box, and it loses value while it waits.

Code on an unintegrated branch is inventory. It has been paid for. It is not yet part of the product. It is stored somewhere, it must be tracked, the defects in it are undiscovered, and every commit anyone else lands on the trunk devalues it slightly, because it is now written against a world that has moved.

A branch is not progress. Progress is what happens when the branch stops existing.

Divergence has two dimensions

How much reconciliation you will eventually pay for is roughly the product of two things.

Time. The longer a branch lives, the more trunk commits it must eventually be squared with. This grows linearly and feels like it grows faster, because the commits stack up in unfamiliar parts of the codebase.

Surface. The more files a branch touches, and the more of them are files other people also touch, the higher the chance of overlap per unit of time.

Multiply them and you have your expected pain. What makes the second slice and third slice collide was a small time and a wide surface: a generated document that every change to the test suite rewrites is, by construction, a file with maximal overlap. Any two branches touching any tests at all will contend for it.

This suggests two levers, and teams almost always reach for the wrong one first. You can try to reduce surface, by dividing the codebase into territories, by rules about who edits what, by discouraging people from touching shared files. That is how you get a codebase nobody is allowed to improve. Or you can reduce time, which requires nothing of the code and everything of the habit.

Integrate before it is finished

The objection to integrating every few hours is always the same, and it is a good objection. The work is not done. The quota is half implemented. Merging it into the trunk would put an incomplete feature in front of users.

This confuses two different kinds of done, and untangling them is the unlock.

Being functionally ready means the feature does what a user needs. Being technologically ready means the code compiles, the tests pass, and the trunk is in a state that could be built and shipped. You cannot integrate work that is not technologically ready. You can absolutely integrate work that is not functionally ready, and you should, several times a day.

The rule for whether a branch can go in is not "is the story finished". It is: does it build, and does the whole test suite pass? A half implemented quota, with the resolver written and tested and the route not yet calling it, meets that bar. Nothing is user visible. Nothing is broken. And the thousand lines of context it is written against are now the same thousand lines everybody else is working from.

Under test driven development you are never more than a few minutes away from a state that satisfies this, because that is what the red green cycle produces: a green bar, every few minutes, by design. The reason continuous integration and TDD are usually described together is not fashion. TDD is what makes integrating this often physically possible.

The rule that sounds absurd

Many teams practising this agree that you must integrate before you go home. Not "should". Must. And they attach a consequence that reads, the first time you meet it, like a joke: if you cannot integrate, throw the day's work away and start again tomorrow.

Sit with it and it stops being a joke.

If you have been working test first, you were green a few minutes ago. If you now cannot reach a state that builds and passes, something has gone wrong that is larger than a typo, and the distance between you and safety is not the few minutes you think it is. The code you would be discarding cost you an afternoon. The debugging you are about to embark on, in a tangle you no longer understand, at the end of a day, will cost you more, and it will produce something you trust less.

There is a version of this that is easier to accept. Not "throw away a day", but: when you are badly stuck, revert to the last green commit and redo the work. The second attempt is nearly always faster, because you now know the shape of the problem, and it is nearly always cleaner, because you are no longer carrying the three abandoned approaches you tried along the way.

The reason this rule can exist at all is the same reason the whole practice can exist: the thing you are reverting to is at most a few hours old.

Say it out loud before you do it

One more piece of common sense that costs nothing. Collisions cluster around wide ranging changes. Renaming something used everywhere, moving a module, changing a signature that thirty files depend on.

When you are about to make a change like that, tell the team first, so they can integrate whatever they are holding before you land it. Then land yours quickly. The alternative is that four people each discover, separately, at different hours, that the thing they were building against has been renamed.

This is not a process. It is a sentence in a chat channel.

What we should have done

The third slice was cut from the first while the second was in flight. The moment the second merged, the third was working against a trunk that no longer existed, and nobody noticed, because there is nothing in the tooling that notices. Git will not tap you on the shoulder to say that the ground moved.

The correct move was not to resolve the conflict better at the end. It was to bring the trunk into the branch the moment the second slice landed, an hour's work at most, at a point where the only thing to reconcile was one generated file that needed regenerating anyway.

We did not, because integrating is something we do when we open a pull request, rather than something we do while we work. That habit has a name, and the next post is about why it is the weaker of the two available practices, and why almost every team using a forge has adopted it without ever choosing it.


The practice of continuous integration described in this series is set out in the continuous integration chapter of The Art of Agile Development by James Shore and Shane Warden.

E

Written by

EkoHacks Team

More from Ideas

·6 min read

The Gate Went Red, and It Was Right

We added the missing check on our API specification, pushed it, and it failed on its first run. Not because anyone had forgotten to regenerate the file, but because the file was never a function of the source. It depended on whose environment last produced it, and only a machine with no environment could ever have noticed.

E
EkoHacks Team
·6 min read

Ready to Ship, Not Ready to Release

Most teams have a hidden delay between saying done and being able to ship. The goal is to be technologically ready to release at any moment, even when the feature is half built. Why our deploy workflow ships the commit that was tested rather than the newest one.

E
EkoHacks Team
·6 min read

Never Break the Build

If the software worked five minutes ago, only five minutes of change can be to blame, and debugging turns into reading. That is what a green trunk buys. It is bought by agreement, not by tooling, and one of our own checks turns out to be incapable of failing.

E
EkoHacks Team
EkoHacks

EkoHacks is a coding institute teaching employable software engineering through real projects. We build civic technology and train developers, born in Nigeria, launching in rural Greece. Global tech, local impact.

Stay Tuned

Be the first to hear about new dojos, insights, and opportunities from EkoHacks.