Batches, Serial Numbers, and Traceability

When something goes wrong — a contaminated ingredient, a faulty component, a counterfeit — you need to answer two questions within hours:

  • Backward trace: where did this come from?
  • Forward trace: where did it all go?

In regulated industries, being unable to answer is a criminal matter, not a service issue.

Batch vs serial

  Batch / Lot Serial number
Granularity A group of units made or received together One individual unit
Quantity Many Always 1
Used in Food, pharma, chemicals, textiles, electronics components Machines, vehicles, appliances, IT equipment, medical devices
Purpose Recall, expiry, quality characteristics Warranty, service history, ownership, theft
Typical volume Thousands of batches Millions of serials

Both can apply: a serialised medical device belongs to a manufacturing batch.

Batch management

The batch record

batch(
  material_id, batch_number,
  production_date, expiry_date, best_before_date,
  next_inspection_date,
  vendor_batch_number,          -- their number, for backward trace
  country_of_origin,
  status,                       -- UNRESTRICTED | RESTRICTED | BLOCKED | QUARANTINE
  characteristics JSONB,        -- assay %, moisture, colour, tensile strength, grade
  created_at,
  PRIMARY KEY (material_id, batch_number)
)

Batch characteristics

Batches of the same material differ measurably, and customers care:

Fabric batch B2601: shade code 4A-127, GSM 182, shrinkage 3.1%
Fabric batch B2602: shade code 4A-129, GSM 178, shrinkage 3.4%

Batch determination selects which batch to issue based on rules: FEFO, matching shade, minimum assay, remaining shelf life ≥ customer requirement. In apparel, shipping two different dye lots in one order is a defect — the garments won’t match. This is real, expensive, and exactly what batch characteristics exist to prevent.

Shelf life

Total shelf life:               365 days
Minimum remaining at goods receipt:  270 days  ← reject inbound stock that's too old
Minimum remaining at delivery:       120 days  ← per customer; retailers set this contractually

FEFO picking is mandatory for perishables. Expired stock must be automatically blocked, and near-expiry stock reported so it can be sold down or donated before it becomes a write-off.

Batch status

  • Unrestricted — usable
  • Restricted — usable only for specific purposes
  • Quarantine / QI — awaiting test results
  • Blocked — failed quality, under investigation, or recalled

A status change must propagate: blocking a batch has to block every unit of it, wherever it is, including stock already allocated to orders.

Batch derivation

In production, the output batch inherits characteristics from the input batches:

Input:  Resin batch R-100 (purity 99.2%), Additive batch A-55 (purity 98.0%)
Output: Compound batch C-780 (derived purity 99.0%)

Rules: minimum, maximum, weighted average, or copy. This is standard in process manufacturing.

Serial number management

Serial profiles

Configuration decides when a serial number is required:

Event Serial required?
Goods receipt Capture the vendor’s serial
Production confirmation Assign your own
Goods issue / delivery Record which specific unit shipped
Stock transfer Track movement
Customer installation Link to an installed-base record
Service / repair Attach service history

The equipment / installed base record

A serial number that lives at a customer site becomes an equipment record:

equipment(id, serial_number, material_id, batch,
          customer_id, installation_address, installed_at,
          warranty_start, warranty_end, service_contract_id,
          parent_equipment_id,      -- component installed in a machine
          status, current_location)

This is the foundation of aftermarket service: warranty validation, service history, spare-part compatibility, and recall targeting. See Manufacturing → quality.

The traceability chain

Vendor batch V-8821
      │ goods receipt
      ▼
Your batch R-100 ──┐
                   │ production order 4711
Your batch A-55  ──┤
                   ▼
             Output batch C-780
                   │ production order 4890
                   ▼
             Finished batch F-9001, serials SN00001–SN05000
                   │ deliveries
                   ├──▶ Customer A: SN00001–SN01200
                   ├──▶ Customer B: SN01201–SN03000
                   └──▶ Distributor C: SN03001–SN05000 (onward, unknown)

The chain breaks at the distributor. This is why regulated supply chains push “one step forward, one step back” obligations onto every participant — nobody sees the whole chain, but everyone can reconstruct it link by link.

The data that makes it work

