What counts as one job

DHSeaDev — Chrome Extensions, Windows Tools, & Idle Games

ServeBoard has been the odd one out on this site for a long time. Every other project has a page you can read; ServeBoard had a password box. That made sense when the tool was inseparable from the operational data it was built against, and it stopped making sense the moment the rebuild pulled those two things apart. So the gated page is gone, and a real one has taken its place.

This is the part of the story worth telling: the rebuild was not really about the code. It was about an identifier.

Two different things, both called an id

The tool reads two sources that describe the same work. One is a spreadsheet export. The other is an API. Both have a field that looks like a job identifier, and for a long time I treated them as the same thing, because they are named as though they are.

They are not. The export’s identifier is the number a human reads — the one printed on paperwork, the one someone says out loud on the phone. The API’s identifier is a database row id: an internal handle, stable, meaningless to anyone outside the system, and never intended to appear in front of a user. The API also exposes the human-facing number, separately, under a different name.

Conflating them is not a cosmetic mistake. It produces a program that appears to work, joins records that should never join, and shows an operator a number that means nothing to them. The rule that came out of it is now a hard one: an internal row id is for URLs and internal lookups, never for display, and never as a join key against anything that came out of a spreadsheet.

The dedupe key was wrong from the beginning

Once the two identifiers were separated, a bigger problem surfaced. Deduplication had been keyed on the per-row job identifier — one row, one job, count it once. That is a perfectly reasonable model, and it is the wrong model for this data.

A single piece of work can involve several parties. When it does, it legitimately produces several rows, each with its own row-level identifier. Keyed that way, one job counts as three. Every ratio computed downstream inherits that inflation, and nothing about the output looks broken — the numbers are simply wrong in a direction nobody questions.

The fix was to move the dedupe key up a level: from the row identifier to a client-supplied matter key — the pair of the requesting client and their own reference number for the matter. That key is deliberately not unique per row. It repeats across every row belonging to the same piece of work, which is exactly the property you want when the question is “how many jobs was this, really.”

  • Row-level id answers which record is this.
  • Matter-level client key answers which piece of work is this.
  • Analytics almost always wants the second one, and almost always gets handed the first.

The bug that plausible numbers were hiding

While that was being untangled, a derivation bug turned up that had been zeroing attempt counts on the overwhelming majority of rows. Everything built on top of it — ratios, scorecards, per-person reads — had been running on those zeros for a long time.

Nobody caught it, and the reason nobody caught it is the interesting part: the outputs looked plausible. They were in the right range. They moved in the right direction week over week. A number that looks reasonable is the hardest kind of wrong to find, because there is no error, no exception, and no moment where the program tells you it has stopped knowing what it is doing.

Two things came out of that. First, a floor rule: any recorded outcome implies at least one attempt, because an outcome cannot exist without one. That single invariant would have made the bug visible immediately. Second, a habit — a figure the tool produces is a claim, not evidence. Confirming it means measuring it a second way, not re-running the thing that produced it.

What else the rebuild forced

  • Idempotent ingest. Every row is content-hashed at import. Re-importing the same export, or an overlapping date range, cannot double-count. This stopped being a discipline the user had to maintain and became a property of the tool.
  • Identifier-safe parsing. Numeric-looking identifiers read out of a spreadsheet will happily arrive as floats and pick up a trailing decimal, which quietly breaks every join afterward. They are now coerced explicitly rather than inferred.
  • Memoized navigation. Derived views are stamped and cached instead of recomputed on every click. Moving between scorecards went from a visible wait to instant.
  • A GUI smoke gate. A structural edit deleted a method that nothing in the unit suite touched. Reading the diff did not catch it; launching the interface did. The gate is now part of the pre-ship run.
  • One file. The rebuild ships as a single file. The previous multi-file layout produced its own recurring failure mode, where the module being edited was not always the module being executed.

The direction change

ServeBoard was built for one operation, against one shape of data. The rebuild is deliberately less tied to that: a local-first desktop tool that ingests a messy export, resolves it into real units of work, and produces reporting that someone can act on — without a server, an account, or anything leaving the machine.

It is not a download, and it is not going to be one. It is the kind of thing I build to order. If you have a process that has outgrown its spreadsheet and nobody has ever had the budget to fix it properly, that is the conversation I want to have.

The tool’s own page is here: ServeBoard.