At some point, every developer ends up juggling more than one repository. Maybe it's a microservices architecture, a frontend-backend split, or shared libraries reused across projects. On paper, it sounds clean. In practice, it can turn into a coordination headache pretty quickly.
Let’s walk through how to work with multiple GitHub repositories in a way that stays organized, predictable, and scalable.
When multiple repositories actually make sense
Before diving into techniques, it’s worth understanding why you’d split things up at all. Multiple repositories aren’t always the right answer.
Common scenarios where multi-repo setups work well:
- Microservices architectures where each service evolves independently
- Shared libraries used across different projects
- Access control needs (different teams owning different repos)
- Different deployment cycles per component
A common mistake developers make is splitting too early. If your components are tightly coupled, multiple repos can slow you down instead of helping.
Cloning and managing multiple repos locally
At the simplest level, working with multiple GitHub repositories means managing several directories. But even this can get messy without structure.
A clean approach is to group related repositories under a single parent directory:
1projects/
2 ├── api-service/
3 ├── web-client/
4 ├── auth-service/
5 └── shared-utils/
6Instead of manually cloning each one, you can script it:
1repos=(
2 "git@github.com:org/api-service.git"
3 "git@github.com:org/web-client.git"
4 "git@github.com:org/auth-service.git"
5)
6
7for repo in "${repos[@]}"; do
8 git clone "$repo"
9done
10This small step saves time and ensures consistency across environments.
Coordinating changes across repositories
Here’s where things get interesting. Making changes in one repo is easy. Making coordinated changes across three or four is where complexity creeps in.
Let’s say you update a shared API contract. You might need to:
- Update the backend service
- Update the frontend client
- Update a shared SDK
Without a plan, this leads to broken builds and version mismatches.
Use versioning deliberately
If you maintain shared libraries in separate repositories, version them properly using semantic versioning:
1v1.2.0 → new feature
2v1.2.1 → bug fix
3v2.0.0 → breaking change
4Then consume them explicitly:
$ npm install @org/shared-utils@1.2.0
This avoids accidentally pulling unstable code.
Git submodules: useful, but tricky
Git submodules are often the first tool developers reach for when dealing with multiple repositories. They let you embed one repo inside another.
1git submodule add https://github.com/org/shared-utils.git libs/shared-utils
2Sounds perfect—but there’s a catch.
Submodules lock you to a specific commit, not a branch. That means updates don’t automatically flow in.
To update a submodule:
1git submodule update --remoteAnd yes, people forget to do this all the time.
When submodules work well:
- You need strict version control between repos
- You want reproducible builds
When they become painful:
- Frequent cross-repo changes
- Teams unfamiliar with submodule workflows
Monorepo vs multi-repo: a quick reality check
If managing multiple GitHub repositories feels heavy, you’re not imagining it. That’s why many teams eventually consider a monorepo.
| Approach | Strength | Tradeoff |
|---|---|---|
| Multi-repo | Clear boundaries, independent releases | Coordination overhead |
| Monorepo | Easier cross-project changes | Tooling complexity, larger repo size |
There’s no universal winner here. If your services are loosely coupled, multiple repositories are perfectly fine. If everything changes together, a monorepo might be simpler.
Automating multi-repo workflows with GitHub Actions
Manual coordination doesn’t scale. This is where GitHub Actions becomes essential.
You can trigger workflows across repositories using repository dispatch events:
1curl -X POST \
2 -H "Authorization: token $TOKEN" \
3 -H "Accept: application/vnd.github.v3+json" \
4 https://api.github.com/repos/org/web-client/dispatches \
5 -d '{"event_type":"api-updated"}'
6Then in the target repo:
1on:
2 repository_dispatch:
3 types: [api-updated]This lets you:
- Trigger builds when dependencies change
- Run integration tests across services
- Automate deployments in sequence
It’s a big step toward treating multiple repositories as a cohesive system instead of isolated pieces.
Keeping everything in sync without chaos
Working with multiple GitHub repositories isn’t just about tooling—it’s about discipline.
Some practices that make a noticeable difference:
- Consistent branching strategy across all repos
- Clear ownership for each repository
- Automated CI/CD pipelines to catch integration issues early
- Documentation that explains how repos interact
One overlooked detail: naming conventions. Keeping repository names predictable (e.g., auth-service, billing-service) helps both humans and automation tools.
A practical setup that scales
If you’re building a system with multiple repositories today, a balanced approach usually looks like this:
- Separate repositories for independent services
- Shared libraries versioned and published (not copied)
- GitHub Actions coordinating cross-repo workflows
- Optional use of submodules for tightly controlled dependencies
This setup avoids over-engineering while still giving you flexibility.
Multiple repositories aren’t the problem—lack of coordination is.
Once you treat your repositories as parts of a system instead of isolated units, things start to feel a lot more manageable.