The Case for Git Submodules: An Architect's Dilemma.
Revisiting Git Submodules: A Scalable Solution to Shared Code in Modern Architectures
Introduction: A Tangled Web of Shared Components
In complex frontend ecosystems, shared code is unavoidable.
Component libraries, design systems, or utility functions often need to be consumed by multiple apps. Avoiding code duplication is best practice, but the cost of sharing can quickly become painful.
Imagine this: You're managing four frontend apps and two design systems. A one-line bug fix to the core button component requires:
- Branching and pulling requests in two repos
- Publishing an update to an npm registry (sometimes private)
- Rolling out semantic version bumps across all consumers
- Triggering builds that wait in CI queues
What should have taken 10 minutes turns into a full working session.
This is not theoretical.
In one enterprise, we found that simple updates to the shared UI library introduced an average overhead of 3.2 dev-hours per release cycle. The ripple effects caused QA delays, stale dependencies, and in a few cases, production regressions.
The Technical Challenge: Why Traditional Model Struggles
Traditional dependency sharing, via package managers like npm or yarn, offers insulation and convenience but at the cost of visibility and friction.
The issues include:
- Version churning: Semantic versioning is a safety net, but also a delay mechanism.
- Build complexity: Each consuming app must rebuild parts of the dependency even if changes are minor.
- Release lag: A change made to a shared component today won’t show up downstream until the pipeline is complete tomorrow.
- Registry overhead: Hosting private registries like Verdaccio adds infra complexity.
For small projects, this friction is manageable. But at scale, with dozens of teams, velocity takes a direct hit.
Unlocking Scalability with Git Submodules
Git Submodules provide a simple and direct method to include one Git repository inside another.
Unlike npm packages, which import the final build artifact, submodules allow consumers to pull the actual source code, at the exact version they choose.
Here’s why this matters:
- Atomic changes across repos: A change in the shared repo can be linked to a specific consumer update.
- No version mismatches: Teams opt into updates by pointing at a specific commit.
- CI acceleration: Since source code is embedded, you avoid redundant fetch+build steps.
- Infra simplicity: No need for custom registries or internal package hosts.
In practice, teams at companies like Shopify and Bloomberg have used Git Submodules to manage shared code bases that demand exact reproducibility.
Architectural Blueprint: A Playbook for Using Git Submodules
To implement Git Submodules effectively, teams should follow clear conventions:
- Use submodules only for stable, shared libraries , not for volatile code.
- Require version bumping via commit hashes rather than pointing to main/trunk.
- Automate submodule updates via scripts or bots, but make them opt-in.
- Pin submodule commits in CI to ensure reproducibility.
- Document developer workflows , many devs find submodule UX confusing.
Example Setup
Imagine you have:
- A base component library:
shared-ui - A frontend app:
app-frontend
# Inside app-frontend repo
$ git submodule add https://github.com/my-org/shared-ui.git libs/shared-ui
# Later, update submodule
$ cd libs/shared-ui && git pull origin main
$ cd ../..
$ git add libs/shared-ui
$ git commit -m "Update shared-ui submodule to latest main"
You can also lock to tags or release branches to maintain stability.
High-level Architecture Diagram (described)
- Multiple frontend apps (A, B, C)
- Each contains a
libs/folder pointing to shared component libraries via submodules - Updates to shared libs are pull-based and manually reviewed
- CI workflows pin submodule commits for deterministic builds
This approach is especially powerful in mono-cloud setups where small teams own distinct repos but need strong code-sharing discipline.
Conclusion: Submodules, Not Silver Bullets
Git Submodules are not the shiny new thing.
They have rough edges. The UX is not beginner-friendly. Merge conflicts can get messy.
But in the right context , shared libraries with low churn and high consumption , they reduce complexity, increase traceability, and streamline developer operations.
Instead of pushing shared code into a monorepo or struggling with npm versioning games, submodules offer a middle path.
Reflect on this:
- What tools are you using to manage shared code today?
- Are the trade-offs worth the cost in time and flexibility?
- Could submodules offer a low-friction alternative in some parts of your system?