← Back to list

Git Is Holding Us Back: A Heretical Take from Someone Who Uses It Every Day

After years of Gerrit workflows, submodule nightmares, and Change-Id archaeology, I’ve concluded Git’s complexity is a design flaw we’ve…

Bruce Wen · 2026-06-29 06:09 · 0 claps · 5.9 min read paywalled
#git #version-control #developer-experience #tooling #contrarian-thinking
Open on Medium ↗
Wiki topics: HIS · History 🔓 · Open Source 🏺 · Archaeology & Anthropology ⚖️ · Law & Justice

Git Is Holding Us Back: A Heretical Take from Someone Who Uses It Every Day

After years of Gerrit workflows, submodule nightmares, and Change-Id archaeology, I’ve concluded Git’s complexity is a design flaw we’ve normalized.

I spend more time fighting Git than writing code.

That’s not a confession of incompetence — it’s an indictment of a tool we’ve collectively decided is beyond criticism.

Every week I watch Change-Ids collide after squash operations, submodules drift out of sync, and pre-push hooks block legitimate work because Git’s internal model leaks complexity into every workflow built on top of it.

Photo by Yancy Min on Unsplash

Photo by Yancy Min on Unsplash

I’m not saying we should abandon Git.

I’m saying we should stop pretending it’s good. We should stop conflating ubiquity with quality, and we should acknowledge that most of Git’s power is accidental complexity masquerading as flexibility.

I’ve used Git across personal projects, open-source contributions on GitHub, and enterprise Gerrit workflows with mandatory code review.

The common thread? Git fights me at every level of sophistication.

The Submodule Tax: A Weekly Ritual of Pain

In my daily work, I maintain a multi-repo workspace where parent repos contain submodules like cicd-config, module-common, and module-tracing.

The rule is simple: commit and push the submodule before the parent, then uplift.

In practice, this means every single push is a multi-step ceremony where forgetting one step — or running nox which auto-updates submodules — creates a cascade of failures.

I've lost count of how many times a pre-push hook has blocked me with "uncommitted changes detected" because nox updated a submodule pointer I didn't ask it to touch.

The "fix" is to git add all submodules and amend them into my commit. This isn't version control — it's bookkeeping. Git submodules are the tool's way of admitting it doesn't actually handle multi-repo workflows, while pretending it does.

The contrarian take: monorepos exist not because they're architecturally superior, but because Git submodules are so painful that organizations restructure their entire codebase to avoid them. We've let a tool's limitation dictate our architecture.

Git submodules are so fundamentally broken that entire architectural movements (monorepos) exist as workarounds.

Change-Id Archaeology: When History Becomes a Trap

Gerrit’s Change-Id system is built on top of Git, and it exposes Git’s worst quality: the assumption that commit identity is simple.

After a git reset --soft and re-commit, the commit-msg hook can regenerate the same Change-Id if the tree hash matches a previously merged commit.

I've pushed to Gerrit only to get "change ... closed" because my fresh commit accidentally reused a dead Change-Id.

The workaround I've developed — generating Change-Ids from date | sha1sum | cut -d’ ‘ -f1 instead of tree hashes — is absurd. I'm working around a tool that's working around another tool's identity model.

Git's content-addressable storage is elegant in theory, but in practice it means tree hash collisions across branches (which happen when branches differ only by submodule pointer) create real workflow failures.

People defend this by saying "you just need to understand Git's internals."

That's the problem. No tool should require you to understand its internals for routine operations. I don't need to understand B-tree page splits to use a database. Why should I need to understand Git's DAG to push code?

# The ritual I perform after every squash to avoid Change-Id collision
git log -1 | grep Change-Id
# Check it's not already merged:
git log origin/master --oneline --grep="<that-id>"
# If collision detected, generate fresh:
git commit --amend -m "$(git log -1 --format=%B | sed "s/Change-Id:.*/Change-Id: I$(date | sha1sum | cut -d' ' -f1)/")"

Git’s content-addressable model creates subtle identity collisions that no amount of ‘understanding the internals’ can fully prevent in complex workflows.

The Hook Industrial Complex

My pre-push pipeline runs stylefix, doccheck, securitycheck, stylecheck, tests, and typecheck — each in its own virtualenv.

On resource-constrained servers, this OOMs and gets killed by signal 9.

