A system originally identifies `User` entities by email address (using the email as the natural key/identity). Later, the business adds an 'email change' feature. What breaks because of this identity choice, and how does a surrogate key like a UUID fix it?
answer
- identity must be permanent for entity's whole lifecycle
- natural key (email/username) risks 'identity drift' on edit
- surrogate key (UUID/sequence) decouples identity from mutable attributes
- email becomes a unique-indexed attribute, not the primary key
basics
~20 sIf a user's identity IS their email, then changing the email means the user 'becomes a different user' as far as the system is concerned — any records referencing the old email (orders, permissions, links) lose their connection. A separate, unchanging ID (like a random UUID) fixes this because the ID never changes even when the email does.
solid answer
~60 sUsing a mutable business attribute (email) as an Entity's identity conflates two different things: what uniquely identifies the entity, and what the entity's current state happens to be. When the email changes, every foreign key, cache key, external system reference, and log entry keyed on the old email 'points at a user who no longer exists' from the system's perspective, even though it's the same person with the same history — orders silently detach, session tokens keyed by old email fail lookups, and downstream systems that mirrored the old email as a join key go out of sync. The fix is to introduce a surrogate identity (e.g., a UUID generated once at account creation) that never changes for the entity's entire lifetime, and treat email purely as a mutable attribute of the User, like any other profile field. This is the identity-vs-value distinction applied at the whole-Entity level: identity must be something the business is willing to guarantee is permanent, and 'permanent' rules out almost every real-world business attribute, which is why surrogate keys are the default recommendation.
go deeper
Should recognize that if something used as an ID can be edited by the user, that's risky, even without knowing the term 'surrogate key.'
Should recommend using a generated id (UUID/sequence) as identity and treating natural-looking business fields as regular mutable attributes with a unique index instead.
Should trace through the concrete cascading failures (orphaned foreign keys, diverged external systems, stale session/cache keys) that a natural-key change causes and explain the surrogate-key fix's trade-offs (readability, migration cost).
Should plan a safe migration path off an existing natural-key design in a live system with zero downtime and coordinate the change across service boundaries and external integrations, not just within one database.
## The promise a natural key quietly makes An Entity's identity is supposed to be the one thing about it that never changes for its entire lifecycle — everything else is allowed to mutate precisely because the identity anchors 'this is still the same thing' through those mutations. The moment a team picks a business attribute as that identity — email, username, SSN, a license plate — they are implicitly promising that attribute will never change for the life of the entity, and almost no real-world business attribute can honestly make that promise. **Email is a particularly common and particularly bad choice** because 'change my email' is one of the most ordinary account-management features a system can have, yet a natural-key design makes that ordinary feature catastrophic at the identity layer. ## What actually breaks Mechanically, here's what breaks. - **Deleting the old identity.** If `User` rows are keyed by email in the database, updating a user's email is not an UPDATE of one field — it is, from the identity model's point of view, deleting the old identity and creating a new one, because 'the user with email X' has ceased to exist and 'the user with email Y' has begun to exist. - **Every referencing table.** Every other table with a foreign key referencing the old email either needs a coordinated cascading update across every table (expensive, error-prone, and impossible to do atomically across service boundaries in a distributed system) or is left pointing at a row that no longer matches anything, silently orphaning orders, audit log entries, permission grants, and support tickets. - **External systems.** External systems that received a copy of the email as a join key (an analytics warehouse, a billing provider) don't get the memo at all unless there's an explicit synchronization event, so they permanently diverge, misattributing the user's future activity to a phantom 'old' identity. - **In-flight requests.** Session tokens or password-reset links that embed or hash the email similarly break for any in-flight request that started before the change. ## The surrogate-key fix The fix — a surrogate key, typically a randomly generated UUID assigned once at entity creation and never altered — exists precisely because it separates two concerns that natural keys accidentally merge: | The concern | Its phrasing | Its nature | |---|---|---| | identity | 'which record is this' | permanent | | attributes | 'what does this record currently say' | mutable | Every foreign key, cache entry, and external reference points at the UUID, which is completely indifferent to the user changing their email, name, or phone number — those changes become ordinary UPDATE statements on non-key columns, with zero cascading effects. Email retains an important role — it's still how a human logs in — but that's now a lookup index (a unique index on `users.email`), not the identity itself; the lookup index can be dropped and rebuilt on a new value trivially, whereas identity cannot. ## What the surrogate key costs The trade-off of surrogate keys is mostly usability and readability, not correctness: - UUIDs are unwieldy to type, log, or debug by eye compared to a human-readable natural key, and if a system exposes raw UUIDs in URLs it can look unpolished (mitigated by adding a separate, still-mutable-if-needed 'slug' or 'handle' distinct from the true identity). - There is also a genuine design question about whether a natural key can ever be trusted — some values really are effectively permanent by strong external guarantee (an ISO country code, a well-governed product SKU under strict change control) and using them as keys is fine; the risk is specifically attributes the business itself allows end users or admins to edit through an ordinary feature. ## The migration teams end up paying for A well-known real-world instance of exactly this bug class: early versions of many web applications used username or email as the primary key for user accounts, and adding a 'change username' or 'change email' feature years into the product's life became a surprisingly large, risky migration project — every foreign key across dozens of tables, every cache key, every external integration had to be audited and migrated to a newly introduced surrogate id before the 'simple' rename feature could ship safely. Teams that have been through this once tend to treat 'always give every Entity a surrogate identity at creation, never derive identity from a mutable business attribute' as a hard rule from day one, exactly because retrofitting it later is so much more expensive than establishing it upfront.
- Are there cases where a natural key is actually safe to use as identity?Yes, when the value is governed by a strong external authority and is genuinely immutable in practice, like an ISO 3166 country code or a well-controlled product SKU under strict change management — the risk is specifically attributes an ordinary user or admin can edit through a normal feature, not all natural keys universally.
- If a team already shipped email-as-primary-key and needs to fix it, what's the migration strategy?Introduce a new surrogate id column, backfill it for all existing rows, add it as a foreign key everywhere the email was previously used as a join key, migrate all reads/writes to use the surrogate id, and only then relax email to a plain unique-indexed mutable attribute — typically done as a multi-step, backward-compatible rollout rather than a single big-bang change.
- Does this problem also apply to Value Objects, or is it purely an Entity concern?It's specific to Entities, because Value Objects have no identity to begin with — their 'identity' is their current data, so there's nothing to keep stable across a change; a Value Object simply becomes a different value, which is expected and fine, unlike an Entity silently becoming unreachable by its old identity.
A person's national ID number vs. their name: your name can change (marriage, legal change) but your national ID stays the same your whole life, so government records and benefits stay correctly linked to you regardless of name changes — because the system was smart enough to key everything on the permanent number, not the mutable name.
saying these in an interview costs you the question
- Uses a user-editable field (email, username, phone) as a primary/foreign key without flagging the risk
- Doesn't distinguish 'identity' from 'a unique attribute'
- Assumes cascading UPDATEs across all referencing tables is a trivial fix for a natural-key change
- Can't explain why external systems that mirrored the old natural key would silently diverge
- Treats UUID-as-surrogate-key as merely a style preference rather than a correctness safeguard