The Siren Song of the ‘User’ Model

Reading Time: 9 minutes

The other day, a podcast host asked me if I have any rants on my mind. One thing sprang up.

I didn’t address it in the interview because I don’t trust myself to speak extemporaneously about it yet. But, blog posts give me a chance to say my piece thoughtfully and without getting interrupted. So let’s do it here.

Ulysses and the Sirens, Herbert James Draper, 1910

I don’t like the concept of a ‘user’.

I think everyone in tech would improve at their jobs if we all stopped defaulting to the concept of a ‘user.’ I think the concept of a ‘user’ pigeonholes us into ideas that mislead our product direction and corrupt our technical choices. It gets away with these crimes because we’re so accustomed to throwing around the word ‘user’ that the cost does not occur to us.

First of all, ‘user’ is consequentially vague.

If you call them ‘users,’ you never have to know what on Earth people do with this thing. Saying ‘user’ allows teams to get away with never establishing a perspective about who they want to help or why. It encourages teams to build things that try to please everyone—in the process serving no one.

Here’s how that often manifests: 60% of the way through the development process, the designers discover that they pictured a different ‘user’ than the product team did. When this happens, the engineers usually have to undo/redo a ton of work. Engineers hate this, so it creates a churn risk. You think you haven’t seen it, eh? What you don’t realize is that every time an engineer got a new job because they “didn’t see a product vision” or “wanted to sink their teeth into a well-defined problem”, this likely happened. I wish I were kidding.

Are people on this app to listen to the radio? Then they’re listening, not “using.” Are they on here to talk to their friends? Then they’re socializing, not “using.” Are they on here to find affordable vegetables that they can purchase within walking distance because they don’t have access to a car or the means to replace a bike if it gets stolen? Once again, there are much better ways to describe these people than ‘user.’ Figure out what people are supposed to be doing here and why they care about doing that, and refer to them like you know this information.

Second of all, ‘user’ abdicates responsibility.

The word ‘user’ invokes the idea of manipulation, of addiction. It suggests that people need this product without referencing the product’s responsibility to do right by them.

This manifests in a few ways, but here’s a really common example: some subset of the engineers or product people decide “screw everybody who currently has our app.” Teams will come up with discounts that favor exclusively new accounts, deliberately leaving behind anyone who helped the company get to where it currently is. Or they completely reroute the product away from what those people wanted, while still charging them until they figure it out. When the product direction changes deliberately with an eye toward something useful, that’s one thing (and honestly, what it probably is, is a new product—not jerking around an old one). Flailing around in search of traction with one group and then, when that fails, setting sights on a different group, again smacks of a shaky vision.

Even if I don’t have a specific term for the people a product should serve yet, I say “constituents” instead of ‘users.’ This word choice reverses the role of dependence. A constituency describes the people a delegate or elected official represents. People in leadership positions have a responsibility to ensure fair treatment for their constituents and to do right by them. Thinking of people who use my software as my constituents helps me keep this responsibility in mind.

Third of all, ‘user’ screws up the data model.

I’m serious—it almost always does, in the end.

Like, even in the most basic case where everybody on the app does the same thing, there are usually multiple levels of access like free, paid, and admin. Somehow, ‘user’ usually represents only two out of three of those—but I have seen every combination of two here with one left out. Inevitably someone eventually inserts a subclass with some no-op methods because somebody would otherwise have access or behavior they shouldn’t have. We’ve talked about that pattern. It causes problems. It doesn’t help that numerous authors of programming books, including the Gang of Four, demonstrate the ‘polymorphism’ concept on a ‘user’ model. This encourages folks to copy them without understanding the consequences of incomplete polymorphism.

Suppose, for example, we have free and paid users. That still leaves admins out. That’s fine, right? Admins are an edge case. Whoops! You don’t have admins, so now your fancy social auth doesn’t work, and will never work, for people who changed their social media handles:

That’s level zero. What about when not everybody on the app does the same thing? Lyft has drivers and riders. Poshmark has buyers and sellers. Those two groups need different functionality. Oh, but wait. What if someone wants to drive sometimes, and take a Lyft at other times? What if they want to buy some items and sell others? More freaking problems with the user model. Now it needs modes, maybe, which could get cumbersome, especially if there are more than two. You know, like there are in an American healthcare app, which covers claimants, their dependents, providers who have to talk to the claimants, provider offices that do the billing, insurance companies that jam themselves in there, and patient advocates who try to un-jam the insurance companies. Who is the user?

The claimants and dependents case exemplifies another hurdle: multiple people on the same account. Netflix does this, too: you can have different family members with different watching preferences under one subscription. That’s an account with multiple profiles. In fact, a lot of companies want this, but they get stuck because they named the account model ‘user’ and now they get confused. It sounds so obvious when I put it like that, but I spent a decade watching this happen, and then several conversations about it with more experienced programmers, before I pieced it together enough to yell about it.

Again, that’s the obvious case. What about, say, when an individual person wants to maintain separate profiles for different use cases in a piece of software? Suppose one person watches the following kinds of videos:

  • Baking tutorials that they watch on the weekends
  • Rollerblading tutorials that they watch during the warmer months
  • Horror scenes from movies that they watch for their side gig as a movie critic, some of which are NSFW
  • Tutorials about how to use excel, which they use at their job
  • Replays of recent sports events they missed in a sport of interest
  • Lamaze tutorials that they’re using to prepare to give birth

Every one of these types of videos is useful to this person in a different context, so it doesn’t always make sense to recommend something from any of them. The first one is relevant two days per week, the second seasonally, the third only outside work, and the fourth only inside work. The fifth probably should never get recommended because the person is seeking out very specific recent events, and the sixth is only useful until the person gives birth—at which point additional lamaze recommendations become annoying, not helpful. Representing all these different kinds of videos under the concept of ‘user’ makes it extremely difficult to disambiguate them for content recommendation purposes. That’s not just a technical challenge. Because this is the data model, engineers don’t even think to do it.

This is what I mean about ‘user’ corrupting our technical choices. Data models can change, technically, but they only change when engineers see the mismatch between the data model and the reality it represents. We’re worse at this than we think we are, and we struggle to counteract that. The above data model doesn’t function as a ‘user.’ It functions as a collection of Preferences, represented by collections of Watches, tied together by one WatchHistory, of which an Account might have multiple. But when we jam all that into ‘user,’ we miss the underlying structure.

This makes the choice of a ‘user’ model risky.

Tossing a bunch of crap into a ‘user’ model frequently has consequences down the road, no matter how many authentication frameworks default to it. And that’s why I think everyone in tech would improve at their jobs if we all stopped defaulting to the concept of a ‘user.’ It’s too easy—it shortcuts too much thought that we should be thinking.

So please, make them ‘users’ if you must. But first, make sure you must. Because that’s probably not what you really have.

If you liked this piece, you might also like:

The Crafting Interpreters series

This post about structural verification (I’m just figuring you’re into objectcraft, so)

This post about why use, or not use, an interface (specifically in Python because of the way Python does, or rather doesn’t exactly do, interfaces)

2 comments

  1. I really like this article, and there’s no question that User tends to absorb additional functionality. Instead of renaming it, I like to make User, or Constituent, responsible only for authentication and log in. Any additional responsibilities are connected to another object. Their billing info is in a separate Customer object that references User. Their podcast, or music preferences would end up attached to Listener. Then, if they need to change accounts, that object can be moved between accounts.

  2. I like to use the notion of Roles that have access to various functionality/data & can be dynamically assigned to or even shared across people.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.