惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
有赞技术团队
有赞技术团队
S
Schneier on Security
aimingoo的专栏
aimingoo的专栏
T
Troy Hunt's Blog
U
Unit 42
Hacker News - Newest:
Hacker News - Newest: "LLM"
V2EX - 技术
V2EX - 技术
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
H
Heimdal Security Blog
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Cloudbric
Cloudbric
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
The Cloudflare Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
MyScale Blog
MyScale Blog
F
Fortinet All Blogs
N
News | PayPal Newsroom
Attack and Defense Labs
Attack and Defense Labs
D
DataBreaches.Net
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
F
Full Disclosure
The Register - Security
The Register - Security
L
LINUX DO - 热门话题
Webroot Blog
Webroot Blog
Google Online Security Blog
Google Online Security Blog
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
I
Intezer
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
云风的 BLOG
云风的 BLOG
博客园 - 叶小钗
T
Threatpost
Spread Privacy
Spread Privacy
小众软件
小众软件
AWS News Blog
AWS News Blog
S
Secure Thoughts
S
Security @ Cisco Blogs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Designing Pairing Codes: Tradeoffs, Mistakes, and a Simple Approach That Works
Aleksandar S · 2026-05-03 · via DEV Community

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 BoothBeam, the flow is straightforward. A user opens the app on a TV and immediately sees a pairing code. Then 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.

Constraints you can’t ignore#

It’s tempting to think “just generate a random code” and move on, but a few real-world constraints quickly complicate things. Users are often typing these codes from a distance, sometimes in busy environments like conferences or events. That means readability matters. Mistakes will happen, so the system should minimize the chance of confusion.

At the same time, codes need to be short-lived. In my case, they expire after five minutes. Multiple TVs can generate codes at the same time, and 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.

It’s also important to be clear about what this system is and what it isn’t. 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 right character set#

The first design decision is what your codes should look like. Numbers-only might seem like the simplest option, but they come with downsides. Digits are easy to confuse when viewed from a distance, and they don’t give you as many combinations per character, which forces you to make codes longer.

Letters-only is slightly better, but still limited. You reduce the total number of combinations and increase the chance of accidentally generating real words, which can lead to awkward situations.

A combination of letters and numbers strikes the right balance. It gives you a larger pool of possibilities and allows you to keep codes short without sacrificing uniqueness. That’s the direction I took.

How long should a pairing code be?#

Once you decide on the character set, length becomes the next question. With 26 letters and 10 digits, you get 36 possible characters. A 4-character code gives you:

36^4 = 1,679,616 combinations

Enter fullscreen mode Exit fullscreen mode

At first glance, that seems more than enough. But practical issues start to show up as soon as you think about how people actually read and type these codes.

The 0 vs O problem#

One of those issues is the classic confusion between the number 0 and the letter O. Years ago, I learned to write a slashed zero to distinguish it from the letter, and it stuck with me. The problem is that most people don’t share that habit. When they see something like O0O0 on a screen, they hesitate or make mistakes.

The simplest way to solve this is to remove both characters entirely. By excluding 0 and O, you reduce the character set slightly, but you eliminate a very real source of user error. Now you’re working with 25 letters and 9 digits, for a total of 34 characters:

34^4 = 1,336,336 combinations

Enter fullscreen mode Exit fullscreen mode

You lose some combinations, but you gain clarity, which is far more valuable in this context.

The profanity rabbit hole#

Another issue shows up once you start generating random combinations of letters: sooner or later, you’ll produce something that looks like a real word. Sometimes that word won’t be something you want displayed on a TV at a conference booth.

My first instinct was to filter these cases. Build a list of profanity, check generated codes against it, and discard anything problematic. On paper, that sounds reasonable. In practice, it quickly turns into a mess. No list is ever complete, languages vary, slang evolves, and you end up maintaining something that never truly solves the problem.

After thinking it through and discussing it out loud, it became clear that this approach was solving the wrong problem. Instead of filtering out bad words, you can design the system so that words never appear in the first place.

A simpler approach#

The key insight is simple: if your code always contains at least one digit, it can’t form a real word. That removes the need for any filtering.

For a 4-character code, you can enforce this by placing a digit in the second or third position. This guarantees that every code includes at least one number, breaking any potential word pattern.

