Git Submodules vs. Monorepos: What’s the Best Strategy?
Learn when to use Git submodules, when to avoid them, and how to structure large projects using a monorepo approach.
Git Submodules vs. Monorepos: What’s the Best Strategy?
Learn when to use Git submodules, when to avoid them, and how to structure large projects using a monorepo approach.
When your project starts growing, multiple tools, shared libraries, or even separate apps. You’ll hit a key question: how do I organize all this code?
Two common strategies are Git submodules and monorepos. Both help manage complex setups, but they work very differently. In this article, we’ll break down what submodules actually are, when they make sense, and how monorepos can keep everything under one roof without extra Git tricks.
Let’s dive into the pros, cons, and real-world use cases of each!
What You’ll Learn
- What a Git submodule is and how it works
- The pros and cons of using submodules in real projects
- When to avoid submodules and use a monorepo instead
- How to structure a clean, maintainable monorepo with folders
- Real examples of each strategy in action

What a Git Submodule Is (And How It Works)
A Git submodule lets you include one Git repository inside another. Think of it like nesting a separate project inside your main one. They stay linked, but each has its own version history.
Instead of copying the code manually, you add the sub-repo as a reference to a specific commit. That means your main project will always point to the exact version you chose, not whatever is latest.
How it works:
- You run git submodule add <repo-url> path/
- Git clones the external repo into that folder
- Your main repo tracks the commit ID of the submodule, not the whole repo
- When someone else clones your repo, they’ll also need to run git submodule update — init — recursive to pull in the submodule’s content
Example use case:
You’re building a game that uses a custom shader engine. Instead of copying the engine code, you add its repo as a submodule. Now you can update it separately, roll back if needed, or use the same engine across multiple games.
# Step 1: Initialize a Git repo (if you haven’t already)
git init
# Step 2: Add the submodule
git submodule add https://github.com/example/lib-example lib/
# Step 3: Stage everything (main repo will track .gitmodules + the submodule ref)
git add .
# Step 4: Commit
git commit -m "Add submodule"
The Pros and Cons of Using Submodules
Pros
- Modular Codebase Keeps external tools or libraries separate from your main code.
- Version Locking You choose the exact commit your project depends on. No surprise updates.
- Reusability The same submodule can be used across multiple projects without duplication.
- Clean History Keeps the submodule’s Git history out of your main repo’s log.
Cons
- Extra Commands You have to run git submodule update — init after cloning or pulling.
- Easy to Forget Submodules can fall out of sync if teammates don’t manage them properly.
- More Git Complexity Beginners often run into issues pushing/pulling with submodules involved.
- Detached Development You can’t edit submodule code freely unless you cd into it and manage it separately.
TL;DR: Great for shared, stable dependencies. Painful if you’re actively developing both projects at once.

After running git submodule add, Git stages two new files: .gitmodules and the submodule folder itself. These need to be committed to fully track the submodule.
When to Avoid Submodules and Use a Monorepo Instead
Submodules sound useful, but they’re not always the right move. If you’re constantly working across multiple parts of a project especially with frequent updates, submodules can slow you down.
Avoid submodules if:
- You’re editing both the main repo and submodule often It becomes a hassle to commit in two places and keep them in sync.
- You want smooth onboarding for teammates Forgetting to run git submodule update — init is common and breaks things fast.
- You need simple CI/CD or deployment Many pipelines need extra config to deal with submodules properly.
- You don’t need version pinning If you’re actively developing all the parts together, version locking isn’t a benefit.
Use a monorepo if:
- Everything’s under the same team
- Projects are tightly connected
- You want one commit history and easy local dev
- You want to avoid Git complexity for newcomers
Example:
A company building a frontend, backend, and shared utils. Instead of breaking them into submodules, they use:
/web-app
/frontend
/backend
/shared
It’s cleaner, centralized, and easier to manage for teams shipping fast.
How to Structure a Clean, Maintainable Monorepo
A monorepo is one Git repository that holds multiple projects or components. No submodules, no external links just folders and clear boundaries.
Basic structure:
/my-monorepo
/frontend
/backend
/shared
/scripts
/docs
Each folder should be treated like its own mini-project:
- frontend might have its own package.json
- backend could have a requirements.txt or CMakeLists.txt
- shared holds utils used by both
- scripts handles automation or setup tasks
- docs stores Markdown files or diagrams
Tips for clean monorepos:
- Keep README.md files in each folder
- Use naming conventions (like /lib-, /service-) for clarity
- Avoid cross-folder spaghetti — treat each part like an isolated package
- Use tools like Lerna, Nx, or Turborepo for larger JS monorepos
- Use relative imports or well-defined interfaces between folders
Bonus (for advanced setups):
- Add a top-level script (run_all_tests.sh, build_all.py) to simplify CI/CD
- ckSet up pre-commit hooks to lint or test only changed folders

A simple monorepo layout using tree -L 2. Each folder represents a separate part of the project — frontend, backend, shared code, and docs — all managed in a single Git repository.
Real Examples of Each Strategy in Action
Submodule Example
Use case:
You’re building a C++ game and want to include a third-party physics engine that updates on its own.
How it’s used:
git submodule add https://github.com/physx/physx-engine external/physx
Now you can:
- Keep the engine in your repo
- Pull updates manually when needed
- Avoid cluttering your main commit history
This is common in game dev when reusing custom tools or open-source libraries across projects.
Monorepo Example
Use case:
A SaaS company has:
- A frontend in React
- A backend in Python
- Shared validation logic used by both
Instead of splitting them up, they organize everything like this:
/company-app
/frontend (React app)
/backend (FastAPI server)
/shared (Validation, utils, types)
Benefits:
- One repo to clone and work in
- Easy coordination between teams
- CI/CD can run tests for all parts in one pipeline
This is common at places like Google, Facebook, and many startups that prioritize velocity and shared tooling.

The babel/babel GitHub repo is a true monorepo. It organizes everything from codemods to scripts and packages in a single codebase.
Wrap Up
Both Git submodules and monorepos solve the same problem: organizing code across multiple components. But they do it in completely different ways.
Use submodules when:
- You need to include external repos
- You want strict version control over shared dependencies
- The subproject is developed separately and reused across projects
Use a monorepo when:
- You control all parts of the codebase
- You want simpler workflows and collaboration
- The projects are closely tied and evolve together
Pick the strategy that fits your team, your tooling, and how often you touch each part of your codebase. In the end, it’s all about making development smoother, not harder.
메타데이터
- post_id
- caa5de25490b
- slug
- git-submodules-vs-monorepos-whats-the-best-strategy-caa5de25490b
- url
- https://medium.com/@fulton_shaun/git-submodules-vs-monorepos-whats-the-best-strategy-caa5de25490b
- canonical_url
- https://medium.com/@fulton_shaun/git-submodules-vs-monorepos-whats-the-best-strategy-caa5de25490b
- author_url
- https://medium.com/@fulton_shaun
- status
- ok
- fetched_at
- 2026-07-06 19:56:14