Case for a monorepo
🎈 Air
At Air, we’re building apps for capturing, storing, and managing videos. We currently offer an iOS app and a web app that share a good amount of code. This article walks through the steps we took to build a universal codebase and synced design system in a single Git repository.
Goal
My goal was to build a cross-platform codebase that synced with our design system and allowed for rapid code changes to increase our team’s output.
Universal code
I looked at our projects and found plenty of reusable code and shared folders. Our apps share utilities, constants, config settings, i18n strings, linting rules, and React Native components (thanks to react-native-web). With this information, I was able to split our code into reusable packages.
Synced design system
We use Storybook to generate our design documentation. Our docs use Air’s shared components which keeps our design system and code in perfect sync. When a change is made to our components, our apps and docs are updated.
Finding our optimal file structure
First implementation: Practice repos
Our initial code organization strategy was to keep our app repositories separate. We then built functional practice projects that contained essentially reusable code for us to copy-paste into our apps. As we moved quickly, this was an easy way to stay in sync for a small team, but was ultimately not a sustainable solution.
- ✔ Simple
- ✘ Hard to stay in sync
- ✘ Duplicated code
- ✘ Not sustainable
Second implementation: Polyrepo structure
I then built a polyrepo structure, abstracting all shared packages into their own repositories (constants, utilities, config, components, etc.).
Our apps consumed these packages as defined in their package.json
file. This was clean, but led to dependency management chaos. Every time a change needed to be made to a dependency, we were forced to jump into a separate repo, update it, bump the package version (if need be), hop back into our original project, and reinstall the dependency. Another issue we ran into was, because we serve our web app and site on Netlify, we had to create workarounds for reusing our Git credentials to install our private repositories.
- ✔ Clean
- ✔ Reusable code
- ✘ Slowed down development time
- ✘ Dependency management is difficult
- ✘ Issues with Netlify builds
Third implementation: Monorepo
Finally, I used Yarn Workspaces to build a single repo that hosts all our projects and handles our dependencies in a more maintainable way.
- ✔ Clean; all dependencies are in a single
packages
folder - ✔ Reusable code
- ✔ Consistent linting and project configuration files across all packages (
.eslintrc
,.babelrc
,.gitignore
,prettierrc.json
, etc.) - ✔ Cross-platform testing is easy to set up
- ✔ Simple releases with Lerna
- ✔ Allows for rapid development across dependencies
- ✔ Simple dependency management
- ✔ Contains our synced design system
- ✔ Works perfectly with Netlify builds
🏆 Goal achieved
Thanks to our new code organization, we now have:
- A universal codebase with DRY, shared code between projects
- Synced design docs that use our shared components
- Rapid, cross-project code changes and releases in our new monorepo
Though a monorepo worked for our purposes (and for others [Facebook, Google, Twitter, Babel, React Native Web, React Router, Meteor]), be sure to review the pros and cons before implementing one.
Takeaways
- Consistency is underrated. Tools like Prettier and ESLint are spectacular for working with a team across multiple projects.
- Using
lerna import
orgit subtree
carries over a repo’s Git history and combines its commits chronologically with the monorepo’s commit history. We’re still finding the best way to compose our commit messages for the monorepo as commits can span multiple packages. We may try namespacing our commits. (e.g.[web, components] Refactor gallery
) - An exciting aspect of building a business from the ground up is the speed at which we’re able to adopt or reject new technologies. I’m proud of our team for their ability to move quickly and willingness to try new things.
$ >whoami
I’m Chris, a designer-developer that has worked in advertising, in big tech, on Wall Street, and am now building a startup in a Brooklyn warehouse. I care deeply about accessibility and performance in my apps.
Mash the clap (👏) button if you found this article helpful or want to show your support for our startup. Thanks!