Jun 18, 2025·8 min read

Generated API clients without repo churn for web and mobile

Generated API clients can save time, but bad placement creates noisy diffs and slow releases. Compare review flow and timing for web and mobile teams.

Generated API clients without repo churn for web and mobile

What goes wrong when codegen lands in the wrong repo

A small API change can turn into a noisy pull request when generated API clients live in the wrong place. One new field in a response should be easy to review. Instead, the team gets a spec edit plus 2,000 changed lines of generated code, lockfile updates, and files nobody touched by hand.

That noise hurts review quality fast. A backend engineer knows the endpoint changed, but a web or mobile reviewer now has to scan pages of generated output to check if anything odd slipped in. Most people stop reading carefully. They approve the diff because they have no real way to inspect it.

The pain gets worse when the backend repo owns generation and release timing. A mobile team may need the new client today, but they cannot use it until the backend change merges, generation runs, and the updated client gets published or copied over. Mobile releases already move slower than web releases. Adding backend merge timing on top of app store timing is a good way to miss a sprint.

Web teams get a different problem. They pull fresh generated code even when they only changed UI text or a button state. Their branch now carries API client updates they did not ask for. Merge conflicts show up in files they do not understand, and simple frontend work starts to feel heavier than it should.

Ownership also gets blurry. If nobody clearly owns the generation step, specs drift from real use. One team edits the protobuf or REST spec, another forgets to regenerate, and a third hand-patches the client to unblock a release. After that, nobody trusts the generated output.

A common example is an orders API with one added optional field. The real change is tiny. The wrong repo setup turns it into review noise, delayed mobile work, extra frontend pulls, and quiet spec drift. After a few rounds, teams stop trusting codegen and start working around it by hand.

Where you can put generated clients

The generator matters less than the address where its output lives. Put generated API clients in the wrong place, and every small schema change turns into noisy diffs, awkward reviews, and version drift between teams.

One common option is the backend repo. This keeps the spec and generated code close together, so server changes and client updates happen in one commit. It works well when one team owns both sides and web developers pull updates often. The downside is obvious after a few weeks: backend pull requests fill up with machine-written files that most reviewers do not read, and mobile teams still need a clean way to consume the result.

Another option is to generate inside each web or mobile repo. App teams like this because they control when they update, test, and release. It also fits mobile well, since iOS and Android often pin a client version for a release branch. The tradeoff is duplicated setup. If three apps generate the same client in three repos, you now have three places where codegen can break.

Separate repo or packaged output

A separate package repo gives generated code its own home. That usually makes reviews cleaner. Backend developers change the spec, automation publishes a new client package, and app teams upgrade when ready. This setup is often easier to live with once more than one consumer exists. Still, it adds another release process, and someone has to keep package versions tidy.

The cleanest setup for repo churn is often to avoid committing generated source at all. CI can build the client from protobuf or REST definitions and ship an artifact instead of checked-in files. Web apps can install the package in a normal dependency update. Mobile teams can pin a version and move on their own schedule. You trade visible source diffs for stronger automation, release tags, and reproducible builds.

A small example makes the difference clear. Say the API adds one optional field on Tuesday. A web team may want that field the same day. A mobile team may not ship for two weeks. If generated code lives only in app repos, both teams must regenerate and commit changes separately. If CI publishes versioned artifacts, web can adopt the new client now while mobile stays on the older one until the next app release.

For one backend and one fast-moving web app, the backend repo is often enough. For several consumers, especially mobile, a package repo or CI-built artifacts usually creates less noise and fewer arguments in code review.

How protobuf and REST specs change the choice

The spec format changes more than the generated files. It changes how predictable your diffs are, how much config you need, and whether web and mobile teams can share the same client shape.

With protobuf, code generation is usually stricter. Types are explicit, field numbers lock in wire compatibility, and the output tends to change in smaller, cleaner diffs when the schema changes. If you pin the generator version, teams can review those updates without guessing whether a template changed or a naming rule shifted.

That makes protobuf a good fit for generated API clients that live outside the main app repo, often as a versioned package. The contract stays stable, and the generated code is more mechanical.

