How to Generate Unique Codes That Never Repeat, Even at Scale
The first time most people need a batch of unique codes, whether it's for a giveaway, a set of gift cards or a voucher campaign, the instinct is simple: generate a random code, check it against the list of ones already generated, and if it's a duplicate, throw it away and try again. For a small batch this works perfectly well and you'll probably never notice a problem. The trouble starts when "small batch" quietly turns into "hundreds of thousands," and the naive approach starts behaving in ways that surprise people who haven't run into the birthday paradox before.
Generate-and-check works fine for a few thousand codes in one batch, but collisions scale roughly with the square of batch size, so it breaks down at scale. The reliable fixes are a larger code space (longer codes), a persistent sequential counter that can never repeat by definition, or a database uniqueness constraint with retry-on-collision for continuous generation.
Why do collisions happen sooner than intuition suggests?
The birthday paradox is the classic illustration: in a room of just 23 people, there's already better than even odds that two of them share a birthday, even though there are 365 possible birthdays to choose from. It feels wrong at first glance, because most people intuitively compare 23 against 365 rather than thinking about how many pairs of people are actually being checked against each other. With 23 people there are 253 different pairs, and each pair is a fresh chance at a match.
The same math applies directly to code generation. A four-character code drawn from 62 alphanumeric characters has roughly 14.7 million possible values, which sounds like more than enough for a batch of a few thousand codes. But collision risk in a batch scales roughly with the square of how many codes you're generating, not linearly, so a batch of just a few thousand codes drawn from that 14.7 million pool will start hitting duplicate collisions far more often than the raw pool size would suggest. This is exactly why the generate-and-check approach, which works fine for 50 codes, starts slowing down noticeably once you're generating tens of thousands from a code space that isn't large enough to comfortably absorb that volume.
Is checking for duplicates within a batch good enough?
For a single batch of a few hundred or a few thousand codes, generating candidates and discarding duplicates as you go is genuinely a reasonable approach, and it's exactly what the "Unique Codes Only" option on this site's generators does. It's simple, it's fast enough at that scale, and it needs no external database to check against. The important caveat is that it only guarantees uniqueness within that one batch. If you run the generator again next week for another campaign, nothing stops it producing a code that happened to appear in last week's batch, because the two runs know nothing about each other.
If you generate codes in several batches, check each new batch against the ones you have already issued. Pasting both lists into ListDiff's Compare Two Lists shows any codes that appear in both, and it runs entirely in your browser.
Does making the code space bigger actually solve the problem?
The more robust long-term fix isn't smarter checking, it's simply making the pool of possible codes so much larger than the number of codes you'll ever generate that collisions become astronomically unlikely without needing to check for them at all. This is exactly why UUIDs are 128 bits: not because any single application needs that many possible values, but because it's large enough that even generating billions of them, the odds of ever seeing a duplicate stay negligible. Adding just one extra character to a random code roughly multiplies the available pool by 36 to 90 times depending on the character set, which very quickly outpaces any realistic batch size. If duplicate checking is becoming a performance bottleneck, lengthening the code is usually a more effective fix than optimising the check itself.
What if I need a genuine guarantee, not just low odds?
If you genuinely need a guarantee, not just a very low probability, the reliable method is to build uniqueness into the structure of the code itself rather than relying on randomness at all. A sequential counter, stored persistently and incremented by one every time a code is issued, can never collide with itself, by definition. Plenty of production systems combine this with a random-looking prefix or suffix purely for appearance, so codes don't look predictable to a customer, while the actual uniqueness guarantee comes from the underlying counter, not the random-looking part.
This is the same principle behind the sequential code generator on this site: the {n} token in a pattern increments deterministically, so a batch like TICKET-000001 through TICKET-050000 is unique by construction, with zero chance of a collision anywhere in the range, and zero need to check.
How do large production systems handle continuous generation?
For systems generating codes continuously over time rather than in one big upfront batch, like an app issuing a new voucher code every time a customer completes an action, the standard production pattern is to generate a random code, attempt to insert it into a database column with a unique constraint, and if that insert fails because the code already existed, generate a new one and try again. This pushes the actual uniqueness guarantee onto the database itself rather than trusting an in-memory check, which matters once you have multiple servers generating codes at the same time and a simple in-memory list can no longer see what any of the others just generated.
So which approach fits your situation?
For a one-off giveaway of a few hundred or a few thousand codes generated in a single sitting, the built-in dedupe option here is genuinely enough, and it's the simplest thing that works. For anything ongoing, high-volume, or running across multiple servers, lean on a bigger code space plus a database constraint rather than trying to track every code you've ever issued in memory. And if traceability matters more than secrecy, a sequential pattern sidesteps the whole collision question by making duplicates structurally impossible in the first place.
You can try both the random, dedupe-checked approach and the sequential, collision-free approach on the main generator and the sequential generator respectively, both entirely client-side, so you can experiment with pattern length and character sets before deciding which fits your batch size.
Frequently Asked Questions
Why do duplicate codes show up sooner than I would expect?
It's the birthday paradox at work. Collision odds in a batch scale roughly with the square of how many codes you generate, not linearly, so a code space that sounds huge on paper still produces duplicates far sooner than intuition suggests once you're generating thousands of codes.
Is checking for duplicates as you generate enough?
It's fine for a single batch of a few hundred or a few thousand codes generated in one sitting. It only guarantees uniqueness within that one run though, not against codes generated in a separate batch weeks later.
Does making the code longer actually help?
Yes, and often more effectively than optimising a duplicate check. Adding one extra character to a random code multiplies the pool of possible values by roughly 36 to 90 times depending on the character set, which usually outpaces any realistic batch size.
How do UUIDs guarantee uniqueness?
Technically they don't guarantee it, they make it astronomically unlikely. A version 4 UUID is 128 bits, a code space so large that generating billions of them still keeps collision odds negligible, without any duplicate checking at all.
What if I need an absolute uniqueness guarantee, not just low odds?
Use a persistent sequential counter as part of the code. An incrementing number can never collide with itself by definition, which is a stronger guarantee than any amount of random-number checking can offer.
How do production systems handle this when codes are generated continuously?
The standard approach is to generate a random code, attempt to insert it into a database column with a unique constraint, and regenerate on failure. This pushes the actual guarantee onto the database, which matters once multiple servers are generating codes at the same time.