Sernuman is a portable Elixir library for scoped serial-number generation. It exists for the moment the serial one-liner stops fitting: the second document type, the per-tenant counter, the January reset, the server in another time zone. The library separates what a number looks like from where the counter lives — a template grammar decides the shape, a swappable store decides when the counter resets.

Why this exists

Serial-number code starts as a one-liner: current date, a prefix, a random suffix. It works until the second document type arrives. A request-for-quote number and a purchase-order number look similar. Then one side wants a different prefix, the other wants the counter to reset every January, and a warehouse in another time zone renders yesterday as today.

At that point the format string lives in two places, the counter in two tables, and the exceptions keep growing. I met this pattern in ERP-style work: contracts, equipment tags, warehouse bins, and machine identifiers all follow the same path from one-liner to small subsystem, and every copy of the one-liner carried different bugs.

Sernuman fixes the format once and the reset rule once, and gives every document type the same code path.

The shape

The shape of a number comes from a template: date tokens, variables, and a sequence counter, joined by literals.

  • Date tokens are ISO 8601 pieces: {YYYY}, {MM}, {DD}.
  • Variables come from a context map: {id}.
  • Truncation and grouping are part of the grammar: {id:9:3} trims a value to 9 characters and inserts a dash every 3 characters.
  • The sequence counter is a width-padded integer: {seq:4:0} renders as 0001.

The template SN-{id:9:3}-{YYYY}{MM}{DD}-{seq:4:0}, given an id from the context map and the date 29 October 2026, renders as SN-01k-3f2-axq-20261029-0001.

The counter

The counter lives behind a store. A scope is a pair: entity type and scope id. A machine in tenant 1 and a machine in tenant 2 keep different counters, and a document in either tenant keeps its own.

The reset boundary is configured: per day, per month, or per year. The date renders in a configured IANA time zone, so “today” is the business’s today, not the server’s. Two stores ship with the library: an ETS store that needs no database, and an Ecto store behind an optional dependency.

Two rules hold for any store. Issued serials are immutable: a correction mints a new number and voids the old one, and a counter never moves backwards. A change lock allows one template change per calendar year per entity type, so the format of the numbers issued in a given year stays stable.

What it is not

Sernuman does not manage documents, workflows, or tenants, and it does not migrate existing numbers. It is one grammar and a few stores, and that is the point.

Version 1.0.0, proprietary.

blog-post
sernuman: template-driven sequences 2026
/images/software/stack/Elixir.png Elixir
/images/software/stack/Ecto.png Ecto
Proprietary In Production

Related

Have something worth building?

A paragraph about what you are working on is enough to start.

Say hello