Engineering and Infrastructure

Will agents like Git any more than we do?

Lenny Pruss
Rebecca Dodd
Share
May 20, 2026

Disclosure: Amplify is an investor in East River Source Control.

For better and worse, the industry has (mostly) converged on Git for version control. A huge leap forward for distributed collaboration around code, Git also has its quirks: merge conflicts, a confusing number of ‘states’, performance issues at scale. You know the memes. 

For two decades, those quirks have been manageable, mostly adding friction rather than blocking work altogether. Developers grumble and work around it. But the underlying assumption was always that the humans using Git would absorb some of that complexity themselves.

But what happens when agents are writing most of our code?

The short answer, I believe, is: the things that make Git frustrating for humans becomes untenable for agents. This is why so many people, myself included, are excited about Jujutsu (jj), a new Git-compatible VCS that rethinks many of these things from first principles. 

In this post, I’m going to attempt to explain Git’s history and why the way it’s built will create big problems for an agent-first SDLC. Then, I’ll explore why I believe that jj might be (is probably!) the future. 

A brief history of version control systems

In the early days of computing, writing software was slow and painstaking enough that programs stayed small by necessity — a single developer could hold an entire codebase in their head. You didn’t need to track changes across a codebase, because changes were infrequent and you were probably the only person writing them. There was no need to coordinate with other developers, and thus no concept of version control. These were ideal conditions for code as craft: think of Mel, whose carefully hand-optimized programs consistently beat the same code manipulated by the optimizing assembler program.

As languages improved and tooling matured, it became faster and easier to write code. This had a nice consequence: more developers could work on more code together. Development ceased being a completely solo activity — now, you worked on code with your teammates. This is wonderful for many reasons, but introduced a class of coordination problems. How do multiple people collaborate on one codebase? If two developers are both editing the same file, whose version wins? What happens when those changes conflict with one another? There was no established playbook for any of this, and teams had to make it up as they went.

There were attempts at early systems to handle this, like RCS (Revision Control System), which took the simplest approach: file locking. If you were editing something, no one else could touch it until you were done. For a small team working on a small codebase, this was a defensible answer to the coordination problem{{jj-fn-1}}. One person at a time, in an orderly queue. Everybody knows the rules.

The trouble was that file locking created as many problems as it solved. Developers would forget to release locks, leaving teammates blocked on work. And file-level granularity turned out to be far too coarse for how development actually worked: a single file might contain dozens of functions that different developers need to work on simultaneously, with no actual risk of conflict between them. The lock made no distinction. 

Instead of preventing simultaneous work, what developers actually needed was a way to reconcile it.

Enter centralized version control 

Centralized systems like SVN and Perforce were built around exactly that idea. Rather than locking entire files, they introduced the concept of merging. Multiple developers could work on the same file simultaneously. The system would attempt to reconcile their changes automatically — falling back to manual conflict resolution only when the same lines had been edited in incompatible ways. 

It’s hard to overstate what a consequential shift this was. Teams that had been serializing their work through file locks (waiting in line to touch code that might not even conflict with what they were doing) could suddenly work in parallel. Instead of being blocked on access, you were blocked only when your work literally collided with someone else’s. For most day-to-day development, you rarely had to stop at all.

This is a much more graceful handling of collaboration on code than something like RCS, and both SVN and Perforce are actually still in production today. Perforce in particular remains the tool of choice for some game studios dealing with massive binary assets — individual files in the tens of gigabytes, repositories measured in terabytes.

Meet Git: the information manager from hell

Git itself arrived in 2005, in fairly dramatic fashion. The Linux kernel had been managed in a proprietary system called BitKeeper; a license change alienated enough of the kernel community that Linus built a replacement in a matter of days. The first commit message described Git as “the information manager from hell”, which, together with the name Git, says everything about what he thought of what he was building at the time. This was a quick and dirty solution, not a deeply planned or meticulously designed one.

What Git added to the discussion, more than anything, was decentralization. In SVN, there was one canonical repository living on a server somewhere. Every meaningful operation — committing, viewing history, comparing branches — required a round trip to that server. You were always, in a sense, a guest in someone else’s house. 

