Thoughts about primary key
Tags: database, architecture, technicaldebt
Published: 2017-11-19 02:40:22

---
title: Thoughts about primary key
published: true
description: Sequential ids and its alternatives
tags: database, architecture, technical debt
---

Many of us who manages databases needs to create identities for their table. As the business grows the table schema evolves with it, but the identities often tend to stays longer.

As someone who interacts with data/database on a daily basis, I spend a lot of time thinking about how to make them better.

Let's discuss one of the pitfalls by creating two tables for an growing hypothetical e-commerce startup: `products` and `customers`. It's often tempting to model the identities using the built-in mechanisms, for example:

```sql
-- MySQL --
CREATE TABLE products (
  id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -- 🌶
  name VARCHAR(50),
  price INT
);

CREATE TABLE customers (
  id INT AUTO_INCREMENT NOT NULL PRIMARY KEY, -- 🌶
  name VARCHAR(50)
);
```

I'm skipping over a few details, but I hope you will get the gist. Each products and customer has an numerical ID and one or more associated columns. There are two problems in the above code, which I’ve marked with a hot pepper 🌶.

Let's add more detail to our hypothetical startup. Let's say that we used to create new products and customers by an internal admin system, perhaps a simple web form.

![Original](https://gist.githubusercontent.com/hden/757ab9d5715857cbe1bdb4f46c4a9148/raw/32ec779ddd3e2ab53fe27f8d64bf4c248f25d543/architecture_01.svg)

It's very likely that the database ids are used for internal communications. After all, names just aren't precise enough. We might even exposed them in the API endpoints, for example `GET /products/5487` 🌶.

As the business thrives, our startup needs to hire more people and start buying advanced software to manage it's growing operation. For example:

* Sales department might want to use Salesforce as it's primary CRM.
* Financial department(s) might want to use some kind of inventory management system.

So our operation changed significantly, and we must integrate with external systems each with it's own database and IDs.

![Enterpriseimplementations Architecture](https://gist.githubusercontent.com/hden/757ab9d5715857cbe1bdb4f46c4a9148/raw/c352e6d5ab95d76b290f12562b3e5adfac3dfcea/architecture_02.svg)

It's quite challenging to keep different databases in sync, especially when there is no common identity key.

* Using multiple keys simultaneously is quite confusing for humans.
* Using foreign system IDs as primary key is not a option since systems might be replaced once in a while.

If we got the chance to really think about the requirement of a common identity key, perhaps in a cozy [hammock](https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/HammockDrivenDev.md), what makes an ID good?

Let's start with the `CREATE TABLE` statement. Regardless of specific database implementations `AUTO_INCREMENT NOT NULL PRIMARY KEY` usually indicates that the column is:

* Unique
* Not null
* Indexed
* Sequential
* Depends on internal states of the database 🌶

The last part is not very obvious, but it have caused scaling problems in [Twitter](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html). In our case, we might want to generate our own primary key using one of the alternatives algorithms.

Several alternatives have been proposed, including [GUID/UUID](https://blog.codinghorror.com/primary-keys-ids-versus-guids/) and their semi-sequential counterpart [Squuids](http://docs.datomic.com/identity.html#sec-6)/[CUID](https://github.com/ericelliott/cuid). Our initial table design can be modified by removing the `AUTO_INCREMENT` statement. Using on of the algorithms above, we can easily generate the primary key in the API server or one of the external system.

```sql
-- MySQL --
CREATE TABLE products (
  id INT NOT NULL PRIMARY KEY, -- or VARCHAR(36), depends on your implementation
  urn VARCHAR(50),
  name VARCHAR(50),
  price INT,
  UNIQUE (urn)
);
```

If exposing primary keys might raise some security concern. We can add a second unique resource name, perhaps in the [URN](https://en.wikipedia.org/wiki/Uniform_Resource_Name#URIs.2C_URNs.2C_and_URLs) format just like Amazon did for [AWS](http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html).

References:
* Mike Bostock's [What Makes Software Good?](https://medium.com/@mbostock/what-makes-software-good-943557f8a488) (for using 🌶 as a marker)
* Tom Harrison Jr's [UUID or GUID as Primary Keys? Be Careful!](https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439) (for proposing a secondary key for public API)