Sep 08, 2025·8 min read

Monorepo vs polyrepo: keeping web, mobile, backend in sync

Monorepo vs polyrepo explained for teams shipping web, mobile, and backend apps. Compare dependencies, releases, and CI tactics to stay fast.

Monorepo vs polyrepo: keeping web, mobile, backend in sync

The real problem: shipping changes across three codebases

Teams don’t argue about monorepo vs polyrepo because they care about Git philosophy. They argue because a small product change turns into three separate changes across web, mobile, and backend, and something breaks along the way.

What breaks first is rarely the UI. It’s usually the invisible glue: an API contract changed without a matching update, a shared library got bumped in one place but not another, or the build pipeline suddenly needs a new step. When one piece ships earlier than the others, users feel it as bugs like “the button exists on web but the mobile app says unsupported” or “the app loads forever because the backend response changed.”

Web, mobile, and backend also run on different release clocks. Web can ship many times a day. Backend can ship often, but needs careful rollout. Mobile is slowest because app store review and user updates add real delay. A “simple” change like renaming a field can force you to plan around the slowest lane, even if only one screen needs it.

You’re likely paying a repo coordination tax if these keep happening:

  • Breaking API changes get discovered after merge.
  • Version alignment depends on manual reminders and spreadsheets.
  • One feature needs multiple coordinated pull requests that sit waiting on each other.
  • CI is slow because it builds and tests far more than the change touched.
  • Rollbacks feel risky because it’s unclear which commit matches which release.

Team size and product maturity change the right answer. Early on, most teams win by making coordination cheap and visibility high, even if things are a bit messy. As teams grow, boundaries start to matter, but only if interfaces are stable and ownership is clear.

If every meaningful change must land in three places, you will pay that tax somehow. Repo strategy is mostly about how you want to pay it.

Monorepo and polyrepo basics without jargon

A repo is just where your code lives, along with its history. When you have web, mobile, and backend, the choice is straightforward: keep everything together, or split it up.

A monorepo is one repository that contains multiple apps and often shared code too. Web, iOS/Android, backend services, and shared libraries sit side by side.

A polyrepo is the opposite: each app (and sometimes each service) has its own repository. Shared code usually becomes a separate package, or teams copy small pieces when needed.

Day to day, monorepos usually feel like this: sharing code is easy, cross-app changes can be one pull request, and rules are consistent. The tradeoff is social: ownership can get fuzzy unless you set clear boundaries, and repo-wide checks can feel strict.

Polyrepos usually feel like this: each team can move independently, repos stay focused, and access control can be simpler. The tradeoff is coordination: sharing code takes planning, and cross-app changes often become multiple pull requests with careful timing.

Many teams end up with a hybrid: apps in separate repos, shared contracts in one place; or one monorepo with strong boundaries so each team mostly stays in its own area.

If you use a platform that generates backend, web, and mobile from one source of truth, you reduce drift because contracts and logic live together. AppMaster, for example, generates production-ready backend, web, and native mobile apps from a single model. That doesn’t remove release realities (mobile still ships slower), but it can eliminate a lot of “did we update all three?” overhead.

Dependency management: keeping shared code safe

Shared code is where teams lose time, regardless of repo layout. A small change in a shared library or API contract can break web builds, mobile releases, and backend deploys in different ways.

When you share libraries (UI components, validation rules, auth helpers), you’re choosing between one version for everyone or multiple versions over time.

  • One version is simpler and avoids “it works on my branch” surprises.
  • Multiple versions let teams move at their own pace, but create clutter and make security fixes harder to roll out.

API clients and schemas deserve extra care. Manual updates are slow and error-prone. A better pattern is to treat the API schema as the source of truth and generate clients from it, then either check them in or generate them in CI. The goal is to fail fast: if the backend adds a required field, the mobile client should break in build, not three days later in QA.

Breaking changes spread when behavior changes without a safe path forward. Prefer additive changes first (new fields, new endpoints), then deprecate later. If you must break something, use versioned endpoints or a short compatibility window.

Concrete example: the backend renames status to state. If web updates today but mobile can’t ship for a week, the backend needs to accept both fields for that week, or ship an adapter that maps the old field to the new one.

A few rules that keep dependency updates boring (in a good way):

  • Update on a cadence. Small weekly updates beat big quarterly ones.
  • Require explicit approval for breaking changes, plus a short migration note.
  • Automate checks: dependency bumps, client regeneration, and basic contract tests.
  • Define “done” as “web, mobile, and backend builds are green,” not “my repo passes.”

Generated code can reduce drift, but it doesn’t replace discipline. You still need one contract, clear deprecations, and predictable updates.