Git flipped this entirely, because every developer gets a full local copy of the repository; not a pointer to it, or a cache of recent changes, but the whole thing. The entire history of the project — every commit and branch — is on your local machine. This opened up a whole new world of possibilities: committing from an airplane{{jj-fn-2}}, experimenting on weekend projects without a VPN, making hundreds of commits and never touching a shared server until your idea is ready for primetime. 

The psychological effect of this is immensely freeing: working in Git feels like owning your work in a way that centralized systems never quite allow. Your clone is your own house, and you don’t have to clean up until you’re inviting someone over.

Branching, in particular, went from a thing that teams avoided, to the thing teams built their entire workflow around. In SVN, branching was slow and cumbersome enough that most teams reserved it for major releases and nothing else. Merging back was also painful enough that the whole exercise rarely felt worth it for anything smaller. In Git, a branch is just a pointer to a (series of) commit(s). Creating and deleting branches is trivial and instantaneous. You can have dozens of branches going at any given time, each representing a different line of thought, and it costs you almost nothing. 

Git’s approach to branching made the feature branch workflow possible, and by extension, pull requests, code review, and the whole modern collaborative development model that we now take for granted. 

The other major improvement Git brought to the table was resilience. Every clone of a Git repo contains the full project history, which means no single point of failure can wipe out everything — someone, somewhere, has a copy. 

Git’s arrival was significant, but it didn’t exactly become a standard overnight. The tool itself was rough around the edges — Linus built it for his own needs and wasn’t particularly concerned with anyone else’s experience of it at the time. For several years it coexisted with Mercurial, a contemporary that many engineers considered the better-designed system, and with SVN, which still had enormous institutional inertia. It was still anyone’s game.

The turning point in taking Git from a personal project to ubiquitous was GitHub. The web-based UI made several Git features much more accessible, in particular pull requests. And because the GitHub founders were Ruby evangelists, their early adoption of Git ended up making waves in important communities throughout the industry.  

Mercurial never had an equivalent platform push, which turned out to matter more than any of its technical merits. Version control systems are fundamentally collaboration platforms, and GitHub understood that before anyone else.

The trouble with Git

This is not the first blog post to critique Git, and it certainly won’t be the last. Nor am I attempting a vain takedown of a tool that is, at the end of the day, community built and maintained. My goal here is instead to set the stage, via historical context, on why there has been so much movement towards alternatives like jj over the past few years. 

There are broadly two categories of problems that make Git tricky to work with: data structures and UX.

Performance at scale

Git was built around a model of blobs, trees, and commits forming a Merkle-tree-like chain, where each object hashes its parent. At scale, certain operations require walking the entire commit history — things like finding merge bases, computing diffs, checking ancestry — meaning performance tends to degrade as repositories grow.

For startups this usually doesn’t matter; their Git misfortunes are for the most part confined to inscrutable merge conflicts. For teams at Meta or Google scale though, with millions of commits and hundreds of engineers committing simultaneously, this becomes a hard ceiling. Git’s reconciliation algorithms max out at roughly 2-3 merges to main per second in baseline configurations. If that sounds like an edge case to you, bear with me, because it’s not as far away as you might think. 

The state machine (AKA Schrodinger’s file changes)

The UX problems are more universally felt. It’s perhaps the most harangued meme among developers that Git is comically complex. It’s a colloquial rite of passage for a junior developer (what are those?) to lose a good day’s worth of work thanks to a botched rebase.

Thematically, these problems exist because most tools try to hide the gap between how they store data and how users think about their work. Git, largely by accident of its origins, doesn’t. By exposing its internal state model to the user, Git forces developers to reason about a bunch of different modalities before they can do anything useful: is a file untracked? Tracked-but-unstaged? Staged? Committed? Stashed??? 

Compare this setup to Dropbox: if a file is in your Dropbox folder, it’s just synced. That’s it. Git requires manually cranking through a state machine just to get work recorded. Most developers eventually do this on autopilot, but it’s a lot of ceremony. For most people, simply having your work tracked over time is sufficient. 

