Home Resume
Homeβ€Ί Blogβ€Ί GCP Architecture Series #5 β€” Project IDs: The Name You Only Get to Choose Once…
GCP Architecture GCP Architecture Series

GCP Architecture Series #5 β€” Project IDs: The Name You Only Get to Choose Once

A team needs a naming convention for projects and treats it as a cosmetic decision, discovering later that one of the three identifiers is permanent, globally unique across every Google Cloud customer, and unusable again even after the project is deleted β€” while the two default service accounts in the same project address it by two different identifiers, so an audit that greps for one of them silently misses the other.

Verified against current vendor documentation on 18 August 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

A platform team is standing up its first dozen projects. Someone asks what to call them, the question feels like bikeshedding, and they agree a pattern in ten minutes: the product, a hyphen, the environment. Nobody writes it down, because it is obvious.

1
The ID they wanted was taken by a stranger

A project ID is globally unique across all of Google Cloud, not unique within the organization. The obvious IDs were claimed years ago by companies nobody in the room has heard of, so the convention acquires a company prefix on the spot — for the first twelve projects, and not for the ones created before the meeting.

Correct approach

Prefix from the very first project. The namespace is shared with every other customer, so a convention that assumes availability will be edited by whatever happens to be free that day.

2
The throwaway proof of concept kept its name forever

An ID cannot be in use or previously used, and that explicitly includes deleted projects. The team burned the clean name on a two-week experiment, deleted it, and cannot have it back — not in thirty days, not ever.

Correct approach

Give experiments deliberately ugly IDs with a disposable prefix. Spend the good names on the things that will still exist in three years.

3
Half the projects have IDs nobody chose

When you type a project name, the console generates a unique project ID for you, editable before creation and permanent afterwards. Anyone who clicked through that screen accepted a generated suffix, and the estate now contains both conventions.

Correct approach

Create projects from Terraform or gcloud with the ID stated explicitly. The generated ID is a convenience for a first experiment, not a naming policy.

4
The access audit missed half the grants

A script searched every IAM binding for the project ID. The Compute Engine default service account is project-number-compute@developer.gserviceaccount.com, so every grant made to it was invisible to a search for the ID — while the App Engine default, project-id@appspot.gserviceaccount.com, would have been missed by a search for the number.

Correct approach

Resolve both identifiers for a project and search for both. Any tooling that assumes one canonical form has a blind spot the size of whichever form it left out.

None of this is exotic and none of it is undocumented. It is a set of properties that only become visible after the decision they constrain has already been made.

One of the three can be corrected. The other two cannot.

The name is a display label and you can edit it at any time. The ID is permanent after creation and globally unique. The number is generated for you and read-only. Everything difficult about this topic follows from that asymmetry: the identifier people put the least thought into is the one they are stuck with.

Architecture

A project carries all three identifiers simultaneously. They are not aliases for one another, and different parts of the platform reach for different ones.

Diagram: the three identifiers a Google Cloud project carries with their mutability and rules, and the two default service accounts that address the same project by different identifiers
Three identifiers, one project. Only the name can be corrected, and the two default service accounts in the same project use different identifiers from each other.

The name is the only forgiving one

A project name must be 4 to 30 characters and may contain letters, numbers, single quotes, hyphens, spaces or exclamation points, ending with a letter or a digit. It is a display label, and you can edit it at any time during or after creation.

Because it is mutable it is also the wrong thing to build anything on. It is the field to put the readable, human version in — the one that can absorb a reorganisation or a product rename without breaking a reference.

The ID is a global claim

The ID must be 6 to 30 characters, lowercase letters, numbers and hyphens only, starting with a letter and not ending with one. It must not be in use or previously used, including by deleted projects. After creation it is permanent.

Two words in there carry most of the weight. Globally unique means the namespace is shared with every Google Cloud customer on earth, so availability is a race rather than a choice. Previously used means the namespace is append-only: deletion does not return anything to the pool.

The number is what the platform actually means

A project number is automatically generated and read-only. It is also the canonical resource name — projects/PROJECT_NUMBER — even though API calls accept either identifier.

That flexibility is why the distinction stays invisible for so long. Everything works with either form, right up until you are reading something the platform generated rather than something you wrote, and it is expressed in numbers you have never seen before.

The two default service accounts disagree with each other

This is the sharpest illustration that the identifiers are not interchangeable. In one project, with no configuration on your part:

  • App Engine's default service account is project-id@appspot.gserviceaccount.com.
  • Compute Engine's default service account is project-number-compute@developer.gserviceaccount.com.

