In gRPC, how is a service's fully qualified name derived from its .proto file, and what breaks when the package line changes?
answer
- package plus dot plus service name
- the stanza name is an abbreviation
- case matters
- renaming the package renames the service
- the v1 segment is deliberate
basics
~20 sA service's fully qualified name is the proto package, a dot, then the name in the service stanza, and it is case-sensitive. Changing the package line renames the service, so callers built against the old name no longer reach it.
solid answer
~40 sThe gRPC specification defines it as `Service-Name = {proto package name} "." {service name}`. A file declaring `package muni.permits.v1;` and `service PermitAdjudication` yields `muni.permits.v1.PermitAdjudication`, and the match is **case-sensitive** — the spelling in the file is the spelling that must travel. That qualified name, not the bare stanza name, is the identity a server matches an incoming call against, which has two practical effects. Two services may share a stanza name as long as their proto packages differ. And editing the `package` line is *not* a cosmetic change: it renames the service, and every caller compiled against the old qualified name stops finding it, method names unchanged.
code
protobuf · 11 linessyntax = "proto3";
package muni.permits.v1;
service PermitAdjudication {
rpc GetApplication(GetApplicationRequest) returns (Application);
}
// Fully qualified service name:
// muni.permits.v1.PermitAdjudication
// Service-Name = {proto package name} "." {service name}go deeper
Remember that a service is identified by its proto package and its stanza name joined with a dot, and that the two together are what travels.
Compose the qualified name from a file you are shown, and explain why a package edit renames every method under the service at once.
Use the shape of a failure as evidence: one method failing points at a missing method, every method failing points at a renamed service or package.
Decide where version segments live and hold the line on it, since the package name is the unit an organisation forks when a contract has to break.
## Two names, and only one of them is the identity A `.proto` file gives a service a short name inside the `service` stanza — `PermitAdjudication` — and that is the name people say out loud. It is not the name the system uses. The gRPC specification composes the identity from two parts of the file: `Service-Name = {proto package name} "." {service name}` So a file that opens with `package muni.permits.v1;` and declares `service PermitAdjudication` defines a service whose full identity is `muni.permits.v1.PermitAdjudication`. The stanza name alone is an abbreviation; the qualified name is what a server matches an incoming call against, and what the caller's generated artifact carries. ## The parts, and how exactly they are joined - The **proto package** comes from the file's `package` declaration. It is a dotted name and every segment counts — `muni.permits.v1` is not interchangeable with `permits.v1`. - The **service name** is the identifier immediately after the `service` keyword. - They are joined with a single **dot**. Not a slash, not a colon. - Matching is **case-sensitive**: `PermitAdjudication` and `permitadjudication` are different services, and a mismatch is not corrected for you. - A file with no `package` declaration yields a service whose qualified name is just the stanza name, which is exactly why shared schemas always declare one. ## Why two services can share a stanza name Because the package qualifies the name, two teams may each declare `service PermitAdjudication` without colliding, provided their packages differ: | File declares | Qualified service name | Collides? | |---|---|---| | `package muni.permits.v1;` | `muni.permits.v1.PermitAdjudication` | no | | `package muni.inspections.v1;` | `muni.inspections.v1.PermitAdjudication` | no | | a second stanza in `muni.permits.v1` | `muni.permits.v1.PermitAdjudication` | yes — rejected at generation | The collision that matters is two identical qualified names, and that one is caught when the schema is compiled rather than at runtime. ## Editing the package line is a rename This is the part candidates get wrong, because the `package` line looks like a namespacing convenience for generated code. It is that as well, but it is first of all half the service's identity. Change it and: - **every** method under that service changes identity at once, even though not one `rpc` line was touched; - callers compiled against the old qualified name now name a service the server does not declare, and are answered `UNIMPLEMENTED (12)`; - the failure is total rather than partial, which is a useful diagnostic: one failing method suggests a missing method, *every* method failing suggests a renamed service or package. ## The version segment is deliberate The convention of ending a package with a version segment — `muni.permits.v1` — exists precisely because the package is the part an organisation intends to fork. A deliberate breaking revision proceeds like this: 1. Copy the contract into a new package, `muni.permits.v2`, and make the breaking edits there. 2. Serve both, so `muni.permits.v1.PermitAdjudication` and `muni.permits.v2.PermitAdjudication` are two distinct services answering side by side. 3. Migrate consumers one at a time, each on its own release schedule. 4. Retire the old package when its traffic is provably zero. The package is also what qualifies the message types declared in the same file, so versioning there moves the whole contract together rather than leaving methods and messages on different clocks. ## Mistakes to avoid - Quoting the bare stanza name as the service identity, and then being unable to explain why two teams' identically named services do not collide. - Assuming the name is matched case-insensitively because so much of HTTP is. - Treating the `package` line as generated-code cosmetics and editing it during a tidy-up. - Putting the version in the service name instead of the package, which works but strands the message types in an unversioned namespace.
- Two departments each declare a service named PermitAdjudication in separate files. Is that a collision?Not if their proto packages differ. The identity is the package-qualified name, so `muni.permits.v1.PermitAdjudication` and `muni.inspections.v1.PermitAdjudication` are two distinct services that can be served side by side. Declaring both inside the same package is the real collision, and the schema compiler rejects it before anything is deployed.
- Why do teams put a version segment in the proto package rather than in the service name?Because the package is the part they intend to fork. Publishing `muni.permits.v2` beside `muni.permits.v1` yields a second, separately named service that can answer alongside the first, so consumers migrate on their own schedule. The package also qualifies the message types in the file, so versioning there moves the whole contract at once.
saying these in an interview costs you the question
- Thinks the name in the service stanza alone identifies the service
- Assumes service name matching is case-insensitive
- Calls a proto package rename a cosmetic, source-only change
- Expects two services with the same stanza name to always collide
- Believes the package affects only generated-code namespaces