Never use the word “User” in your code
You’re six months into a project when you realize a tiny, simple assumption you made at the start was completely wrong. And now you need to fix the problem while keeping the existing system running—with far more effort than it would’ve taken if you’d just gotten it right in the first place.
Today I’d like to tell you about one common mistake, a single word that will cause you endless trouble. I am speaking, of course, about “users”.
There are two basic problems with this word:
- “User” is almost never a good description of your requirements.
- “User” encourages a fundamental security design flaw.
The concept “user” is dangerously vague, and you will almost always be better off using more accurate terminology.
You don’t have users
To begin with, no software system actually has “users”. At first glance “user” is a fine description, but once you look a little closer you realize that your business logic actually has more complexity than that.
We’ll consider three examples, starting with an extreme case.
Airline reservation systems don’t have “users”
I once worked on the access control logic for an airline reservation system. Here’s a very partial list of the requirements:
- Travelers can view their booking through the website if they have the PNR locator.
- Purchasers can modify the booking through the website if they have the last 4 digits of the credit card number.
- Travel agents can see and modify bookings made through their agency.
- Airline check-in agents can see and modify bookings based on their role and airport, given identifying information from the traveler.
And so on and so forth. Some the basic concepts that map to humans are “Traveler”, “Agent” (the website might also be an agent), and “Purchaser”. The concept of “user” simply wasn’t useful, and we didn’t use the word at all—in many requests, for example, we had to include credentials for both the Traveler and the Agent.
Unix doesn’t have “users”
Let’s take a look at a very different case. Unix (these days known as POSIX) has users: users can log-in and run code. That seems fine, right? But let’s take a closer look.
If we actually go through all the things we call users, we have:
- Human beings who log in via a terminal or graphical UI.
- System services (like mail or web servers) who also run as “users”, e.g.
nginx
might run as thehttpd
user. - On servers, there are often administrative accounts shared by multiple humans who SSH in using this “user” (e.g.
ubuntu
is the default SSH account on AWS VMs running Ubuntu). root
, which isn’t quite the same as any of the above.
These are four fairly different concepts, but in POSIX they are all “users”. As we’ll see later on, smashing all these concept into one vague concept called “user” can lead to many security problems.
But operationally, we don’t even have a way to say “only Alice and Bob can login to the shared admin account” within the boundaries of the POSIX user model.
SaaS providers don’t have “users”
Jeremy Green recently tweeted about the user model in Software-as-a-Service, and that is what first prompted me to write this post. His basic point is that SaaS services virtually always have:
- A person at an organization who is paying for the service.
- One or more people from that organization who actually use the service, together.
If you combine these into a single “User” at the start, you will be in a world of pain latter. You can’t model teams, you can’t model payment for multiple people at once—and now you need to retrofit your system. Now, you could learn this lesson for the SaaS case, and move on with your life.
But this is just a single instance of a broader problem: the concept “User” is too vague. If you start out being suspicious of the word “User”, you are much more likely to end up realizing you actually have two concepts at least: the Team (unit of payment and ownership) and the team Members (who actually use the service).
“Users” as a security problem
The word “users” isn’t just a problem for business logic: it also has severe security consequences. The word “user” is so vague that it conflates two fundamentally different concepts:
- A human being.
- Their representation within the software.
To see why this is a problem, let’s say you visit a malicious website which hosts an image that exploits a buffer overflow in your browser. The remote site now controls your browser, and starts uploading all your files to their server. Why can it do that?
Because your browser is running as your operating system “user”, which is presumed to be identical to you, a human being, a very different kind of “user”. You, the user, don’t want to upload those files. The operating system account, also the user, can upload those files, and since your browser is running under your user all its actions are presumed to be what you intended.
This is known as the Confused Deputy Problem. It’s a problem that’s much more likely to be part of your design if you’re using the word “user” to describe two fundamentally different things as being the same.
The value of up-front design
The key to being a productive programmer is getting the same work done with less effort. Using vague terms like “user” to model your software will take huge amounts of time and effort to fix later on. It may seem productive to start coding immediately, but it’s actually just the opposite.
Next time you start a new software project, spend a few hours up-front nailing down your terminology and concepts: you still won’t get it exactly right, but you’ll do a lot better. Your future self will thank you for the all the wasteful workaround work you’ve prevented.