Same project, two identifier forms, chosen by Google rather than by you. Worth noting alongside it: both the creation time and the email address format for default service accounts are documented as subject to change, so anything that parses these strings is built on a surface the vendor has reserved the right to move.

Why This Architecture Holds Up

Most naming standards can be fixed later. Tags get rewritten, resources get renamed, folders get restructured. The project ID is the exception, and it is worth being precise about why the usual escape route is closed.

Renaming is impossible because the ID is permanent. Recreating under a better ID means a new project, which means new resources, a new project number, new default service account identities, and a migration of everything inside it — and the old ID stays consumed regardless. So the cost of a bad convention is not the cost of renaming; it is the cost of migrating, paid per project, forever.

A convention that survives contact with the namespace

Lead with something globally distinctive — the company — because that is what makes the rest of the pattern reliably available. Keep the environment at the end where it reads and sorts predictably. Reserve a visibly disposable prefix for experiments, so a throwaway never consumes a name you will want. And decide all of that before the first project, because the first project is already spending from the same namespace as the hundredth.

Which identifier to use where

Use the ID in the places people read: documentation, dashboards, gcloud commands, the URLs somebody pastes into a ticket. It is the human handle and it is stable.

Use the number in durable machine references — anything storing a pointer to a project long-term — because it is the canonical form and it is what generated bindings and audit records will show you. Storing the ID there is not wrong so much as it guarantees a translation step later, at the point where somebody is trying to understand an audit log at speed.

And record the mapping between them somewhere queryable. Not because it is hard to look up, but because the moment you need it is usually the moment you are looking at an unfamiliar number in a log and want to know which project it belongs to.

The identifiers outlive the things that chose them

A project ID encodes whatever was true on the day it was created: a product name, a team name, an environment. Products get renamed, teams get merged, and the ID does not move. Three years in, an estate reliably contains projects whose IDs describe an organisation that no longer exists.

That is an argument for encoding the least volatile things available. The company prefix will outlive everything. The environment is stable. A team name is a liability, and a product name is somewhere in between — which is what the mutable project name is for, since it can be corrected the day the rename happens.

Key Architecture Decisions

DecisionChoose thisBecause
Project ID convention Company prefix first, environment last, agreed before project one The namespace is global and append-only. A convention that assumes the obvious name is free gets edited on first contact, leaving the estate with two patterns.
What to encode in the ID The least volatile facts available The ID cannot follow a rename. A team name in an ID becomes wrong at the next reorganisation and stays wrong permanently.
Experiments and proofs of concept A deliberately disposable prefix An ID cannot be reused, including after deletion. A good name spent on a two-week experiment is spent permanently.
How projects get created Terraform or gcloud with the ID stated explicitly The console generates an ID from the name, and whatever it generated is permanent the moment somebody clicks through.
What to put in the project name The readable, human version It is the only one of the three you can edit later, so it is where a rename or reorganisation should land.
Referencing a project from durable systems The project number It is the canonical resource name and the form generated bindings and audit records use. The ID there guarantees a translation step at the worst moment.
Auditing IAM bindings Search for both identifiers The App Engine default account embeds the ID and the Compute Engine default embeds the number. One search form silently misses the other.
Parsing default service account emails Do not Google documents both the creation time and the email format as subject to change. Resolve accounts through the API instead of reconstructing strings.
The one-way door, stated plainly

Of everything in this phase, the project ID is the decision with the least ceremony and the least reversibility. Organization policy can be rewritten, folders can be restructured, IAM can be re-granted, services can be enabled and disabled. An ID is chosen once, by whoever happened to be creating the project, and it is a permanent claim on a namespace shared with every other Google Cloud customer.

Closing Thought

The identifiers are a small topic that behaves like a large one, because the platform is relaxed about them right up until it is not. Either identifier works in an API call. Either one gets you to the right project in the console. The asymmetry only appears when something is generated rather than typed — a default service account, an audit entry, a binding you did not write — and by then the choice is years old.

The practical version is one sentence: the name is for people and can change, the number is for machines and cannot, and the ID is a permanent global claim that deserves more thought than the ten minutes it usually gets.

Next in this series

#6 follows the project through its whole lifecycle: creation, the soft delete that keeps consuming quota, restoring from DELETE_REQUESTED, and what "generally reversible" is careful not to promise about the resources that were inside it.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent