Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save joeytwiddle/5c7bab3392a03d8814cba1d1d443daf9 to your computer and use it in GitHub Desktop.
Save joeytwiddle/5c7bab3392a03d8814cba1d1d443daf9 to your computer and use it in GitHub Desktop.

Be Overly Liberal with Thresholds, not Slightly Conservative

A mistake I often see in software development is the way people choose thresholds. The developer is told they need to limit certain types of activity, for logical reasons, but they set the limits too strictly.

I recommend:

When setting a threshold, be generous to the user, and then double that. If a high threshold will still satisfy your requirements, then choose that. There is no need to inconvenience your users. (You might lose them!)

We don't need to make thresholds tight, because the entire concept of a threshold is already tight. Even a high (loose) threshold is still a hard limit.

Example 1

Let's start with an example: You have been told that your software needs to place a limit on the size of usernames. This is a sensible requirement, because usernames are stored in the DB. If we don't set any limit, then an attacker could fill up our database by registering users with usernames which are 50MB long.

What size limit would you choose for usernames?

Here is a bad answer:

  • 20 character limit

What you've done here is choose the lowest number you think you can possibly get away with.

You've thought "Someone might want to have a username that is 20 characters long, so I have to allow that."

But what about users who want a 21 or 22 character username? Bad luck for them, your system will categorically deny that wish. And for what reason? What benefit is there in choosing a 20 character limit, instead of a 25 character limit?

Here is a better answer:

  • 200 character limit

If you are asking "So large? But why?" then I will answer: Because it's acceptable. 200 chars won't crash your DB, it won't lag your servers. It's below 256 (the largest value a byte can hold).

"But nobody will want a username that long!"

Probably not. But this will satisfy the user who wants a 25 char username, and the user who wants a 46 char username. And maybe, just maybe, one day a user who wants a 140 character username will appear, and that user will be satisfied too!

And at what cost to you? Very little. Perhaps the occasional use of .text-truncate, but then you probably needed that anyway. This just makes the need clearer, and easier to test.

Most importantly:

  • We have satisfied our original requirement (make it impossible to fill the DB with long usernames)

  • We have not disturbed any of our users in doing so. (If a user really wants a username longer than 200 characters, they are almost certainly up to no good.)

Example 2

Your team lead is worried about hackers trying to discover your users' passwords through a brute-force attack.

They want you to rate limit login requests, so that the attacker cannot make enough requests to discover a password.

Here is the wrong answer:

  • After 5 failed login attempts, enter paranoid mode for that user. Force them to fill out a captcha, do a password reset via email, or provide the 3rd and 11th characters of their first pet's maiden name.

Here is a better answer:

  • Only engage paranoia mode after 40 failed attempts are made in one day.

Why so liberal?

Because the first answer will almost certainly inconvenience some users. (Probably users who have forgotten their password because haven't used your site in a while, and might not want to use it again after you have inconvenienced them!)

The second answer is unlikely to seriously inconvenience any of your users, but it will still do a good job of blocking brute force attacks. If a hacker can only make 40 guesses a day, it will take them centuries to guess a user's password. (Unless they are using a dictionary, which is a separate concern). If your user has chosen a password made from 8 random letters, it will take on average 100,000,000,000 guesses to discover it.

Example 3

Your users add things to a basket in your app. Your manager says the basket should have a limit, so that malicious users cannot flood the DB by adding thousands of items to their baskets. Sounds reasonable.

Bad answer: Limit the basket to 20 items.

Good answer: Limit the baskett to 1000 items.

That will protect the database, and is unlikely to inconvenience any users.

Example 4

Your app (a visual editor) gets slow if the user selects too many elements at once.

If you set the maximum number of items to 50, then the app will always be super fast, but some users will be inconvenienced.

If you set the maximum number to 500, then users will rarely notice the limit. Some users with slow machines might notice the app is lagging when they select too many items. But that might be an important part of their workflow: they might need to select a lot of items, despite the cost of doing so. That is the user's choice.

What you have achieved by setting the 500 limit, is to ensure that a user on a slow machine cannot select 5,000 items, and in doing so completely freeze the app, forcing the user to close the tab and lose their work. That is the situation you really need to avoid.

Guideline

Consider the two extremes. Don't pick the wrong one! Favour user freedom over computer strictness, as long as it still meets the requirements.

More often than not, simply the fact that you have set any threshold, is good enough.

Ideally, the threshold you set should never be noticed by users. It should only be triggered by attackers or other problematic cases.

When choosing a threshold, don't think about how low you can go and still manage to satisfy 90% of your users. Instead, think about how high you can get away with, and still manage to 100% meet the requirements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment