Why Bug IDs Should Start at 1 in Every Project

bug tracking

The first bug filed in a brand new project should be number one. It usually is not.

Most tools that start life with a single shared counter keep it, because at the beginning there is only one project and the difference does not show. Then the second customer arrives, opens their first project, files their first bug, and it is numbered #913. Nothing is broken. It just reads as though they walked into someone else's building.

We shipped per-project numbering in BugDump this week, and the reasoning behind it generalizes past our own product. A bug ID is one of the few pieces of your data model that people say out loud, and that changes what it has to be good at.

What a global counter actually costs

It leaks. A number drawn from a shared sequence tells anyone who reads it roughly how much total traffic the system has. Two bugs filed a month apart in your project reveal how many bugs everyone else filed in between. This is the same class of problem as sequential invoice numbers, and it is the reason invoicing tools stopped using them.

It reads as noise. #913 carries no meaning for the person looking at it. It is not the 913th bug in anything they can see. The number is large enough to be hard to hold in your head and arbitrary enough that you cannot sanity-check it.

It throws away a free signal. When numbering is per project, the number is also a rough count. BD-6 means this project has had a handful of bugs. BD-2400 means something else entirely, and you learn it without opening a dashboard.

None of this is fatal. Plenty of good tools ship a global counter for years. But it is a small tax paid on every single ID, forever, and the fix is cheap.

Why the prefix earns its place

Per-project numbering creates one new problem: numbers stop being unique. Task 42 exists in every project you own.

The path in the URL already resolves that, since it carries the project. But conversation does not have a URL. Someone says "can you look at 42" in a channel where three projects are being discussed, and the sentence is genuinely ambiguous.

A short key in front of the number fixes it at the only place ambiguity actually occurs, which is human conversation. BD-42 is unambiguous in a way that 42 is not, and it costs three characters. Jira has been right about this for twenty years, and Linear, Shortcut, and Sentry all landed in the same place independently.

The key should be derived, not demanded. Asking someone to invent a project key during signup is a small tax on a moment when they are trying to do something else. Derive it from the project name β€” Bug Dump becomes BD β€” and let people change it later in settings, which most never will.

The three design decisions that matter

Once you commit to per-project numbers with a prefix, three questions decide whether the result is pleasant or annoying.

1. Are numbers reused after deletion?

No. They must never be.

If deleting the newest task frees its number for the next one, then a link, a commit message, or a Slack thread referring to BD-9 can silently start pointing at a different bug. The failure is quiet and arbitrarily delayed, which is the worst combination.

Keep a counter that only ever goes up. Gaps in the sequence are not a defect; they are the visible evidence that old references still mean what they said.

2. Is the prefix part of the address, or decoration?

This is the interesting one, and both answers are defensible.

If the prefix is decoration, BD-42 and 42 are two spellings of one address and both open the task. Renaming a project key is then free: labels update everywhere, links keep working, nobody notices.

If the prefix is part of the address, only the exact current label resolves. Renaming a key breaks every link that carries the old one.

We went with the second, deliberately, after starting with the first. The argument that won: a URL carrying a prefix that is not this project's key is, in every realistic case, a mistake. Someone pasted a link from the wrong project, or typo'd, or is following a stale reference. Opening whatever task happens to hold that number in this project is not helpful. It looks like it worked, and quietly shows the wrong bug.

So a wrong prefix gets a 404, and so does a padded number like BD-007, and so does a number no task holds. The bare number remains a permanent address that no rename can break, and it is the form the product puts in every link, email, and webhook payload it generates.

The cost is real and worth stating plainly: rename a key and links shared under the old one stop resolving. That is the trade you make for never showing someone the wrong bug.

3. Do both forms work everywhere?

They should. People copy the label they can see, not the one your app happens to link with.

If your UI displays BD-42 on a card, then BD-42 pasted into the address bar has to work, or you have created a small trap for anyone who does the obvious thing. Accept both spellings, keep whichever one the visitor arrived on, and generate the canonical one yourself.

What stays a number

The prefix is for humans. Machines should keep getting integers.

Webhook payloads, API responses, and MCP tool arguments in BugDump all still carry the number, with the display label alongside as a separate field. Nothing that already parses a payload had to change, and nobody has to write a parser for a string format to find out which bug an event refers to.

That split is worth preserving deliberately as you build. The label is a presentation concern. The moment it leaks into your integration contracts, renaming a project key becomes a breaking API change, and a cosmetic setting turns into something nobody dares touch.

Migrating without breaking what people already sent

The awkward part of shipping this is that IDs already exist and are already in circulation, in emails, chat history, and other people's tickets.

Renumbering existing tasks so every project starts neatly at 1 is tempting and wrong. Every old reference silently repoints. The alternative is duller and correct: leave existing numbers alone, start each project's counter above its own highest number, and let old projects carry sparse IDs forever. New projects get a clean run from 1, old links keep meaning what they meant, and nobody has to be told anything.

Numbering is a small feature. It is also one that every person on the team touches dozens of times a day, which is a decent argument for spending an afternoon getting it right rather than living with #913 indefinitely.


BugDump adds a bug report button to your site with one script tag, and every report arrives with console logs, network requests, session replay, and environment data attached. Tasks are numbered per project and carry a key like BD-42.