One git engine for reads, another for writes. Here is the engineering reasoning behind our dual-engine approach — and the rule of thumb you can apply to any complex dependency.
The False Choice: Why Our Git Server Uses Two Engines
When you're building something that needs to manipulate git repositories from code, you get told to pick one: a library like go-git, or shelling out to the git binary. Library purists hate subprocesses; subprocess pragmatists don't trust libraries to match the real thing. Most projects pick one camp and live with its trade-offs.
Our git server uses both —
go-gitfor every read operation, and the realgitbinary for every write. This post is the reasoning behind that split, and the general principle it's an example of.
The Rule: Use the Reference Implementation for the Risky Direction
Reads and writes are not symmetric problems, and that asymmetry is the whole argument:
DirectionEnginePrimary DriverWhy This Choice WinsReadsgo-git (In-Process)Speed & ErgonomicsGives typed access to the object database in-process without subprocess spawn overhead or parsing CLI string output.Writesexec git (Subprocess)Strict CompatibilityGuarantees 100% adherence to receive-pack, ref updates, object validation, and protocol quirks by using git's reference implementation.
So the rule is simple: use the reference implementation for the direction where compatibility is non-negotiable; use the library where ergonomics dominate.
What This Looks Like in Practice
The boundary is clean: everything inside the object database on the read side is go-git; everything that mutates refs or accepts client writes is exec git.
Reads (go-git — In-Process)
Code explorer: Trees, blobs, blame, diffs — served fast and cached server-side.
Commit lists & branch pages: Log walks and ref enumeration.
MonkeysAI context: File contents for indexing and PR diffs.
Internal APIs: Endpoints like
GET /{org}/{project}/branches.
Writes (exec git — Subprocess)
Receiving a push (
receive-pack): The critical correctness path.Template operations: Hook-driven repository generation.
Bit-for-bit mutations: Any operation where the result must exactly match what canonical git produces.
The Failure Modes We're Avoiding
Both monoculture options have critical failure modes we wanted to dodge:
All-exec: Spawning a subprocess for every read creates heavy process-spawn overhead on every keystroke in the code explorer. Furthermore, parsing CLI string output is brittle and subject to breaking changes across git versions.
All-library: Reimplementing push acceptance in a library risks subtle drift from real git behavior. "Mostly compatible" is exactly the kind of bug you discover at 2 a.m. from an unusual client.
The General Principle
This isn't a git-specific lesson. The rule generalizes to any complex dependency you build on:
Find the operation where compatibility is non-negotiable, and use the reference implementation for it — no matter how inelegant. Use libraries everywhere else.
Applied to other engineering domains:
Compilers: Use the real compiler for the final artifact even if you maintain a lightweight parser for code analysis.
Databases: Let the canonical database engine handle writes even if you serve in-process read caches.
External APIs: Use the vendor's official SDK for mutations even if you use custom typed structs for read responses.
What It Costs (The Honest Part)
Two engines means two sets of semantics to keep straight. We've paid in three main ways:
Two test surfaces: Our test suite exercises
go-gitreads and real-git writes — particularly at the boundary (e.g., reading back a ref thatexec gitjust wrote).Discipline at the boundary: The team must maintain strict discipline: if it mutates refs, it's
exec; if it only reads, it'sgo-git.Dependency risk:
go-gitmoves at its own pace, requiring version pinning and regular dependency reviews.
Closing Thought
The best engineering decisions are often refusals to fight a false choice. Library or binary? The right answer for us was "yes" — the library for the direction where speed and ergonomics win, the reference implementation for the direction where correctness is everything.
If you ever find yourself defending a monoculture against mounting evidence, ask which direction you can afford to be subtly wrong in. That's your library. The other direction is your reference implementation.
Every MonkeysCloud project ships with 2 free servers and 2 free databases — git hosting, environments, CI/CD, and MonkeysAI included. Try it free
