Back to blog
ai

40 Developers on One App. What I Learned Is Changing How My AI Agents Work.

By Youcef EL KAMEL
9 min read

Git stacking, from chaos to candlestick

Confession: my Git repo was a plate of spaghetti. Three AI agents and myself pushing features simultaneously, and the history looked like a weather map after a hurricane.

Then I did a client engagement. 40 developers. Two repos (long story…). And their Git tree looked like a candlestick chart, clean, readable, every commit on a single straight line.

I stole everything. Not the tools. The method. And today, it’s what allows my AI agents to work autonomously on the same app without stepping on each other’s toes.

Here’s the full journey.

The chaos before

When you work solo or with 2-3 agents, Git is manageable. You commit, you push, you live your life.

But when features pile up and everyone works in their own corner, everything changes. Each feature lives in its branch. Each branch drifts from develop. And when it’s time to merge…

  • Cross-merges : feature A merges develop, feature B does the same, each merge creates a node in the history
  • Cascading conflicts : resolving the same conflict 3 times because 3 branches diverge
  • Unreadable tree : impossible to understand who did what
  • Duplicated effort : merging shifts the burden to whoever merges, usually me

At one point, I looked at the Git history and saw a plate of spaghetti. Not a DAG. A plate of spaghetti.

The lesson from 40 developers

Arriving at this client, I expected the worst. 40 devs on two repos should be total chaos, right?

Wrong. Their secret: a Git discipline nobody had ever taught me. Not a magic tool, a set of rules everyone follows, religiously.

Rule 1: always start from the right base

Every new feature starts from the latest develop. Not another feature branch, not a local snapshot. develop is the source of truth.

Rule 2: systematic rebase, never merge

The golden rule: never merge develop into your branch. Always rebase before every push and every PR. Rebase replays your commits on top of the latest changes. The history stays linear.

Rule 3: squash first, then rebase

If a dev has 15 WIP commits, they squash first. Why? Resolving a conflict on 1 squashed commit = once. The same conflict across 15 commits = 15 times. Squashing before rebasing is non-negotiable.

Rule 4: force push, but safe

After a rebase, a normal push is rejected. The only allowed option: --force-with-lease. Never bare --force, it would overwrite someone else’s work. --force-with-lease refuses the push if someone pushed in the meantime.

Rule 5: one branch = one developer

Never two people on the same branch. Otherwise, one person’s squash + force-push erases the other’s work. It’s a cultural rule, not a technical one.

The result: git log --graph shows a straight line. Each commit = one ticket. git bisect works in 30 seconds. Reviews are clean.

Git stacking: stack instead of merge

But the real revelation was stacking. Instead of parallel branches that eventually get merged, devs stack their changes sequentially.

Before (parallel branches + merges):

develop: A → B ──────────── M1 ────── M2 → C
 ↘ ↗ ↗
feature-x: X1 → X2 ─────┘ /
feature-y: Y1 → Y2 ─────────┘

After (stacking + rebase):

develop: A → B → X1' → X2' → Y1' → Y2' → C

Each layer in the stack is a distinct PR. The first lays the foundation, the second builds on top. Reviews happen in parallel, but the history stays linear.

It’s this concept, popularized by Stacking.dev, that changed everything for me. No complex tools needed. Just discipline.

From 40 developers to my AI agents

This is where it gets interesting. I realized that the same principles that allow 40 humans to collaborate without chaos apply exactly to my autonomous AI agents.

Today, when I launch an agent on a feature:

  1. It always starts from develop, never from a stale base
  2. It rebases before every push, history stays clean
  3. It squashes WIP commits, conflicts resolved once, not fifteen times
  4. It pushes with --force-with-lease, built-in safety
  5. One agent = one branch, never two agents on the same branch

The result? My agents work in parallel on the same app. Each in its own branch. Each rebasing regularly. And when it’s time for review, I’m validating code that’s always up to date, not diffs on a base that’s 3 days old. Every PR is rebased on the latest develop, so I review exactly what will be merged.

