Why graphite.dev is a monorepo

Greg Foster
2 min readOct 11, 2022

--

I worked with my cofounder Tomas to architect Graphite’s codebase as a monorepo from day one, for the sake of simplicity. The purpose of good software architecture is to reduce complexity — where complexity is defined by how hard the system is to change. I need our codebase to be simple because I need our rate of change to be as fast as possible.

Software complexity comes in three forms — change amplification, cognitive load, and unknown unknowns. Building within a monorepo — as opposed to multi-repo — has helped simplify each axis. The tradeoffs here are so straightforward that I’ve come to view startups that engineer in private multi-repos as having bad technical judgment.

Graphite’s monorepo reduces change amplification because it makes abstraction cheap. Multi-repo systems are plagued with needed to publish shared resources to external sources of truth, amplifying the number of steps an engineer needs to take when coding. In contrast, monorepos skip intermediate publishing steps. Build configurations can inherit from a central definition, and private libraries can import one another without publishing.

Secondly, Graphite’s monorepo keeps engineers’ cognitive load to a minimum. There’s only one workspace I need to open in my editor. There’s one build command, and one git repository to make commits to. I find conventions (such as implicate file name patterns or encoded as linters) tend to develop on a per-repository basis. While multi-repos allow engineers working in dispart codebases more stylistic freedom, they incur a high cognitive load when switching contexts. By centralizing all of Graphite’s code in one place, we increase the company’s code consistency and therefore the ease with which a ramped-up engineer can change it.

Lastly, we have the problem of unknown unknowns. Multi-repo architecture obscures dependencies. If I adjust the interface or implementation in one library, it’s nontrivial for me to understand how I’m affecting downstream use cases. I can test the library and publish a change, but it’s possible that call sites in other repos need to update their implementations in subtle ways. Perhaps the call sites never even bump their import versions. By colocating code in a monorepo, I make it easy to see and test both implementations and call sites. This results in fewer regressions and faster migrations.

Across Graphite’s architecture, we want simplicity and easy change. In a world where a startup can engineer its codebase as multi-repo or monorepo, it’s a no-brainer for us to keep as much as possible in one place. The two exceptions to this rule are privacy and privilege. If there is a subset of code I want to make public, or that I need to restrict permissions to, I’ll break it out of the monorepo. I believe these cases shouldn’t be preempted, and rather one should build as much as possible in a monorepo until a need to extract arises.

--

--

Greg Foster
Greg Foster

Written by Greg Foster

graphite.dev cofounder, former airbnb infra engineer

No responses yet