OpenAPI is different. The spec may describe the API well, but the generated result depends a lot on generator settings, templates, nullable rules, enum handling, and naming options. Two teams can point at the same spec and still get different clients if they do not lock those choices down. That pushes many teams to keep the config and generation step closer to the app, or to maintain a dedicated SDK repo with very strict tooling.

Mobile support also matters. gRPC works well on Android and iOS, but the browser often needs a different transport such as gRPC-Web, or a separate proxy layer. That can change the SDK format you publish. A mobile team may want generated protobuf stubs plus a thin wrapper, while the web team may need a custom client that hides transport quirks.

REST usually slides into browser and app stacks more easily. A REST client can sit on top of fetch in web, URLSession on iOS, and OkHttp on Android without pulling in much runtime machinery. That often means fewer surprises when teams update the client.

A simple way to choose:

  • Use protobuf when contract strictness matters most and you want smaller, more reviewable diffs.
  • Use OpenAPI generation when your API is REST first and your teams already live in standard HTTP stacks.
  • Publish separate SDK shapes when mobile can use gRPC directly but web cannot.
  • Keep generator versions and config pinned, especially for REST client generation.

A common split works well: protobuf for internal service contracts, REST clients for browser-facing apps. It is not the only setup, but it saves a lot of avoidable churn.

Choose a setup step by step

Start with a plain inventory. Write down every team and app that consumes the API, even the ones people forget during planning. That usually means the web app, iOS app, Android app, and any partner-facing tool or internal script.

Then sort those consumers into two groups: teams that need generated source in their own codebase, and teams that only need a published package. This one choice removes a lot of repo churn. A web app that builds many times a day may be fine pulling a package update, while a mobile app may want pinned versions so QA can test one client release at a time.

  1. Map every consumer and how it ships. A browser app can often accept change faster than an App Store release. A partner tool may need a slower, more stable update path.
  2. Decide the delivery format for each consumer. If a team needs to debug generated code or patch templates, keep source close to that team. If they just call the API, publish a package and keep generated files out of the app repo.
  3. Set an update rhythm before you write any automation. Some teams can take client updates on every merge. Others want one daily publish. Mobile teams often prefer updates only at release cut, because changing the client mid-cycle creates extra test work.
  4. Name one owner for the generator config, version rules, and breakage policy. Without one owner, small template edits turn into arguments across four repos.

A small example makes the trade-off clear. If the web team deploys ten times a day, they can usually track package versions closely. If iOS and Android release less often, they should pull a tagged client version on a schedule they can test.

If you want one simple default, keep the spec in the API repo, publish generated clients as versioned packages, and let each app choose when to upgrade. That keeps review noise low and makes release timing a team choice, not a surprise.

Set a review flow people will actually use

Match Release Timing
Map web and mobile release schedules so one API change does not block every team.

Most review pain starts when people read 2,000 changed lines of generated code before they see the one field that caused it. That order is backward. For generated API clients, reviewers should start with the contract change, not the output.

A good pull request makes the human decision easy to find. If someone adds a new enum value, changes a field name, or marks a field optional, that diff should sit at the top of the review. The generated files matter, but mostly as proof that the generator still matches the spec.

A simple flow works well for both web and mobile teams:

  • Review the protobuf or REST spec diff first.
  • Keep generator config changes in the same pull request.
  • Collapse or hide generated files when checking app logic.
  • Let CI regenerate clients and fail if the output does not match.

That second step saves a lot of confusion. If the output changes because someone updated a template, changed naming rules, or switched versions, reviewers need to see that cause in the same place. Separate pull requests turn a clear review into guesswork.

It also helps to split review roles a bit. One person checks whether the API change makes sense for product and app behavior. Another checks whether the generator setup still produces clean clients. Most teammates do not need to read every machine-made line unless the diff looks odd.

A small example shows why this works. Say a backend developer adds a new "locale" field. Web and mobile reviewers first decide whether the field name, type, and default behavior fit the app. After that, they can skim the generated code for surprises, not study every file line by line.