Important note: rebasing, especially when there are conflicts to resolve, requires an agent running a thinking-mode model (extended reasoning). A standard model will struggle to resolve conflicts intelligently. A thinking model analyzes the context, understands the code’s intent, and produces a clean rebase. Don’t trust this to a basic model.

Streamlined review: up-to-Date code, clean previews, targeted E2E tests

This is where the method really pays off. Because every agent rebases systematically before requesting review, I always get:

  • A clean diff against the latest develop, not a diff polluted by hundreds of unrelated lines
  • A readable history, easy to pinpoint exactly which commit introduced what
  • A web preview of the feature, the agent can build a preview version of the branch, I test it directly in the browser

And here’s the thing that saves me the most time: from that clean preview, I can launch an autonomous E2E tester agent that knows the semantics of every widget in the app. It tests the specific version of the branch, not prod, not develop, but exactly the feature under review. Game changer.

The tester agent:

  • Knows the Semantics of every Flutter widget
  • Walks through critical user journeys
  • Verifies the feature doesn’t break anything existing
  • Sends me a report before I even look at the code

The full cycle: dev agent → rebase → preview → E2E tester agent → AI review → I approve. This deserves its own article, subscribe to not miss it

Git worktree: multi-Tasking without context loss

Another concept I brought back from that engagement: worktrees. When you need to switch between two urgent features, git stash + git checkout = lost context.

Worktrees let you have two working directories in parallel, each on its own branch. One worktree per active feature. Each has its own file state, its own index.

For my agents, it’s even more powerful: each agent can have its own worktree, work independently without ever interfering with the others. And cleanup when merged.

Why merging shifts the problem

The classic question: merge or rebase? After seeing the 40 devs in action, the choice is clear.

MergeRebase
HistoryMerge nodes → unreadable treeLinear → clean candlestick
ConflictsResolved once, but permanent nodeResolved during rebase, then clean
BisectBroken by merge commitsWorks in 30 sec
Mental load”Who merged what and when?”Simple sequential reading
For agentsMore noise, polluted contextOne clean stream to understand

Merging shifts work to whoever merges. Agents do their job in their branches, and at merge time, I deal with the chaos. With rebase, each agent handles its own integration. Work is distributed.

And this disciplined rebase work streamlines the entire downstream chain: the AI review sees a clean diff, E2E tests run on a targeted version, and I validate in minutes instead of spending 45 minutes untangling an incomprehensible diff.

The tools out there (And why i use none of them)

Stacking is a concept. Tools that automate it exist, but none replaced a solid homegrown workflow.

Graphite is the most complete turnkey solution, CLI, VS Code, merge queue, AI code review. Used at Shopify, Duolingo, Snowflake. But it costs $20-40/user/month and adds a dependency.

Git Town is lighter, a single git town sync replaces a manual fetch/rebase/push sequence. Interesting on paper, but a well-configured skill for my agents does the same job.

Then there’s --update-refs, available since Git 2.38. It automatically updates all branches pointing to rebased commits. Without it, manual stacking = tedious recursive rebasing. With it, native stacking becomes viable. No subscription. No dependency. Just Git.

The takeaway: you don’t need paid tools. The 40-dev method relies on discipline, not tooling.

What actually changed

BeforeAfter
Git treeSpaghettiLinear candlestick
git bisectUnusable30 seconds
Conflicts15× the same one1× per rebase
Code reviewPolluted diffClean, up-to-date diff
Review time30-60 min5 min + AI review + E2E test
AI agentsNoisy context, errorsClear, predictable instructions

The biggest difference: my agents understand the repo. A linear history = clean context. An agent can trace any bug by walking up a straight line. No need to guess which merge introduced the problem.

Resources

What I learned from that client is that large-scale collaboration relies on simple principles applied with discipline. Not magic tools. And those same principles completely transform how my AI agents work autonomously, from dev to review, all the way to targeted E2E testing.

If this kind of content speaks to you, raw dev lessons, zero theory, from someone who ships apps every day, join the newsletter below

#Git #stacking #AI agents #collaboration #rebase #mobile development #team #autonomy #workflow #code review