skip to content

Where should the .proto defining a gRPC service live, who owns it, and what does the generated-code step cost consumers?

level: principalimportance: should knowfreq 38%

answer

  1. one source of truth, many consumers
  2. the implementer owns the file
  3. copies drift silently
  4. every consumer pays a rebuild
  5. review the contract, not the code

basics

~20 s

The implementing team owns the file, but it should be published from one place every consumer builds against rather than copied per repository. The cost is a build-time dependency for every consumer: a caller built from generated code reaches a new method only after rebuilding.

solid answer

~40 s

Ownership goes to the team that implements the service — one owner, because a contract with four editors is four contracts. Location is the real decision: the file can live in the implementing team's repository and be published from there, in a dedicated schema repository shared by several services, or be copied into each consumer. The first two work; the third is how four departments end up compiling four different contracts. Whichever you pick, the cost is the same and is usually unbudgeted: every consumer carries a build-time dependency on the schema, so a contract change only reaches callers through their pipelines. That makes the schema's release cadence every consumer's release cadence, which is a governance question long before it is a technical one.

go deeper

for a junior

Know that the schema is a shared file with an owner, and that you build against a published copy rather than editing your own.

for a middle

Explain why copies of a contract drift and what a published, versioned schema gives a consuming team that a copy cannot.

for a senior

Set up the distribution: one owned location, versioned artifacts, and a check that flags a renamed or removed method before it merges.

for a principal

Own the trade between generating locally and publishing artifacts per language, and accept that the schema's release cadence becomes every consuming team's cadence.

## What is actually being decided Three separate questions get bundled into "where does the `.proto` live", and a lead has to answer each: - **Who may change it?** One team should own the file. A contract several teams edit is not a contract. - **Where do consumers get it?** From one published location with a version, or from copies. - **Who runs the generator?** Each consumer, or a publishing pipeline that emits generated artifacts consumers depend on like any other library. The technical answers are not hard. What makes this a judgment call is that the cost of a wrong answer lands in *other teams'* pipelines, months later, and is invisible from the owning team's repository. ## Three homes for the file | Arrangement | Works because | Costs you | |---|---|---| | In the implementing service's repository, published from there | ownership and implementation sit together; one obvious owner | consumers depend on another team's repository, and its release cadence | | In a dedicated schema repository across several services | contracts are reviewed as a group; consumers depend on one thing | a release step of its own, and an owner who is not the implementer | | Copied into each consumer | no coordination at all, briefly | copies drift, silently, and nobody can say which contract is current | The first two are defensible and the choice between them is mostly about how many services and languages you have. The third is not an architecture; it is the absence of one, and it produces the failure this whole area exists to prevent — several teams compiling against contracts that no longer agree, discovered one failing call at a time. ## The cost nobody budgets - **Every consumer carries a build-time dependency.** A caller built from generated code cannot reach a new method until its own build regenerates and redeploys, so the contract's cadence becomes everyone's cadence. - **Generated artifacts multiply.** One published artifact per target language, each versioned, each released; or every consumer runs the generator, and every consumer owns that toolchain. - **Skew becomes a version number — if you let it.** Publishing versioned artifacts is what turns invisible drift into a line in a dependency list. Copies hide the same drift completely. - **The review is not an ordinary code review.** An edit here can break four pipelines without touching a line of anybody's code. ## Governing a change A workable gate has four parts, and it is cheap to build before it is needed: 1. **A named owner** who approves every edit to the file, with no fallback to whoever is on call. 2. **An automated check** that flags removals and renames of a method, the service or the package, so a breaking edit cannot merge by accident. 3. **A published version** on the schema, and on the generated artifacts, so any consumer can say which revision it built against. 4. **A changelog consumers actually read**, because the migration work is theirs and they need lead time, not notification. ## The trade to make consciously Generating locally in each consumer keeps the pipeline uniform and avoids publishing one artifact per language, at the price of every consumer owning a generation step. Publishing generated artifacts removes that step from consumers and makes skew legible as a dependency version, at the price of a release pipeline per language and an owner for it. Few languages and few teams: generate locally. Many of either: publish. A copy of the schema per consumer is defensible in exactly one case — when the consumer sits outside your release boundary, another organisation or a client you cannot rebuild, and is pinned deliberately. That is a pin with an owner. Inside one organisation with a shared pipeline, it is drift with a story. ## Signals you chose wrong - Two teams quote different contents for the "same" contract. - Nobody can say which revision a given service was built against. - A contract edit merges without an owner reading it, and a consumer finds out from an alert. - Every contract change requires a scheduled evening in which several teams deploy together. - The schema lives wherever the first consumer happened to need it, and has since acquired three editors.

  • Should consumers run the generator themselves, or depend on a published generated artifact?
    Publishing versioned artifacts removes a toolchain from every consumer and makes skew visible as a dependency version, but costs a release pipeline per target language. Generating locally keeps one uniform pipeline and publishes nothing, but every consumer owns the generation step. Choose by how many languages and teams you have, not by taste.
  • What review gate does a service contract deserve that ordinary code does not?
    One that treats the file as published API: a named owner approving each edit, an automated check that flags a removed or renamed method, service or package, and a changelog consuming teams actually read. The aim is to make a breaking edit impossible to merge by accident, because the cost lands in other teams' pipelines.
  • When is a per-consumer copy of the schema defensible?
    When the consumer sits outside your release boundary — another organisation, or a client you cannot rebuild — and is pinned to a revision on purpose, with someone owning the pin. Inside one organisation on a shared pipeline it is not a pin, it is drift, and it is how four teams end up compiling four contracts.

saying these in an interview costs you the question

  • Lets each consuming team keep its own edited copy of the schema
  • Treats a contract edit as an ordinary code review
  • Ignores that consumers must rebuild before they can call a new method
  • Leaves the file wherever the first consumer happened to need it
  • Assumes a version number removes the need to coordinate removals