Freesbe

Blog

Running many AI coding agents on one repo without merge hell

By Freesbe4 min read

Most of Freesbe’s code is written by AI coding agents. That isn’t unusual any more. What’s less common is how many run at once: on a normal day, several agents work in parallel, each on its own feature, each on its own branch, each opening its own pull request. Our repository passed 400 pull requests in under three weeks.

Writing the code turned out to be the easy part. The hard part was the same thing that slows down any large team: merge conflicts.

How we work

Each feature gets its own agent with its own branch and its own pull request. A person describes what they want, the agent builds it, posts screenshots or a root cause for review, and opens the pull request only after a human says yes. Every pull request runs CI, deploys to staging after merge, and reaches production only if staging’s smoke tests pass.

Two rules keep the branches independent:

  • One branch and one pull request per feature. No feature waits on another one.
  • No stacked pull requests. A branch built on top of another unmerged branch turns one review into two and one conflict into many. If work depends on something unmerged, it waits locally and opens its pull request after the first one merges.

The two files everyone touches

When we looked at where conflicts came from, almost all of them were in two kinds of file:

  • Translation catalogs. Freesbe ships in 25 languages, and every feature adds new strings. Every branch appends its strings to the end of the same files, so every pair of branches conflicted on every catalog.
  • The What’s new page. Every feature that people will notice adds an entry at the top, and the page keeps only the newest 10 dated entries, moving the oldest under “Earlier”. Two branches each add one at the top and each move a different one down.

Neither conflict is real. Both sides want to keep both changes. But git sees two edits to the same lines and stops, and with many agents merging in a day, that meant a constant stream of branches going red for no reason.

Merge drivers: teach git what the file means

Git lets you plug in a custom merge driver per file pattern. Instead of merging line by line, git hands your script the common ancestor, your version and theirs, and your script writes the result.

We wrote two:

  • Translations merge string by string. Strings added on either side are kept. A string changed on one side takes that change. Only the same string changed differently on both sides is a real conflict, and that almost never happens.
  • What’s new merges entry by entry, then lays the page out again: the newest 10 entries under their month headings, the rest under one “Earlier” heading.

Both are set up automatically by pnpm install, so every agent and every person gets them without thinking about it.

A bot that only touches what’s broken

After every merge to main, a workflow looks for open pull requests that now conflict, merges main into them with the drivers, and pushes. If a real conflict remains, it comments on the pull request naming the files, and that comment wakes the agent that owns it.

Just as important is what it doesn’t do: it leaves pull requests that don’t conflict alone. Merging main into a branch that doesn’t need it restarts that branch’s CI, and with many branches open that wastes hours of runner time a day.

The lesson we learned by breaking main

Merge drivers run on your machine, not on GitHub. When anyone uses GitHub’s Update branch button, GitHub does a plain merge that knows nothing about our drivers. One day that left the What’s new page on main with entries run together and one too many dated entries, and the next deploy carried it.

So the rule now is: merge locally, always, and the workflow lays out What’s new again on main after every merge just in case. We also learned that GitHub runs no pull request checks at all while a branch conflicts with main, so a branch can sit there with no CI and look fine. The agents now merge and push the moment they see a conflict instead of waiting.

What we’d tell another team

If you’re running AI agents in parallel, or just have a big team:

  1. Find the files that always conflict. It’s usually a handful: translations, changelogs, lockfiles, generated indexes, route tables.
  2. Ask whether the conflict is real. If both sides always want to keep both changes, it isn’t, and a merge driver of 100 lines can make it disappear.
  3. Automate updates only where they’re needed. Rebasing everything on every merge feels tidy and burns CI.
  4. Keep humans where they matter. Our agents never merge their own work or deploy to production. A person looks at the result and clicks Merge.

Agents made the code cheap. The process around the code is what makes it shippable.

Try all of it free with up to 10 people.

No card, no time limit. Invite your team and see what sticks.