Git’s hook system provides no resource management, no parallelism control, no graceful degradation. It’s a raw script execution model from the 1990s with a 2020s workload bolted on.

I’ve built an entire security hook infrastructure that normalizes whitespace (tr -s ' ' to prevent git push bypass), blocks --no-verify, validates Change-Ids, enforces self-review stamps, and checks for leaked secrets.

This infrastructure exists because Git's model of "trust the committer" doesn't match enterprise reality. Every hook is a patch over Git's assumption that the person running commands knows what they're doing.

The industry response has been to build platforms on top of Git — GitHub Actions, GitLab CI, Gerrit — that essentially replace Git's inadequate hook model with their own.

We don't use Git's collaboration features. We use platforms that happen to store data in Git.

Git hooks are so primitive that every serious team builds an entire platform on top to compensate, proving the base tool is insufficient for modern workflows.

The Dirty Secret: Nobody Uses Git, They Use GitHub

When I work on my personal projects on GitHub — tiptap, my tip generator, various utilities — the workflow is fundamentally different from my enterprise Gerrit work.

Not because the code is different, but because GitHub has replaced most of Git with its own abstractions.

Pull requests aren’t a Git concept. Branch protection rules aren’t a Git concept. Code owners files, status checks, merge queues — none of these are Git.

Strip away the platform and what do you have? A content-addressable filesystem with a confusing CLI that has 150+ commands, where git checkout alone does four completely different things depending on arguments.

The Git community's response to this is documentation — mountains of it — which is another way of saying "the interface is so unintuitive that you need a manual for basic operations."

I’ve watched junior developers stare at detached HEAD warnings with genuine fear.

I've seen experienced engineers accidentally force-push to main. These aren't user errors — they're design errors that we've normalized through survivorship bias.

The people who found Git intuitive stayed in software; the ones who didn't may have left the industry entirely.

The success of Git is actually the success of platforms built to hide Git’s complexity — strip those away and the tool is hostile to humans.

What Would Actually Be Better

I’m not suggesting we return to SVN or adopt some hypothetical replacement. I’m suggesting we be honest about Git’s costs.

Every git reflog rescue, every reset --hard to undo a bad merge, every Stack Overflow search for "how to undo X in Git" — these are costs we've internalized and stopped counting.

What I want is a version control system that treats multi-repo as a first-class concept (not submodules bolted on), that has a principled CLI where each command does one thing, that handles identity without content-address collisions, and that provides resource-aware hook execution.

Some of this exists in fragments — Sapling (Meta’s fork), Jujutsu, Pijul — but adoption inertia means we’ll be stuck with Git for another decade.

My practical conclusion: invest in your Git infrastructure layer (hooks, platforms, aliases) because the base tool won’t improve.

Accept that you’re building a version control system on top of a version control system, and budget engineering time accordingly.

In my case, that’s an entire kiro-cli-config repository with skills, hooks, and automation — a meta-project that exists solely to make Git tolerable.

The pragmatic response isn’t to replace Git but to acknowledge its costs honestly and invest deliberately in the infrastructure layer that compensates for its deficiencies.

Conclusion

I’ll keep using Git tomorrow. I’ll fight with Change-Ids, nursemaid submodules, and maintain my hook infrastructure.

But I refuse to pretend this is good.

The emperor has no clothes, and the clothes he’s supposedly wearing are documented in a 500-page manual that starts with “Git is a stupid content tracker.”

Linus Torvalds built Git to solve Linux kernel development — a specific, extreme workflow with thousands of contributors and a benevolent dictator integration model.

The rest of us inherited a tool designed for that context and spent fifteen years building platforms to adapt it to ours.

That’s not elegance. That’s path dependency.

My hope is that the next generation of developers will be less reverent and more critical.

Question the tools. Count the costs.

And maybe — just maybe — build something better.

References:

Explore:


메타데이터
post_id
fbb002b77b47
slug
git-is-holding-us-back-a-heretical-take-from-someone-who-uses-it-every-day-fbb002b77b47
url
https://medium.com/@wenijinew/git-is-holding-us-back-a-heretical-take-from-someone-who-uses-it-every-day-fbb002b77b47
canonical_url
https://medium.com/@wenijinew/git-is-holding-us-back-a-heretical-take-from-someone-who-uses-it-every-day-fbb002b77b47
author_url
https://medium.com/@wenijinew
status
ok
fetched_at
2026-08-08 08:20:06