Most payroll software has a dirty secret: the law lives in a config screen.
Somewhere in the admin UI there's a form where a human — the employer, an implementer, a support rep — types the statutory rates: overtime multipliers, sick-pay tiers, leave accrual. The software then faithfully multiplies whatever number a person keyed in. When the law changes, somebody has to remember to update the field. When it's keyed wrong, every payslip is wrong, silently, at scale.
We took the opposite approach for our Israeli payroll-compliance engine: the statute is the code. Not a paraphrase of it, not a tunable default — the actual sections of the law, encoded as pure functions, pinned by a golden-test suite that fails the build the moment the math drifts from the statute.
Here's the architecture, and why we think it's the only defensible way to build statutory software.
The three design rules
1. The law is pure, Decimal-only Python.
Every rule lives in a rules/ package of plain modules — no Django, no ORM, no I/O. Overtime is overtime.py. Sick pay is sick.py. Convalescence pay is havraah.py. Each module opens with a docstring that cites the statute it implements, down to the section:
"""
Israeli overtime calculation (חוק שעות עבודה ומנוחה, תשי"א-1951).
Statutory basis (verified against the full statute text):
* §16(א): first 2 overtime hours at ≥125%, beyond at ≥150%.
* §2(א): regular working day is 8 hours (on a 5-day week the 1/4/2018
extension order sets a 42-hour week ⇒ 8.4h/day — parameterised).
* §17(א)(1): weekly-rest (Shabbat) and chag work pays ≥150% of the
regular wage. NOTE: 175% is a collective-agreement uplift, NOT the
statutory floor — the statutory default here is 150% and the premium
is a per-company policy override.
...
All money/hours are ``decimal.Decimal`` — never float.
"""
Two things worth stealing here:
-
Money is
Decimal, never float. Payroll math in binary floating point is how you get a payslip that's off by an agora and a compliance report that doesn't reconcile. Every rate is aDecimal("1.25"), every result rounds HALF_UP to agorot at display time. -
The statute's caveats are in the docstring, not just the happy path. That
§17(א)(1)note — "175% is NOT the statutory floor" — is the kind of thing that gets hand-waved in a sales deck and then wrong in production. We write the trap down where the implementer (future us) will trip over it.
2. Golden tests pin the math to the statute.
A golden test is a fixed input → a known, hand-verified-correct output. Not "does it run," not "is it within tolerance" — this exact shift on this exact date must produce this exact number, because that's what the law says.
Across the 17 rule modules we have 401 golden tests. A sampling of what they pin:
| Module | Golden tests | What it locks down |
|---|---|---|
minwage |
41 | The statutory floor, incl. youth/apprentice sub-rates and the 182-hour monthly divisor |
notice |
37 | Statutory notice periods by tenure |
absences |
36 | Paid/unpaid absence classification |
leave (+ maxrule) |
45 | Annual-leave accrual, the §3 Amendment-12 table (16→28 days by seniority) |
workrest |
31 | Weekly-rest entitlement, the Sunday-first 42h week |
holidays |
30 | The 9 statutory paid chagim + the Hebrew-calendar edge cases |
havraah |
23 | Convalescence-pay day counts by seniority band × the July extension-order rate |
overtime |
22 | The 125%/150% daily and weekly caps, rest-day premiums |
severance |
24 | §12 last-salary × years, the §14 8⅓% discharge, the tax-exempt cap |
sick |
23 | The 0%/50%/100% day-of-period ladder, 1.5-day monthly accrual |
| … | … | gross→net (2026 tax brackets + NI), employer burden, travel, anomaly, full-assembly |
The point isn't the count — it's that when a rate changes, exactly one golden test turns red, and the diff between "the old number" and "the new number" is the legal amendment. The test suite reads like a changelog of Israeli labor law.
3. A drift-guard between the engine and every consumer.
The engine is Python (Django backend). But our marketing site has client-side calculators in TypeScript. Two implementations of the same law = a guaranteed fork. So the TS "mirror" doesn't get to be independent: a sync test parses the Python Decimal literals and asserts every TS constant equals the Python value. Bump MONTHLY_MINIMUM_2026 in Python without updating the mirror, and the frontend build breaks. The Python engine is the single source of truth; everything else is a checked projection of it.
Why this matters (beyond correctness theater)
For a 5-employee business, payroll compliance isn't a feature — it's the thing that can cost more than the software. The Israeli statutes we encode (overtime, annual leave, sick pay, chagim, severance, convalescence) all carry real penalties for getting them wrong, and the classic failure mode isn't malice, it's a config field that drifted from the law.
When the employer can't type the rate — when the rate is Decimal("1.25") pinned by a golden test that cites §16(א) — the whole class of "stale config" bugs disappears. The law updates, we update the constant and the golden test together, the drift-guard propagates it, and every downstream calculator and payslip reflects it. One change, verified once, everywhere.
That's the bar I'd hold any statutory engine to: can you show me the test that proves this number is the legal one? If the answer is "it's in the admin settings," you don't have a compliance engine — you have a calculator with a hopeful default.
Top comments (0)