What if the user doesn't have an SSN? What happens if they have one but lawfully refuse to provide it? What happens when you ask for and SSN from a US citizen who is also a European citizen? What happens when your database leaks?
In general, relying only on natural keys is a nightmare. Double nightmare if it's PII. Natural keys only work if you are flawlessly omniscient about the domain. And you aren't.
Some cases I've seen in previous roles where some natural key is required include reconciling third party data sources, or processing events from a topic or stream and being able to replay the event log, etc knowing that a different ID may break other third parties you don't control since they've already imported the ID. Being able to replay your data sets from scratch and get exactly the same data can have some real advantages for some apps.
Of course you need to be aware of the domain and assume that the key can change over time and have strategies to deal with that (e.g. entity version tables bound by time, data migration to add key attributes, etc etc) and the data structures/processes needs to be designed for this. There's more work in it for sure to get right - it shouldn't be the default. But in some cases I've seen it work really well which frankly surprised me at the time.
Identity crisis: how Social Security numbers became our insecure national ID https://www.theverge.com/2012/9/26/3384416/social-security-n...
Back in the 1990s I was trying to convince my colleagues not to use SSNs as unique IDs. I've since noted that quite a few organizations that had gravitated to SSNs as IDs had to go through expensive and chaotic migrations to real unique IDs.
SSN alone is not sufficient, of course. But it _is_ definitely part of the natural key that you use _implicitly_ ANYWAY, whether recognizing it or not.
> Natural keys only work if you are flawlessly omniscient about the domain
I would call that BS. Nobody is "flawlessly omniscient" about anything, not even in mathematics, yet we design and build systems that work.
On the other hand, yes, it is a very good requirement to have someone on the team during database modeling who understands the domain model thoroughly. No UUID columns will save you from that.
These are different concepts. "Looking for a person" means search. You can look for people lots of ways. In medicine for example, they often look for first name + last name + birthday. Is that a unique ID? No, but it's close enough for search usually.
On the other hand, for any kind of indexing or foreign keys, you want an actually unique, immutable ID, which means you don't want a natural key, you want an artificial ID created just for your database.
And such systems typically use synthetic keys to completely dodge the kind of problems I outlined.
The problems with natural keys are that you, the programmer, don't know as much as you think you know. You muddle the problem domain with the solution domain and when something comes along in the problem domain you didn't think of, it's now much harder to fix.
> On the other hand, yes, it is a very good requirement to have someone on the team during database modeling who understands the domain model thoroughly. No UUID columns will save you from that.
They save you from having to work out how to store a record when you chose SSN as primary key and discover that, uh, no you can't do that. The same goes for purchase order numbers, waybill numbers, student IDs, payroll IDs, bank account numbers, license plates ... anything whatsoever that is visible in the problem domain will or will have exceptions you didn't know about, didn't foresee and for which legislation or policy allows no exception for not using a UUID column.
Having a notion of multiple verified ways to contact the user is one step up over this. Not lot of websites actually do this. Linkedin is one of them; because they recognized early that they needed to track their users as they changed their professional affiliation.
I tend to use list fields for things like phone numbers and email addresses. This makes it clear that I understand that users might legitimately have several of those and that they change over time. I always liked what Keybase did with encouraging its users to have multiple verified third party identities (the more the better).
Using a social security number is a mistake for a different reason: it's a sensitive bit of information that can be abused if it falls in the wrong hands. You should protect it like you would protect a credit card number and generally not store it unless you absolutely have a valid business reason to. Also, it's country specific which sort of makes it an obstacle if you need to serve an international market (or have the ambition to do so later).
I have three discord accounts and there is no way to avoid that.
Unless of course, you're using a relational database like OP and incur a performance hit from using a UUID as your primary key. Additionally, they're not sortable like autoinc id's.
I've always wanted to try out Twitter's Snowflake ID [1] algorithm to get around this, but it requires requires using something like Zookeeper. I've seen some people on the net talk about UUIDv6 being sortable by time, but there's still the potential performance hit of index size.
While I'm bringing this up I've never actually tested how slow PK UUIDv1's are and at what magnitude their performance hit becomes noticeable.
[1] https://blog.twitter.com/engineering/en_us/a/2010/announcing...
ksuid https://segment.com/blog/a-brief-history-of-the-uuid/
Using DB autogenerated IDs risks that nightmare situation I've seen in more than one organization I've worked with: the IDs "leak" and become actual identifiers, in perpetuity, for the related entity. Now you can no longer renumber your table, and if you dump and restore you get all new IDs.
The case you describe sounds like using Oracle ROWID as a key, which I suppose people do as a hack to get around schema problems instead of fixing the schema. [1] That's an exceptionally bad idea.
[1] https://docs.oracle.com/cd/B19306_01/server.102/b14200/pseud...
To expand on this, there are three common approaches to create automatic keys in SQL applications.
1.) Generate it in the application itself. You can make UUIDs yourself with a simple call in most languages. Ensuring uniqueness is your problem--to generate integers, for example, you'll need some sort of coordination if there's more than one application thread.
2.) Generate it from a SEQUENCE. Sequences are database-side key generators that hand back a unique sequence number from a block of available sequence numbers when you call for the next number. Uniqueness is pretty much guaranteed since numbers come from the shared database server. This approach is popular in PostgreSQL and Oracle.
3.) Generate it from an auto-increment column, such as MySQL AUTO_INCREMENT columns. Auto-increment columns generate the key server-side at insert time. Uniqueness is guaranteed, but you can't see the key until it has been inserted. If you need to know the key for follow-on INSERT or UPDATE commands you have to select it back using LAST_INSERT_ID(). [1] Auto-increment keys are popular in MySQL.
ORMs like Hibernate JPA tend to provide all of these as choices. I don't use ORMs much myself hence can't comment much on the details.
[1] https://dev.mysql.com/doc/refman/8.0/en/information-function...
Also, SSN is a terrible key [1] and getting uniqueness without central coordination can be done with UUIDv4 or ksuid [2], as long as you have a reasonably trustworthy source of randomness
[1] https://news.ycombinator.com/item?id=26776092
[2] https://github.com/segmentio/ksuid/blob/master/README.md
As far as SSAN I'm not asserting it's a good key, just a natural key, which is to say a key inherent in the record.
Why would you want to sort by ID? Sort by something sensible, like the signup date instead. An autoincrement may stop corresponding to time if for instance at some point a database has an external dataset imported into it.
IMO, using an ID for anything other than an opaque identifier is asking for trouble.
“14632” in a log file could be anything, whereas a UUID is way more explicit and searchable.
"Oh, it's user 14632 - yeah I've seen that ID crop up against this issue a bunch of times in the past"
I can't do that with UUIDs.
Search for a UUID and the results are guaranteed to always be relevant. Can't do that with integers.
So yes, I agree with you, I always use a random UUID as ID.
> Then you may end up having to restructure the entire database, which will be a very annoying thing to do.
Well, you might call it annoying, I may call it a good upfront design requirement. Yes, you have sometimes spend time on stuff that doesn't immediately pay off, but it will pay in the longer run.
If you have a problem with your natural keys, this is mostly due to missing or misunderstanding your requirements, or an incomplete domain design. You can of course just sweep stuff under the rug, and pretend it's not there, but it's not a maintainable strategy.
(Unless you are a consultant of course, with a fixed term assignment on a project - in which case it is an excellent strategy for yourself...)
What happens when the user changes the email address? What if the social security number changes, because it was wrong or because it actually changes? What if the original unique identifier wasn't a good choice? What if we decide that the user can have multiple email addresses? Then you may end up having to restructure the entire database, which will be a very annoying thing to do. What if you implement additional rules for what an email is allowed to look like and now the constraint fails for existing users, and this correction needs to be propagated to millions of already existing rows?
Real-life personal data is weird and fuzzy. They can violate seemingly sensible rules like being unique, unchanging, or conforming to any rule whatsoever. Best not to let them spread all over the DB and cause trouble later.
Instead, you could just have a random ID that doesn't mean anything and therefore can stay fixed forever, and any user-related metadata stays in the user table, where it can be modified as needed. Plus an UUID is a fixed 16 bytes, which is easy and efficient to deal with.