QR Codes Explained: How They Store Data, Survive Damage, and Fail to Scan
A QR code is a carefully engineered data structure, not just a pixel pattern. This guide covers versions and capacity, the four error-correction levels, why masking makes every code look messy, and the quiet zone, contrast and size rules that decide whether it actually scans.
Almost everyone has generated a QR code, and almost no one understands the design decisions baked into one. Why does the pattern look like random noise? Why does a code that has been rained on, creased and half-covered by a coffee stain still scan? And why does the same URL, printed a little smaller, suddenly refuse to work? The answers are all in how the format stores data — and once you see the mechanics, the practical rules for printing reliable codes become obvious instead of folklore.
What a QR code actually is
A QR code is a square grid of dark and light modules (the spec's word for the little squares) carrying three kinds of content. First there are the function patterns: the three big finder squares in the corners, which let a camera locate the code, judge its orientation and correct for perspective; the timing pattern of alternating modules between them, which tells the decoder where every module sits even when the image is skewed; and, from version 2 upward, smaller alignment patterns that keep the grid readable when the photo is taken at an angle. Then there is the format information — a small, heavily protected block that records the error-correction level and the mask used. Everything else is your data, laid out in a zigzag path through the remaining modules.
The finder patterns are the reason you can scan a code at any angle, partially out of frame, or from a low-resolution camera feed: the scanner's job is really "find three nested squares in the right ratio, then homograph the rest of the grid back to a square". Lose the finder patterns and even a perfectly intact data region is unreadable.
Versions and capacity: from 21×21 to 177×177
QR codes come in 40 versions. Version 1 is a 21×21 grid; each version adds four modules per side, up to 177×177 at version 40. The version is not printed anywhere — the decoder counts modules. More grid means more capacity, but capacity also depends on how the data is encoded, because the format has dedicated modes that pack characters more tightly than raw bytes: numeric mode squeezes three digits into ten bits, alphanumeric mode (digits, capitals, a few symbols) uses 5.5 bits per character, and byte mode costs a full 8 bits per byte — which is what URLs and UTF-8 text use.
At the lowest error-correction level, the maximum byte-mode capacity runs from 17 bytes at version 1 to 2,953 bytes at version 40. A typical URL of 30–80 characters therefore lands somewhere in versions 3–6, and a generator always picks the smallest version that fits. Remember that when you add tracking parameters: every extra character can push the grid one version higher, and a higher version means smaller modules at any fixed print size.
Error correction: why damage is survivable
A QR code is assumed to be printed on things that get dirty, torn, faded and covered. So the format appends redundant codewords computed from the data using Reed–Solomon error correction. You do not need the finite-field math to use it correctly — only the idea: the encoder treats data as a sequence of symbols and derives extra symbols from it, so that as long as the total damage stays under a threshold, the decoder can reconstruct whatever is missing, no matter where the damage is. A hole in the middle and a torn edge are equally recoverable.
- L ("Low") — recovers roughly 7% of damaged codewords. The default for screen codes.
- M ("Medium") — about 15%. A sensible default for print.
- Q ("Quartile") — about 25%. For codes in harsh environments.
- H ("High") — about 30%. The level to choose if anything will sit on top of your code.
Two details make this robust in practice. First, the codewords are interleaved: consecutive modules in the picture do not come from the same error-correction block, so a single scratch or stain is spread thin across many blocks instead of destroying one. Second, the percentages count symbols, not picture area — a logo covering 10% of the visual area might destroy far fewer or slightly more than 10% of codewords depending on placement. That is why "put the logo in the middle, keep it small, use level H" works: the centre is dense with data but sparse in the function patterns you cannot afford to lose.
Masking: why your QR code looks "messy"
If you encode the same URL twice with different options, you get visibly different patterns. That is masking, and it is deliberate. Raw data can easily produce long runs of identical modules, large solid blocks, or accidental patterns that resemble finder squares — all of which confuse a scanner trying to threshold the image into dark and light. So the encoder XORs the data region with one of eight predefined mask patterns, scores each candidate for regularity and forbidden features, and picks the messiest-looking result. The chosen pattern number is recorded in the format information so the decoder can un-mask it. Function patterns are never masked; only the data area is.
The practical takeaway: a QR code that looks "clean" or aesthetically pleasing is a QR code that was forced into a bad mask or had its structure tampered with. This is also why decorative QR styles work only within limits — the modules still have to be individually resolvable.
Quiet zone, contrast, size: what actually breaks scans
Most "the QR code doesn't work" reports are not data problems at all — they are physics problems at the edges of the grid. Three rules cover nearly everything:
- Quiet zone. The spec requires a blank margin of at least four modules around the code. The scanner uses it to tell "edge of the code" apart from "dark stuff in the photo". A code printed edge-to-edge on a poster, or cropped flush into a banner, fails constantly even though the data is perfect.
- Contrast. Modules are judged by relative brightness, so the margin between light and dark matters more than the exact colors. Dark-on-light is the safe direction; light-on-dark is technically decodable but many scanner apps refuse it. Mid-tone colors on a similar-tone background quietly destroy the margin long before they look bad to your eye.
- Size. What a camera needs is a minimum size per module, not per code. As a rule of thumb for phone scanning, keep modules above roughly one to one-and-a-half millimetres in the printed output. A version 40 code shrunk to a business card has sub-millimetre modules and will fail on most cameras, while a version 3 code at the same size scans from across the room.
URLs: short beats long
People worry that a long URL will "not fit" in a QR code. It will fit — that is rarely the failure. The cost of a bloated URL is density: a 200-character link with UTM clutter pushes the grid to a higher version, which means smaller modules at any fixed print size, which means the code stops scanning at distance or on cheap cameras. Encode a short, stable URL you control — your own redirect — so the printed code never has to change even if the destination does. And treat the payload as public: a QR code is plain text in disguise, so never embed tokens, credentials or personal data directly in it.
Quick reference
- Versions 1–40; grid size = 21 + 4 × (version − 1). The decoder counts modules to find the version.
- Byte mode costs 8 bits per character; numeric and alphanumeric modes pack tighter.
- Error correction L/M/Q/H recovers roughly 7/15/25/30% of codewords — and shrinks capacity by the same choice.
- Masking is applied to the data region only, chosen automatically to avoid scanner-confusing patterns.
- Keep a 4-module quiet zone, high contrast, and module size above ~1 mm for phone scanning.
- Shorter payloads mean lower versions mean bigger modules mean more reliable scans.
Generate your code at the smallest version that fits, pick M or H for anything printed, and give it a proper margin — and the twenty-five-year-old engineering inside the grid will do the rest. You can build a QR code or barcode with the error-correction level, size and colors under your control right here, entirely in your browser.
QR & Barcode GeneratorQR codes and barcodes — open the tool