Conflicts as blockers

There’s also the merge conflict problem. In Git, a conflict is a blocker that you must resolve before you can continue — the system treats unresolved conflicts as invalid states. There’s no way to acknowledge a conflict, set it aside, and return to it when convenient. For most developers, this manifests as an inability to push important changes because of a one-line merge conflict in a meaningless file.

Of course, the Git maintainers are aware of all this, and have been working steadily to improve things. But Git is, as my friend and ERSC founder Ben Brittain calls it, not actually a tool but “an informally specified spec that is half implemented by five or six people”. We’re talking about open-source, load-bearing infrastructure for the entire software industry, with 20 years of backwards-compatibility requirements to contend with, and an ecosystem of tooling built on top of its data structures. The core team is severely constrained in what they can change. 

The trouble with agents and Git

Agents are force multipliers — for better and worse. When it’s trivial to generate tons of new code, Git’s limitations are amplified. If agents are generating and landing code at high velocity, it’s suddenly much more plausible that a mid-sized engineering team might get throttled at 2-3 merges to main per second. It’s no longer a hyperscaler edge case. 

The same goes for merge conflicts: with a team of agents, suddenly one engineer is creating the merge conflicts that would typically only plague much bigger orgs.

On the ergonomics side, the same state machine complexity that makes Git frustrating for developers creates reasoning overhead for agents. An agent working in Git has to track modal file states, figure out what needs to be staged before committing, and decide whether to stash or commit before switching context. That’s still cognitive load, even if it’s a token burden rather than a personal one. You want to constrain the number of decisions an agent has to make about version control, keeping it focused on the actual problem it’s trying to solve.

As agents contribute more code, provenance also gets slippery in Git. When you want to understand why a piece of code looks the way it does, Git’s commit model makes fine-grained tracing difficult. Knowing not just what changed but how a change evolved — who touched it, where it moved in the graph, what was split off or amended — becomes increasingly valuable when the answer to “Who wrote this?” is increasingly “An agent, probably.”

At this point you will be unsurprised that I believe there’s a better way.

Enter jj: Version control without the ceremony

Jujutsu (or jj) started in 2019 as an experiment towards a “next gen VCS” by Martin von Zweigbergk while he was working on version control tooling at Google. Given Git’s trouble with performance at scale, the giants often end up hacking existing VCSs to death or, in the case of jj, rolling their own version control systems{{jj-fn-3}}. 

Google’s primary VCS is an internal system called Piper, built in response to scaling issues with Perforce{{jj-fn-4}}. The working copy for their massive monorepo was still too big for a local copy, leading to a custom virtual file system, and later, a Mercurial alternative called Fig. jj also draws heavily on Mercurial’s design philosophy and takes direct inspiration from how Google and Meta have each approached internal version control at scale.

 jj has since become a healthy open source project with some high-profile proponents and is being standardized across Google’s internal version control stack.

The core difference between jj and Git is that jj eliminates the staging area entirely. Every change is always committed. You save a change, and it’s tracked — there’s no notion of staging or stashing. Just like your Dropbox or Google Drive, your working copy is always in a valid state. All the little incantations and rituals you have to remember for saving your work with Git are just abstracted away.

Several other design choices emerge naturally from this:

Undo as a primitive 

jj maintains an operation log — a secondary data structure tracking every action you’ve taken. Undoing a rebase is one command. Accidentally blow something up: undo. 

Compare this to Git, where a rebase rewrites history. There’s no magic ‘undo’ button, and newcomers might lose days of work because they don’t know how to hunt through the reflog for the pre-rebase state — a cryptic list of HEAD movements where you hope to spot something like “rebase finished” and reset to an earlier entry.

Anecdotally, no one has ever lost data using jj. Git users, by contrast, have all lost data at some point.

First-class conflicts 

In jj, conflicts aren’t blockers — they’re represented in the underlying data structures, which means the system can reason about them rather than just flagging them as invalid. You can have a conflicted commit, move it around the graph, come back to it later, or hand it off to an agent to clean up. You may routinely have multiple conflicts in your repo simultaneously, none of which interrupt your work. In Git, that’s simply not possible.