CI should be strict. Run generation in the pipeline, compare the result with the committed output, and fail fast on drift. That keeps review focused on decisions people made, while automation checks the boring part with perfect consistency.

Match generation to web and mobile release timing

Generated API clients should follow the release clock of each team, not one shared schedule. Web teams can usually pull a fresh client often, test it fast, and ship the same day. Mobile teams work on a longer cycle because app store review, release trains, and hotfix limits all add delay.

Treat web and mobile differently

If the API changes every few days, web can keep up with that pace. A frontend team can regenerate the client, fix a couple of calls, and deploy before lunch. That makes frequent client updates practical.

Mobile needs more stability. If iOS and Android are close to a release freeze, even a safe generated change can create churn in QA, snapshots, and last-minute approvals. That is why many teams batch low-risk API changes before the mobile freeze instead of feeding every small update into the app right away.

A simple rhythm works well:

  • Web pulls generated updates on a short cycle, often with each spec change or on a daily schedule.
  • Mobile updates on planned checkpoints, usually tied to the next app release.
  • Teams group small additive API changes before the mobile freeze.
  • The server keeps older mobile clients working until enough users move to the new app version.

Cut a client version when timing matters

Sometimes an API change needs both sides to move together. A renamed field, a new auth rule, or a changed error format can break old assumptions fast. In those cases, cut a client version and treat it like a real release artifact.

That gives web and mobile a clean target. Web can adopt it quickly, while mobile can pin to that version, finish QA, and release on its own timeline.

A small example makes this concrete. Say the server adds a new optional field for web checkout and changes a validation rule used by mobile. Web can take the fresh client right away. Mobile should wait for a tagged client version, test against the new server behavior, and ship only when the app store window is safe.

If release timing differs, your client workflow should differ too. One schedule for everyone sounds tidy, but it usually creates repo churn from codegen and rushed mobile updates.

One API change across web and mobile

Stop Random Diff Noise
Catch generator drift before it turns small API changes into noisy pull requests.

A small API change can turn messy fast. Say the checkout endpoint now needs one more field, such as a tax ID or delivery note, and the server will reject requests that do not send it.

The web team can usually move the same day. They update the form, pull the new client package version, test the new request, and ship before the day ends.

Mobile teams live on a different clock. iOS and Android may need UI work, QA, store review, and a release slot that opens next week. That gap is normal. The problem starts when generated code lives inside each app repo, because one API change suddenly touches three codebases before all three teams are ready.

A published package keeps that noise contained. The schema changes in the API or client package repo, code generation runs there, and the team publishes a new version. The web app can adopt it now. The mobile apps can stay untouched until their release window arrives.

That gives you a much cleaner sequence:

  • The API team adds the field and publishes a new client version.
  • The web team upgrades right away and updates the checkout form.
  • iOS and Android keep their current package version for now.
  • Mobile teams upgrade when their next release is ready.

This setup works best when the server gives older app versions a short grace period. If the new field is truly required for everyone right away, older mobile builds will fail no matter how clean your generated API clients workflow looks. In practice, teams often accept the old request for a limited time, fill a default on the server, or expose a new endpoint version until mobile catches up.

The package repo does not solve release timing by itself. It solves repo churn from codegen. That is a big win on its own, because each team updates when it has a reason to, not because generated files changed somewhere else.

Mistakes that create repo churn

Repo churn usually starts with a small habit, not a big design mistake. Teams treat generated API clients like normal source files, then a tiny API edit turns into hundreds of changed lines. Reviewers stop trusting the diff, and people merge code they did not really read.

One common problem is generator drift. If one developer runs one version of the protobuf or REST generator and another runs a newer one, the same spec can produce different imports, naming, file order, or comments. Nothing important changed in the API, but Git still fills up with noise. Lock the generator version in CI or run generation in one controlled environment.

Another mistake is mixing hand-written fixes into machine-made files. Someone notices a bad enum name or a missing helper, patches the generated file, and moves on. The next regeneration deletes that fix or creates a conflict that nobody enjoys sorting out. Custom logic belongs in thin wrappers, adapters, or extension files that generation does not touch.

