DEV Community

MarketingPro
MarketingPro

Posted on

1D vs 2D Barcodes: What Developers Need to Know Before Building an Inventory System

If you've ever been handed a "simple" inventory feature, you know the scanner part looks trivial. A scanner types characters into a field, you look up an SKU, done.

Then someone asks you to store a lot number, an expiry date, or a serial number on the same label. Or the labels are so small that your scanner can't read them. Suddenly the type of barcode is an architectural decision.

This post covers the practical differences between 1D and 2D barcodes, how they affect your data model, and how to parse structured payloads safely.

1D barcodes: a pointer, not a payload

Linear (1D) barcodes such as UPC/EAN, Code 128 and Code 39 encode a short string of characters in the widths of bars and spaces. They're typically used to hold a reference: a SKU, a GTIN, a location ID.

Strengths

  • Cheap to print, including on basic thermal printers
  • Readable by almost any scanner, including laser models
  • Very well supported by existing POS and warehouse systems

Limitations

  • Low capacity. Long payloads make the barcode physically wide.
  • Little built-in error correction. A scratch or smudge can make a label unreadable.
  • Scanners must cross the code along its width, so orientation matters for laser readers.

The design implication: treat a 1D barcode as a key. The real data lives in your database, and the scan triggers a lookup.

2D barcodes: a small data container

2D symbologies such as QR Code, Data Matrix and PDF417 encode data in both dimensions. They hold much more information, and they include error correction.

Strengths

  • Higher capacity: item ID, lot, expiry, serial and URLs in one symbol
  • Reed-Solomon error correction, so damaged labels can still decode (QR offers several correction levels, from roughly 7% to 30% recoverable damage)
  • Readable from any angle, and compact enough for small parts (Data Matrix is common on electronics and medical items)

Limitations

  • Requires an imager-based scanner or a camera, since laser scanners generally can't read them
  • Encoding and decoding add a bit more complexity to your pipeline

The design implication: a 2D barcode can carry metadata, not just a reference. That lets you capture lot and serial data at the moment of scanning instead of asking the operator to type it.

A quick decision guide

Situation Usually a good fit
Retail SKUs, simple stock lookup 1D (UPC/EAN, Code 128)
Tight cost constraints, existing laser scanners 1D
Tiny labels or parts 2D (Data Matrix)
Need lot, expiry or serial on the label 2D
Labels exposed to wear or damage 2D, for error correction
Mobile scanning with phone cameras 2D (QR) or 1D

Handling structured payloads

Many 2D labels follow GS1 conventions, where data is split into fields using Application Identifiers (AIs). For example, (01) is a GTIN, (10) is a lot number, and (17) is an expiry date.

Here's a small parser for the human-readable parenthesized format. It validates the GTIN check digit and handles the GS1 convention where a day of 00 means "last day of the month" (Python 3.10+):

import re
from calendar import monthrange
from dataclasses import dataclass
from datetime import date

AI_PATTERN = re.compile(r"\((\d{2,4})\)([^()]+)")

def gtin_is_valid(gtin: str) -> bool:
    """Validate a GTIN-8/12/13/14 using its check digit."""
    if not gtin.isdigit() or len(gtin) not in (8, 12, 13, 14):
        return False
    body, check = gtin[:-1], int(gtin[-1])
    total = sum(int(d) * (3 if i % 2 == 0 else 1)
                for i, d in enumerate(reversed(body)))
    return (10 - total % 10) % 10 == check

def parse_expiry(yymmdd: str) -> date:
    year, month, day = 2000 + int(yymmdd[:2]), int(yymmdd[2:4]), int(yymmdd[4:])
    if day == 0:  # GS1 convention: day 00 means "last day of the month"
        day = monthrange(year, month)[1]
    return date(year, month, day)

@dataclass
class ScanResult:
    gtin: str
    lot: str | None = None
    expiry: date | None = None

def parse_gs1(payload: str) -> ScanResult:
    fields = dict(AI_PATTERN.findall(payload))
    gtin = fields.get("01")
    if gtin is None or not gtin_is_valid(gtin):
        raise ValueError(f"Missing or invalid GTIN: {gtin!r}")
    expiry = parse_expiry(fields["17"]) if "17" in fields else None
    return ScanResult(gtin=gtin, lot=fields.get("10"), expiry=expiry)

print(parse_gs1("(01)09506000134352(17)271231(10)AB123"))
# ScanResult(gtin='09506000134352', lot='AB123', expiry=datetime.date(2027, 12, 31))
Enter fullscreen mode Exit fullscreen mode

Real scanners often emit a raw format with FNC1 separator characters instead of parentheses, so check what your hardware actually outputs. Don't assume the parenthesized form.

Practical lessons

  1. Validate before you trust. Check length, check digits (GTINs use one) and expected format. Scanners sometimes read garbage, and the parser above rejects it instead of passing it downstream.
  2. Make scans idempotent. Operators double-scan. Your inventory movements should handle that gracefully.
  3. Store the raw payload. If your parser changes later, you can reprocess historical scans.
  4. Plan for more than one technology. Barcodes are rarely the end state. Many operations later add RFID or BLE for bulk reads and real-time location, so keep your identification layer abstract (an identifier plus a source_type) rather than hard-coding "barcode" everywhere.
  5. Test with real labels. Print on your actual printers and scan with your actual hardware. Anti-glare film, curved surfaces and low print contrast cause most field failures.

Further reading

Barcodes are the base layer of most tracking setups, but they're one of several identification options. The Inventory Master has a short overview of how barcodes sit alongside other identification technologies, which is handy if you're weighing a move beyond barcodes.

Wrapping up

Choosing between 1D and 2D barcodes isn't just a hardware question. It decides how much data a scan can carry, how robust your reads are, and how much logic your software needs. Start by asking what has to be known at the moment of the scan, and pick the symbology that makes that cheap.

What's the strangest scan failure you've traced back to a label, printer or scanner setting? I'd like to hear about it in the comments.

Top comments (0)