Making conflicts first class also makes auto-rebase more robust. When Mercurial implemented automatic rebasing of amended revisions, unresolved conflicts broke the operation, so the only option was to warn users and bail out, leaving them to clean up orphaned revisions in the log. Since jj’s conflicts are first-class, auto-rebase just works, regardless of conflict state. It’s a good example of something jj does better generally: rather than working around an edge case, redesign the underlying model so the edge case stops being one.

I've spent many years of my career working on version control. What I like most about Jujutsu is how it has non-obvious solutions to UX problems that we've run into in the past. What most people may not realize is that there are many novel features which all interlock to make it easy to use.

Rain, engineer at Oxide Computer Company and a former VCS developer who worked on Mercurial at Meta, on jj

Change identity and provenance 

In jj, a change has an immutable identity. You can trace how a specific change evolved over time — how it moved, what was split off from it, what was amended. For agent-driven development, when you want to understand where a piece of code came from, jj gives you the tools to find out. When debugging, an agent that can trace how a change evolved, rather than reasoning from a static snapshot of the current state, tends to work through problems considerably faster.

For agents specifically, the always-committed model turns out to matter a lot. Simply adding “always use jj, never use Git” to a global CLAUDE.md is sufficient for agents to figure out the tool and work with it effectively. The simpler the state model, the less context the agent spends reasoning about version control mechanics.

Swappable backend

jj is a meta version control system: it builds its own data structures on top of a storage backend — and today, that backend is Git. Git becomes a dumb storage layer. jj handles everything that the user touches, and persists its state using Git under the hood. You don’t have to sacrifice compatibility with GitHub, GitLab, or anything else in the existing ecosystem. This is what makes jj so frictionless to adopt: you could start using it at work without anyone even knowing, because as far as the rest of your infrastructure is concerned, it’s still Git. 

Using the Git backend gets you most of the ergonomic improvements — the always-committed working copy, first-class conflicts, the operation log — but it doesn’t fully solve the collaboration-related data structure problems. Git’s merge throughput ceiling is still there. The performance constraints at scale aren’t solved by a better interface. The backend being swappable though, means those constraints aren’t necessarily permanent.

Where we go from here

One thing is clear: the future holds a lot more code. Agents are already writing a share of what gets committed to repositories today, and that share will go up. Everything that makes Git frustrating now gets more pronounced as the volume increases. In this scenario, version control is more important than ever.

A core belief that I hear from several of my founders is that things that are good for agents tend to be good for humans, and vice versa. The jj story is a good illustration of why. The design choices that make jj a pleasure to use (the always-committed model, first-class conflicts, and so on) weren’t made with agents in mind, but instead because the Git model was unnecessarily complicated for humans. Simplifying the model for humans makes it dramatically easier for agents to reason about. In this sense, agent-first design is not particularly different than…design.

Steve Klabnik (who wrote one of the more widely read jj tutorials and works at East River Source Control) makes the case that jj is unusual among tools: while most choices involve a tradeoff, jj is both simpler and more powerful than what it replaces. The performance ceiling that remains is the next frontier, and jj’s pluggable backend architecture is a way in. East River Source Control is building in this direction: a backend designed for the throughput and scale that high-velocity development will require, with jj as the interface.

The cost of trying jj is low. You can get started here. And if the next-gen VCS for humans and machines sounds interesting to you, you can join East River Source Control’s early access list.

Authors
Lenny Pruss
Rebecca Dodd
Editors
Justin Gage
Acknowledgments
Thanks to Ben and David from ERSC for contributing source material to this post.
You’re on the list, check your inbox.
Oops! Something went wrong while submitting the form.
1

1

If you’ve had the unfortunate experience of using Webflow, this is still how it works today.

2

2

In practice my Wifi never seems fast enough to push while airborne.

3

3

Meta also forked Mercurial into what eventually became Sapling, a divergence Meta formally acknowledged when they renamed it around 2021-2022, by which point it had drifted so far from upstream Mercurial that the original name had stopped making sense.