Teams also create churn when they regenerate every client for an unrelated endpoint edit. A small change to one service should not force fresh output for web, iOS, and Android if only one client needs it. That pattern creates giant diffs, extra review time, and pointless releases. Mobile teams feel this more than web teams because app updates move slower and carry more coordination cost.

The last mistake is skipping version notes and forcing everyone to diff raw code. Generated files rarely explain intent well. A short note like "added one field to OrderSummary" or "renamed status_code to statusCode in the TypeScript client" saves real time in review. Oleg often pushes teams toward this kind of discipline in AI-first delivery work because clean change notes matter more when automation produces large diffs fast.

A good rule is simple: if reviewers cannot tell what changed in under a minute, the process is too noisy. Fix that before the repo gets bigger.

Quick checks before you lock the process

Make Codegen Easier
Set clear ownership for API specs, generator config, and client versioning.

A client generation setup feels fine until the first rushed API change lands on Friday afternoon. Then the weak spots show up fast: noisy diffs, broken builds, and one team blocked by another team's release calendar. A short preflight check saves a lot of cleanup later.

Start with the review test. If a reviewer cannot find the actual API change in about two minutes, the setup is too noisy. That usually means generated files bury the meaningful diff, or the spec change and generated output land in the same giant pull request with no structure. Keep the spec diff easy to read, and make the generated output predictable enough that reviewers can skim it instead of hunting through thousands of lines.

Then check release independence. Web and mobile rarely ship on the same schedule, and pretending they do causes friction. A web team may want the new client today, while mobile may need to wait for app store release timing, QA, or backward compatibility work. If one shared process forces both teams to move together, expect delays and revert pressure.

CI needs the same level of discipline. Run the generator twice on the same input and compare the output. If the result changes between runs, you will get random diffs and pointless review comments. Pin the generator version, pin the plugin version, and make the environment boring. Boring is good here.

One more check is ownership. New teammates should know four things without asking around:

  • who owns the API spec
  • who updates the generator config
  • who approves breaking client changes
  • who decides when a generated client is ready for release

If those answers live only in Slack or in one senior engineer's head, the process will drift.

A small example makes this concrete. Say the backend adds a new optional field to an order response. The web team can adopt the fresh client the same day. The mobile team may wait until the next release window. That is fine if the spec, code generation, and release steps are separate enough to let each team move at its own pace.

If your setup passes these checks, keep it. If it fails even one, fix that before you automate more. Extra automation on top of a messy process just creates faster messes.

Next steps for a cleaner client workflow

Do not redesign everything at once. Pick one service, one placement model, and one month to test it with a real web team and a real mobile team.

A small pilot tells you more than a long debate. Choose a service that changes often enough to expose pain, but not one that can break the business if the experiment gets messy.

Track a few plain numbers during that month:

  • how many generated files each API change touches
  • how long reviews take when codegen is part of the change
  • how many days pass between a spec change and a release in web and mobile
  • how often someone needs to fix generation by hand

Those numbers usually settle the argument fast. If one setup adds hundreds of changed lines, slows reviews, and makes mobile wait for a package update, you have your answer.

Before more teams depend on the client, write version rules on one short page. Decide when a change is patch, minor, or major. Decide who publishes a new client version, and decide whether mobile can skip versions safely when app store timing gets in the way.

Be specific about awkward cases. Generated code can change even when API behavior does not. If you do not name that case early, people argue about release bumps every week.

A simple pilot works well: web pulls the newest client on each merge, while mobile updates on a fixed schedule such as once per sprint. That setup shows you very quickly whether your generated API clients fit both release speeds or whether one team needs a different path.

If the pilot looks clean, keep it and expand one service at a time. If it creates noise, stop early and change the placement before the pattern spreads.

Some teams want another pair of eyes before they lock this in. Oleg Sotnikov can review API specs, CI, and release timing as a fractional CTO. That kind of outside review helps most when the team already ships, but small process problems keep turning into repo churn.