Release coordination: aligning web, mobile, and backend

Release coordination is where repo strategy stops being theoretical. If the backend changes an API field name, the web app can usually update and ship the same day. Mobile apps are different: app store review and user update timing can turn a small mismatch into a week of support tickets.

The practical goal is simple: a user action should work no matter which part updates first. That means planning for mixed versions, not assuming a perfect synchronized release.

Versioning patterns teams actually use

Most teams settle into one of these approaches:

  1. One shared release train: web, mobile, and backend ship as one versioned unit.

  2. Per-service versions with compatibility rules: each app/service has its own version, and the backend supports a defined range of client versions.

A shared release train looks tidy, but it often breaks down under mobile delays. Per-service versions are messier on paper, but they match reality. If you go per-service, write down one rule and enforce it: which backend versions must support which mobile versions, and for how long.

Mobile delays also change how you handle hotfixes. Backend hotfixes can go out fast; mobile hotfixes might not reach users for days. Prefer server-side fixes that keep old mobile builds working. When you must change the client, use feature flags, and avoid removing old fields until you know most users have updated.

Example: you add “delivery instructions” to an order flow. The backend adds a new optional field, web shows it right away, and mobile shows it next sprint. If the backend accepts old requests and keeps the field optional, everything keeps working while mobile catches up.

Who owns the release calendar

Coordination fails when “everyone owns it,” so nobody does. The owner might be a tech lead, a release manager, or a product manager with strong engineering support. Their job is to prevent surprises by keeping release expectations visible and consistent.

They don’t need a complex process. They do need a few repeatable habits: a simple release calendar with cutoffs and freeze windows, a quick cross-team check before API changes ship, and a clear plan for what happens when mobile is delayed (hold backend vs keep compatibility).

If your workflow generates web, mobile, and backend together from one model, you still need a release owner. You’ll usually have fewer “did we update all three places?” moments, but you won’t escape mobile timing.

Keeping CI time under control

Ship changes with fewer handoffs
Create an API, UI, and business logic together to reduce coordination work on every feature.
Start Building

CI gets slow for the same reasons in both setups: you rebuild too much, you reinstall dependencies repeatedly, and you run every test on every change.

Common time killers include full builds on tiny changes, missing caches, test suites that run everything, and serial jobs that could run in parallel.

Start with improvements that help everywhere:

  • Cache dependency downloads and build outputs.
  • Run lint, unit tests, and builds in parallel where possible.
  • Separate fast checks (every commit) from slower checks (main branch, nightly, or pre-release).

Monorepo tactics that help

Monorepos get painful when every commit triggers a “build the world” pipeline. The fix is to build and test only what is affected by the change.

Use path filters and an affected-only approach: if you changed mobile UI code, don’t rebuild backend images. If you touched a shared library, build and test only the apps that depend on it. Many teams formalize this with a simple dependency graph so CI can make a decision instead of guessing.

Polyrepo tactics that prevent drift

Polyrepos can be fast because each repo is smaller, but they often waste time through duplication and inconsistent tooling.

Keep one shared set of CI templates (same steps, same caches, same conventions) so every repo isn’t reinventing the pipeline. Pin toolchains (runtime versions, build tools, linters) to avoid “works in one repo” surprises. If dependency downloads are a bottleneck, set up shared caches or internal mirrors so every repo doesn’t pull the world from scratch.

Concrete example: a feature adds a new “status” field. Backend changes, web shows it, mobile shows it. In a monorepo, CI should run backend tests plus only the web and mobile parts that depend on the API client. In a polyrepo setup, each repo should run its own fast checks, and a separate integration pipeline can validate that the three releases still agree.

If you export source code and run your own CI, the same rule applies: build only what changed, reuse caches aggressively, and reserve slow checks for when they add real value.

Step by step: choose a repo strategy that fits your team

Evaluate with a pilot app
Build one workflow end to end, then decide if it removes your repo coordination tax.
Start Small

The decision gets easier when you start from your day to day work instead of ideology.

1) Write down what must change together

Pick 5 to 10 recent features and note what had to move in lockstep. Mark whether each touched UI screens, API endpoints, data tables, authentication rules, or shared validation. If most features require coordinated changes across all three areas, a split setup will feel painful unless your release process is very disciplined.

2) Trace shared code and shared decisions

Shared code isn’t only libraries. It’s also contracts (API schemas), UI patterns, and business rules. Note where those live today, who edits them, and how changes are approved. If shared pieces are copied between repos, that’s a sign you need tighter control, either through a monorepo or strict versioning rules.

3) Define boundaries and owners

