A unionized payroll management system
A custom payroll engine that encodes each union's collective bargaining agreement as configurable rules — computing pay, running retros, remitting contributions, and producing audit-ready compliance reports.
- Client
- Multi-union employer (NDA)
- Industry
- Workforce & labor
- Services
- Custom software, Digital transformation
- Rules
- Each CBA as versioned data
- Retro
- Re-run periods under the new rates
- Compliance
- Certified payroll from the same run
When a contract covers multiple unions, each governed by its own collective bargaining agreement, payroll is not a software configuration problem — it is a rules-encoding problem. Every union has its own pay scales, its own step progressions, its own shift differentials, its own overtime thresholds, and its own contribution schedules for dues, benefits, and pensions. Off-the-shelf payroll systems cannot hold that complexity; they flatten it, and every exception becomes a manual workaround entered by someone who may or may not remember the fine print of a contract negotiated years ago.
The client had been managing the gap with spreadsheets layered on top of a commercial payroll platform, and the seams showed. Pay errors were routine, grievance filings were climbing, and each new CBA settlement triggered weeks of retroactive corrections done by hand. They needed a system that understood the contracts — not one they had to work around.
The problem
The core problem was representational: no existing system could encode the full ruleset of even one CBA, let alone several, with enough fidelity to compute pay automatically and auditably. Every attempt to use general-purpose tooling required exceptions, and exceptions meant human intervention at scale.
Three hard constraints shaped the design:
- Correctness is non-negotiable. A single classification error on overtime or a missed shift differential can trigger a formal grievance under the contract. The system had to compute identically to what a trained labor-relations specialist would compute by hand — and prove it.
- CBAs are living documents. Contracts expire, get renegotiated, and are sometimes settled after the prior one lapses, creating retroactive obligations across the workforce going back to an effective date.
- Compliance reports are external obligations. Certified payroll filings, union remittance reports, and benefit-fund summaries go to third parties who will audit them. The output format and the audit trail behind it had to be defensible.
What we built
We built a payroll engine whose core primitive is a CBA rule set — a versioned, date-bounded configuration object that encodes every contract provision in structured data rather than code. When the engine processes a pay period, it resolves which rule set applies to each employee, then executes the same deterministic calculation chain against it.
Key modules:
- CBA rule engine — each agreement is stored as a versioned configuration: pay grades, step tables, shift-differential matrices, overtime and double-time thresholds, holiday schedules, and premium triggers. Rules compose, so a night-shift overtime hour applies differentials and overtime multipliers in the correct sequence.
- Time-entry integration — shift records, job codes, and worked hours flow in from the existing time-and-attendance system via a normalized import layer. The engine classifies each hour against the active rule set before any calculation runs.
- Retro-pay processor — when a contract is settled with a retroactive effective date, a single batch job re-calculates every affected pay period under the new rates, diffs the result against what was paid, and produces both an employee-level variance report and the payroll journal entries needed to issue corrections.
- Contributions and remittances — union dues, pension contributions, and benefit-fund deductions are computed per rule set and batched into remittance files formatted for each fund administrator, with a reconciliation ledger that ties every dollar to the payroll run that generated it.
- Compliance and audit reporting — certified payroll reports, prevailing-wage certifications, and union-specific summaries are generated from the same source records that drove the paycheck. Every output line traces back to a time entry, a rule version, and a calculation log.
- Admin UI — a Next.js interface lets the labor-relations and HR teams configure new pay grades, activate new contract versions, preview the impact of a rule change before it goes live, and pull ad-hoc audit trails without needing an engineer.
The architectural decision that made it work was separating the rule layer from the calculation engine completely. Rules are data; the engine is stateless logic that runs against them. That meant a CBA amendment could be entered by an HR administrator, effective-dated correctly, and applied consistently across every future pay run without touching application code. It also meant every calculation was reproducible: given the same inputs and the same rule version, the engine will always produce the same output — a property that proved essential during union audits.
How we approached it
We structured the work in three stages, each designed to retire a different category of risk before the next began.
The first stage was spent entirely on rule modeling. We worked through each CBA with the client's labor-relations team, converting contract language into structured definitions and building a test suite of hand-calculated scenarios — complex overtime situations, multi-union shift splits, mid-period step progressions — that the engine had to match exactly. Nothing moved forward until every scenario passed.
The next stage introduced the time-entry integration and ran the engine in shadow mode: calculating pay in parallel with the existing process, comparing outputs, and investigating every discrepancy. Discrepancies that surfaced revealed gaps in the contract interpretation, not bugs in the code — which is precisely what this phase was designed to find.
The last stage brought contributions, remittances, and compliance reports online, with the first live retro-pay batch run against a recently settled contract. The engine produced the variance report and the journal entries; the team reviewed them the way they would any other payroll run.
Change management was handled primarily through the admin UI. The goal was to give the labor-relations and payroll teams enough visibility into what the engine was doing — and enough control over configuration — that they trusted it rather than felt replaced by it. Parallel runs helped: when the team could see their own hand-calculated numbers matched by the engine, adoption followed.
What we will not claim
No pay-error percentage, no grievance reduction, no close-time in hours. Those figures are not in this repo. The public claim is the system: each CBA as versioned rules, a retro batch that re-runs history under the new rates, and compliance reports that come from the same calculation that printed the check.
Start a build if your payroll complexity has outgrown the tools your vendor sells — or explore our custom software development practice for more on how we approach high-stakes systems.
Built with
- Next.js
- Node.js
- PostgreSQL
- TypeScript