With this constraint, the total number of combinations becomes:

9 * 34^3 = 353,304

Enter fullscreen mode Exit fullscreen mode

That’s still more than enough for short-lived codes and a relatively small number of active devices. If you ever need more capacity, you can increase the length to five characters or even six. The important part is that the solution stays simple and predictable.

Handling collisions without overthinking it#

Even with hundreds of thousands of possible combinations, collisions can happen. The important thing is how you handle them. In this case, a simple retry loop is enough: generate a code, check if it already exists, and if it does, generate another one.

Because codes are short-lived and the number of active entries is small, the probability of repeated collisions is low. Adding more complex mechanisms doesn’t provide meaningful benefits and only increases maintenance cost.

Lifecycle of a pairing code#

Each code follows a simple lifecycle. It’s generated and stored in the database, then displayed on the TV. If the user enters it within five minutes, it’s marked as used and the devices are linked. If not, it expires and becomes invalid.

To keep the system clean, a periodic job runs once a day and removes both used and expired codes. This allows the same combinations to be reused over time without the database growing indefinitely.

Small UX details that matter#

The visual presentation of the code has a bigger impact than it might seem. Using uppercase letters improves readability, especially from a distance. A large font ensures the code is visible across the room, and slight spacing between characters helps users distinguish each symbol without breaking the flow of reading.

A code like AB3D is quick to scan and type. Adding spaces between characters (A B 3 D) slows users down and introduces uncertainty about whether spaces should be typed. Keeping it compact and consistent makes the experience feel faster and more intuitive.

Why this is secure enough#

Since this isn’t a login mechanism, the security requirements are different. The system relies on short expiration times, a limited number of active codes, and the fact that the first valid entry claims the connection. This is the same model used by widely adopted apps, and it works well in practice.

The chance of someone guessing a valid code within a five-minute window and using it before the intended user is extremely low, especially with a few hundred thousand possible combinations.

Final approach#

After exploring different options, the final setup in BoothBeam is straightforward:

  • 4-character codes
  • Uppercase letters and digits
  • Exclude 0 and O
  • Force at least one digit in the middle
  • Enforce uniqueness
  • Expire after 5 minutes
  • Clean up used and expired codes daily

It’s simple, easy to reason about, and avoids unnecessary complexity.

Conclusion#

It’s easy to overcomplicate problems like this, especially when the first idea involves filtering, validation, and edge cases. But if your solution requires maintaining profanity dictionaries across multiple languages, you’re already heading in the wrong direction.

A better approach is to step back and rethink the constraints. By slightly reshaping the problem—forcing a digit into the code—you remove the need for an entire class of solutions. The result is simpler, more reliable, and easier to maintain.

Sometimes the best engineering decision isn’t adding more logic, but removing the need for it entirely.


Appendix: Simple PHP Pairing Code generator#

class PairingCodeGenerator
{
    private const LETTERS = 'ABCDEFGHIJKLMNPQRSTUVWXYZ'; // no O
    private const DIGITS  = '123456789';                 // no 0
    private const POOL    = self::LETTERS . self::DIGITS;

    public static function generate(): string
    {
        $code = '';

        // 1st char
        $code .= self::POOL[random_int(0, strlen(self::POOL) - 1)];

        // force digit in 2nd or 3rd position
        if (random_int(0, 1) === 1) {
            // digit in 2nd
            $code .= self::DIGITS[random_int(0, strlen(self::DIGITS) - 1)];
            $code .= self::POOL[random_int(0, strlen(self::POOL) - 1)];
        } else {
            // digit in 3rd
            $code .= self::POOL[random_int(0, strlen(self::POOL) - 1)];
            $code .= self::DIGITS[random_int(0, strlen(self::DIGITS) - 1)];
        }

        // 4th char
        $code .= self::POOL[random_int(0, strlen(self::POOL) - 1)];

        return $code;
    }
}

Enter fullscreen mode Exit fullscreen mode

And here is the example how to use it:

...
do {
    $code = PairingCodeGenerator::generate();
} while (codeExistsInDatabase($code));

// now you have unique valid $code for furth§er use
...

Enter fullscreen mode Exit fullscreen mode

The implementation is simple on purpose. You can adapt it to any language, but the core idea stays the same.