~/mesha-vegas$⌘
$ cd ../

2026-09-09

The payroll module: the arithmetic is the easy part

Multiplying hours by a rate takes ten minutes. Two years later, payroll is the most delicate part of the product. What sits between the two.

  • #saas
  • #payroll
  • #invoicing
  • #audit
  • #healthtech
  • #modelling
$6 min read10 reads
The payroll module: the arithmetic is the easy part

Everyone knows the offside rule. It fits in two sentences.

And yet we spend minutes on video review, draw lines to the pixel, argue endlessly. Because knowing the rule and applying it to twenty-two moving players are two different jobs.

Payroll is exactly that. The formula is trivial. Applying it to reality is not.

Since 2024 I've contributed to a homecare management platform for the UK market. The module that computes what the agency invoices its clients and what it pays its carers is, by a distance, the one that taught me the most.

The naive version

pay = hours_worked × hourly_rate

There. Ten minutes of work, tests included.

Now, the questions that arrive within six months:

  • The carer clocked in at 9:07 for a visit scheduled at 9:00. Paid from when?
  • They forgot to clock out. Paid for what?
  • The visit was scheduled, the service user was out. Do we pay? Do we invoice?
  • The visit crosses midnight on a Saturday. How many hours at standard rate, how many enhanced?
  • The carer worked 46 hours this week. The six hours beyond are enhanced by how much, above exactly which threshold?
  • Travel time between two visits: working time or not?
  • The rate card changed on 1 April. Do we recompute March?

None of these questions is technical. All of them change the amount someone receives at the end of the month.

The distinction that saves everything: facts and derivations

This is the lesson I carry most from this product, and it reaches far beyond payroll.

A fact: "the carer clocked in at 9:07 on 14 March". Timestamped, immutable, never recomputed. It happened.

A derivation: "this visit counts as 53 minutes of billable time at the night rate". That's the output of a computation, with a rate-card version, at a given date.

The classic mistake is storing only the derivation. The amount is in the database, the fact that produced it isn't. The day a rule changes — and it will — you can't recompute anything. The day a client disputes an eight-month-old invoice, you can't explain anything.

The opposite mistake exists too: recompute everything on the fly, never freeze. Then an invoice issued in January changes amount when you deploy in June. Nobody can work like that.

The right answer keeps both: facts as a journal you never overwrite, derivations as a dated snapshot carrying the version of the rules applied. An invoice is a computation frozen at a moment, with a trace of the rule that produced it.

Correction is part of the job

A developer's reflex, facing bad data, is to prevent the error. Stricter validation, required fields, input checks.

It's an illusion. The error doesn't come from the interface, it comes from the physical world. The carer left their phone behind. The scheduler picked the wrong service user. The visit was cancelled by phone and nobody recorded it.

What you need isn't prevention: it's tooling for correction.

Who may correct what. Until when. With what trace. And above all: what effect on what has already been invoiced and paid.

The rule I now apply: a correction never overwrites. It adds a reversing entry and a new value. That's double-entry bookkeeping applied to software, and it's the only approach that survives an audit.

It's also the feature nobody puts in the initial specification, and everybody asks for after six months.

Rounding, which costs more than you'd think

A detail that looks petty and isn't.

53 minutes at £12.40 an hour is £10.953333… What do you round to? Per visit, or on the monthly total?

The two methods give different results. On one visit the gap is a penny. Across four hundred monthly visits for a hundred carers, the gap becomes an agenda item.

Three rules I apply without exception:

  • Never use floating point for money. Store integers in the smallest unit — pence, francs. Floats introduce errors visible only at the thousandth, and very visible at the end of the quarter.
  • When you round is a business decision, not an implementation detail. Document it.
  • Which way you round, too. In whose favour, when it lands exactly halfway?

Nobody asks for this. Everybody notices when it's wrong.

What a named rule changes

A business rule buried in an if is a lost rule. Nobody can enumerate it, test it in isolation, or explain to the client why the system computed that.

A named rule — NightEnhancement, WeeklyHoursThreshold, BillableTravelTime — becomes a domain object. It can be tested alone. It can be switched on per client. And above all it can be reported back: next to the amount, you can show which rules were applied and in what order.

That's the difference between an invoice people endure and one they understand. Commercially, it's what makes an agency stay: they can answer their own clients without calling you.

The underlying problem

There's an imbalance this product made obvious to me.

We train developers in technique — frameworks, algorithms, performance. We barely train them to interrogate a business. Yet in management software, which is the vast majority of software written in the world, the difficulty is almost never technical.

The code that causes problems isn't the slow code. It's the code that modelled reality wrongly, and it runs very fast.

Automation sharpens this. An assistant writes a correct payroll function in seconds. It doesn't know that at this client, a visit cancelled less than two hours ahead is still billed at 50%. That knowledge is written nowhere — it lives in the head of a person you have to go and listen to. I've developed that point elsewhere.

Four questions before coding anything that computes money

  1. What is the fact, and what is the derivation? If you can't separate them, don't start.
  2. What happens when the rule changes? Retroactive recompute, or not? Who decides?
  3. How do you correct an error that's already been invoiced? If there's no answer, the product isn't ready.
  4. Who can explain this amount to an unhappy client? If the answer is "the developer", a feature is missing.

Those four questions have saved me more trouble than any architectural choice.


The platform is described on its project page, without confidential detail. If you're building a product that computes money — invoicing, payroll, commissions — let's talk.