Designing Pairing Codes: Tradeoffs, Mistakes, and a Simple Approach That Works
Source: Dev.to
Introduction
While building Booth Beam, a digital‑signage tool, I ran into a problem that looks trivial at first glance: how do you connect a TV to a web app in a way that’s fast, reliable, and hard to mess up?
You could force users to log in on a TV, but anyone who has ever typed an email and password with a remote knows how painful that is. There’s a reason most modern apps avoid that entirely.
The approach used by apps like Netflix is simple and effective: pairing codes. Instead of logging in directly on the TV, you display a short code on the screen and let the user enter it on a device they actually enjoy using, like a phone or laptop. That small shift removes friction and makes the whole experience feel instant.
In Booth Beam, the flow is straightforward:
- A user opens the app on a TV and immediately sees a pairing code.
- They open the web app on their laptop or phone, enter that code, and the TV is connected.
- From that point on, they can send content to the screen without thinking about the pairing process again.
It’s a one‑time action that should feel effortless.
Real‑World Constraints
It’s tempting to think “just generate a random code” and move on, but a few real‑world constraints quickly complicate things.
- Readability – Users often type these codes from a distance, sometimes in busy environments like conferences or events.
- Error tolerance – Mistakes will happen, so the system should minimize the chance of confusion.
- Expiration – Codes need to be short‑lived (in my case, they expire after five minutes).
- Concurrency – Multiple TVs can generate codes at the same time; the first person who enters a valid code claims that connection.
Behind the scenes, codes are stored in a database, uniqueness is enforced, and used or expired entries are cleaned up periodically.
Note: This is not authentication. It’s temporary device linking. The security model is the same one used by Netflix and similar apps: short‑lived codes, limited active set, and a narrow window of opportunity.
Choosing the Character Set
The first design decision is what the codes should look.
| Option | Pros | Cons |
|---|---|---|
| Numbers‑only | Simple to generate | Digits are easy to confuse from a distance; fewer combinations per character → longer codes |
| Letters‑only | Slightly better readability | Still limited combinations; risk of generating real words (awkward situations) |
| Alphanumeric (letters + numbers) | Larger pool of possibilities; can keep codes short without sacrificing uniqueness | Must handle ambiguous characters (e.g., 0 vs. O) |
I chose the alphanumeric route.
Length and Combination Space
With 26 letters and 10 digits you have 36 possible characters.
A 4‑character code yields:
36^4 = 1,679,616 combinations
At first glance, that seems more than enough. But practical issues appear once you consider how people actually read and type these codes.
Removing Ambiguous Characters
The classic confusion between the number 0 and the letter O is a major source of error.
Solution: Exclude both 0 and O from the character set.
Now you have 25 letters + 9 digits = 34 characters:
34^4 = 1,336,336 combinations
You lose some combinations, but you gain clarity—far more valuable in this context.
Avoiding Unwanted Words
Random alphanumeric strings can occasionally form real words, some of which may be inappropriate for a conference booth display.
A naïve solution is to maintain a profanity filter, but:
- No list is ever complete.
- Languages and slang evolve.
- Maintaining the list becomes a never‑ending chore.
Instead of filtering after the fact, design the system so that words can never appear.
Enforcing a Digit Presence
If every code always contains at least one digit, it cannot be a pure word.
Implementation tip: force a digit in the 2nd or 3rd position of a 4‑character code.
With this constraint the total combinations become:
9 * 34^3 = 353,304
That’s still more than enough for short‑lived codes and a relatively small number of active devices. If you ever need more capacity, increase the length to five or six characters. The key is that the solution stays simple and predictable.
Handling Collisions
Even with hundreds of thousands of possible combinations, collisions can happen.
A simple retry loop is sufficient:
- Generate a code.
- Check if it already exists in the database.
- If it does, generate another one and repeat.
Because codes are short‑lived and the active set is small, the probability of repeated collisions is low. Adding more complex mechanisms would increase maintenance cost without meaningful benefit.
Code Lifecycle
Each code follows a straightforward lifecycle:
- Generation – Code is created and stored in the database with a timestamp and a “unused” flag.
- Display – The TV shows the code to the user.
- Entry – The user types the code on their phone/laptop within five minutes.
- Verification – The server checks the code’s existence, validity period, and “unused” status.
- Mark as Used – Upon successful verification, the code is marked as “used” and linked to the TV’s session.
- Expiration/Cleanup – A background job periodically removes codes older than five minutes or already marked as used.
Summary
- Alphanumeric (excluding
0andO) gives a clean, readable set of 34 characters. - A 4‑character code with at least one digit provides 353 k possible values—ample for a short‑lived, low‑concurrency scenario.
- Collision handling can be as simple as a retry loop.
- The lifecycle is generation → display → entry → verification → usage → cleanup.
This design keeps the pairing experience fast, reliable, and hard to mess up, while avoiding the pitfalls of ambiguous characters, accidental profanity, and unnecessary complexity.