Decide what the units are (apps, services, libraries), then assign an owner to each unit. Boundaries matter more than the repo layout. Without owners, a monorepo becomes noisy. Without owners, a polyrepo becomes disconnected.

If you want a simple checklist, aim for: one repo or folder per deployable service/app, one place for shared contracts, one place for truly shared UI components, a clear rule for where business logic lives, and a documented owner for each.

4) Choose a release model you can follow

If mobile releases lag behind backend changes, you need a compatibility plan (versioned APIs, backward-compatible fields, or a defined support window). If everything must ship together, a release train can work, but it increases coordination.

Keep branching rules boring: short-lived branches, small merges, and a clear hotfix path.

5) Design CI around common changes

Don’t design CI for the worst case on day one. Design it for what people do every day.

If most commits touch only web UI, run web lint and unit tests by default, and run full end-to-end tests on a schedule or before releases. If most incidents come from API drift, invest first in contract tests and client generation.

Example: one feature that touches web, mobile, and backend

Picture a small team building three things at once: a customer portal (web), a field app (mobile), and an API (backend). A request comes in: add a new “Service status” field to jobs and show it everywhere.

The change sounds small, but it’s a coordination test. Backend adds the field and updates validation and responses. Web displays it and updates filters. Mobile needs to show it offline, sync it, and handle edge cases.

Now the real issue: the API change is breaking. The field name changes from status to service_status, and old clients crash if they don’t handle it.

What a monorepo changes

This is where a monorepo often feels calmer. Backend, web, and mobile updates can land in one pull request (or one coordinated set of commits). CI can run affected tests, and you can tag one release that includes all three updates.

The main risk is social, not technical: one repo makes it easy to merge a breaking change quickly, so your review rules need to be strong.

What a polyrepo changes

With separate repos, each app lives on its own schedule. The backend might ship first, and web and mobile scramble to catch up. If mobile releases require app store review, the “fix” can take days even if the code change is tiny.

Teams usually solve this with more structure: versioned endpoints, backward-compatible responses, longer deprecation windows, and clearer rollout steps. It works, but it’s ongoing work.

If you’re deciding based on evidence, look at your last few months:

  • If incidents often come from mismatched versions, favor tighter coordination.
  • If releases are frequent and time-sensitive (especially mobile), avoid breaking changes or centralize them.
  • If teams are independent and rarely touch the same feature, the polyrepo overhead may be worth it.

Common mistakes and traps to avoid

Test monorepo-like workflow
Prototype your next cross-platform feature and see how much faster review and testing feels.
Create Project

Most teams don’t fail because they picked the “wrong” repo structure. They fail because everyday habits slowly add friction until every change feels risky.

Shared code becomes a dumping ground

A shared library is tempting: helpers, types, UI bits, “temporary” workarounds. Soon it becomes the place where old code goes to hide, and nobody knows what’s safe to change.

Keep shared code small and strict. “Shared” should mean used by many teams, reviewed carefully, and changed with intent.

Tight coupling through hidden assumptions

Even in separate repos, systems can be tightly coupled. The coupling just moves into assumptions: date formats, enum values, permission rules, and “this field is always present.”

Example: mobile treats status = 2 as “Approved,” web treats it as “Confirmed,” backend changes enum order, and everything breaks in a way that looks random.

Prevent this by documenting contracts (what fields mean, what values are allowed) and treating them like product rules, not trivia.

Unclear ownership

When everyone can change anything, reviews get shallow and mistakes slip through. When no one owns an area, bugs sit for weeks.

Define owners for web, mobile, backend, and shared modules. Ownership doesn’t block contributions; it makes sure changes get the right eyes.

CI grows without pruning

CI often starts small, then every incident adds a new job “just to be safe.” Months later, it’s slow and expensive, and people avoid it.

A simple rule helps: every CI job needs a clear purpose and an owner, and it should be removed if it stops catching real issues.

Warning signs you need a cleanup include duplicate tests across jobs, jobs that stay red for days, “quick changes” that take longer to verify than to build, and pipelines that trigger mobile builds on backend-only changes.

Release coordination depends on tribal knowledge

If releases rely on one person remembering the right order and the secret gotchas, you will ship slower and break things more often.

Write down the release steps, make them repeatable, and automate the boring checks. Even if your tooling generates consistent backends and clients, you still need clear release rules.

Quick checks before you commit to monorepo or polyrepo

Centralize business logic
Use the Business Process Editor to keep business rules consistent across backend and clients.
Build Logic

Before you reorganize repos, reality-check how your team ships today. The goal isn’t a perfect structure. It’s fewer surprises when one change touches web, mobile, and backend.

Ask five questions:

  • Independent shipping: Can you release a backend fix without forcing a mobile app update the same day?
  • API change rules: Do you have a written contract for deprecations and how long old behavior stays supported?
  • Shared code discipline: Are shared libraries (UI components, API clients, business rules) reviewed and versioned consistently?
  • CI that runs what matters: Can CI tell what changed and run builds/tests only for affected parts?
  • One release view: Is there one place to see what’s going out across web, mobile, and backend, with owners and dates?

Simple example: a new “address” field gets added to checkout. If the backend ships first, the old mobile app should still work. That usually means the API accepts both old and new payloads for a while, and client updates are optional, not mandatory.

Next steps: reduce coordination work and ship with confidence

The goal isn’t the “right” repo structure. It’s fewer handoffs, fewer surprises, and fewer “wait, which version is live?” moments.

Write a short decision record: why you chose your current approach, what you expect to improve, and what tradeoffs you’re accepting. Revisit it every 6 to 12 months, or sooner if your team size or release cadence changes.

Before moving files around, pick the smallest change that removes real pain:

  • Add and follow versioning rules for shared packages.
  • Define API contracts and enforce them with contract tests in CI.
  • Agree on one release checklist across web, mobile, and backend.
  • Use preview environments for changes that touch multiple parts.
  • Set CI time budgets (for example: PR checks under 15 minutes).

If cross-codebase coupling is the real bottleneck, reducing the number of places that must change can matter more than repo layout. Some teams do that by moving more logic and data modeling into a single source of truth.

If you want to explore that approach, AppMaster (appmaster.io) is built to generate backend services, web apps, and native mobile apps with shared data models and business logic. A low-risk way to evaluate it is to build one small internal tool first, then decide based on how much coordination work it removes.

The confident path is boring on purpose: document the decision, reduce coupling, automate checks, and change repo structure only when the numbers say it will help.

FAQ

How do I decide between a monorepo and a polyrepo for web, mobile, and backend?

Start with how often a single feature forces changes in web, mobile, and backend. If most work is cross-cutting and coordination is your main pain, a monorepo or a strong “single contract” approach tends to reduce breakage. If teams rarely touch the same areas and need independent access and release control, polyrepo can work well with strict compatibility rules.

What usually causes the “everything broke after a small change” problem?

API drift, shared library version mismatches, and release timing differences (especially mobile app store delays) are the usual culprits. The fix is to plan for mixed versions in the real world, not perfect synchronized releases, and to make breaking changes rare and deliberate.

What’s the safest way to manage API contracts across web and mobile?

Treat the API schema as the source of truth and generate clients from it so mismatches fail in build, not in QA or production. Prefer additive changes first, then deprecate old fields later, and keep a short compatibility window when you must rename or remove something.

How should we handle shared libraries without creating a mess?

Aim for small, regular updates on a cadence (weekly beats quarterly) and require explicit approval for breaking changes. Keep a short migration note with each change and define “done” as “web, mobile, and backend builds are green,” not just one repo passing.

Should we ship everything on one shared version or let each part version independently?

The default is per-service versions with a clear compatibility rule: which backend versions support which client versions, and for how long. A single shared release train can work early on, but mobile delays often make it painful unless your product can tolerate waiting for app store timing.

How do we avoid breaking mobile users when the backend ships first?

Keep the backend compatible so old mobile builds still work while users update. Use additive fields, don’t remove old behavior too early, and lean on feature flags when you need to roll out client-visible changes gradually.

Who should own release coordination across web, mobile, and backend?

Make it someone’s explicit job—often a tech lead, release manager, or a product owner with engineering support. The goal is a simple, repeatable calendar and a clear decision rule for delays (hold a change vs keep compatibility), not a heavy process.

What’s the best first step to reduce CI time without changing repo structure?

Default to building and testing only what changed and cache everything you can. Split fast checks (per commit) from slow checks (main branch, nightly, or pre-release) so developers get feedback quickly without paying the full test cost every time.

How do monorepos keep CI from becoming “build the world” on every commit?

Use path filters and an “affected-only” approach so you don’t rebuild the world for a small change. If a shared module changes, run checks only for the apps that depend on it, and keep ownership and review rules clear so one repo doesn’t turn into everyone’s junk drawer.

How do polyrepos avoid drift and inconsistent pipelines across teams?

Standardize tooling and CI templates across repos so each one isn’t reinventing steps, caches, and conventions. Add an integration check that validates key contracts across releases, and pin toolchain versions to avoid “works in one repo but not the other” surprises.

Easy to start
Create something amazing

Experiment with AppMaster with free plan.
When you will be ready you can choose the proper subscription.

Get Started