Every consuming and producing document must record the batch/serial:

material_document_item(
  ..., batch, serial_numbers TEXT[],
  reference_document, reference_item
)
production_order_component(order_id, material_id, batch_consumed, quantity)
production_order_output(order_id, material_id, batch_produced, serials TEXT[])
delivery_item(delivery_id, material_id, batch, serials TEXT[], customer_id)

The trace query is a recursive graph walk over these links. Design it as one from the start; bolting it on later means backfilling data that was never captured.

WITH RECURSIVE trace AS (
  SELECT order_id, material_id, batch_produced AS batch, 0 AS depth
  FROM production_order_output WHERE batch_produced = :suspect_batch
  UNION ALL
  SELECT po.order_id, poc.material_id, poc.batch_consumed, t.depth + 1
  FROM trace t
  JOIN production_order_output po ON po.batch_produced = t.batch
  JOIN production_order_component poc ON poc.order_id = po.order_id
  WHERE t.depth < 20
)
SELECT * FROM trace;

Regulatory requirements

Regulation Scope Requirement
EU 178/2002 Food One step forward, one step back; trace within 4 hours
FSMA 204 (US) High-risk foods Key Data Elements at Critical Tracking Events
EU FMD Pharma Unique identifier per pack, verified at dispense
US DSCSA Pharma Full unit-level electronic traceability
EU MDR / US UDI Medical devices Unique Device Identification
REACH / GHS Chemicals Substance registration, safety data sheets
21 CFR Part 11 FDA-regulated Electronic records and signatures: audit trail, non-repudiation, validated system
GS1 EPCIS Cross-industry The standard event format for what/when/where/why

21 CFR Part 11 — what it means for a developer

If you build software touching FDA-regulated records:

  • Every record change is audit-trailed with user, timestamp, old value, new value, and reason
  • Electronic signatures are bound to records and cannot be transferred
  • The system must be validated (IQ/OQ/PQ) with documented evidence
  • Audit trails cannot be modified or deleted, by anyone, including admins
  • Access is role-controlled and reviewed

This is a heavy compliance burden that changes how you design, test, and deploy. Budget for it explicitly.

Recall

The test of the whole system:

1. Trigger — customer complaint, internal test failure, supplier notification
2. Scope — trace forward from the suspect batch to every customer and location
3. Quantify — how much shipped, how much on hand, how much consumed
4. Block — set batch status to Blocked everywhere; halt shipments
5. Notify — customers, distributors, regulators (within mandated hours)
6. Recover — returns, disposal, credit notes
7. Report — regulator submission, root cause, CAPA
8. Post-mortem — mock recalls at least annually to prove it works

Mock recalls are a required practice in food and pharma. The regulator asks you to trace a batch and gives you a deadline (often 2–4 hours). If your system can’t do it, you fail an audit. Building the trace report before you need it is not optional.

GS1 standards

Identifier What
GTIN Global Trade Item Number — the product
SSCC Serial Shipping Container Code — a logistics unit
GLN Global Location Number — a party or place
GRAI / GIAI Returnable asset / individual asset
GS1-128 / DataMatrix Barcode carriers with application identifiers
EPCIS Event data standard — the “what, when, where, why” of a movement

Application Identifiers encode structured data in a barcode:

(01)09501101530003(10)B2601(17)261231(21)SN00001
 ↑ GTIN            ↑ batch  ↑ expiry  ↑ serial

If you’re parsing barcodes in a warehouse app, learn AI parsing — it saves inventing a proprietary format that breaks on the first customer who uses GS1 properly.

Design rules

  1. Decide batch/serial requirements per material at design time. Retrofitting means the historical data simply doesn’t exist.
  2. Batch is part of the stock key — see Inventory Model.
  3. Every movement, consumption, and output records the batch/serial. No exceptions, including scrap and samples.
  4. Model the trace as a graph and build the recursive query early.
  5. Batch status changes propagate immediately to all stock and all open allocations.
  6. Never reuse a batch or serial number, even after the stock is gone.
  7. Serial arrays get big. For high-volume serialisation, a separate serial_movement table beats an array column.
  8. Run a mock recall drill on your own build before go-live. The gaps will surprise you.

Next