Build Payroll In-House: 172 Tests, Penny-Perfect
Conventional wisdom says never build payroll in-house. Here's when it's justified, and how I made a multi-state payroll tax engine penny-perfect.
By Mike Hodgen
The Conventional Wisdom Is Right (Until It Isn't)
Every accountant, every CFO, every blog post you will ever read says the same thing: never build payroll. Buy Gusto. Buy ADP. Buy Rippling. Pay the monthly fee and never think about it again.
That advice is correct for 95% of companies. I want to be clear about that before I tell you what I did.
Payroll is unforgiving in a way most software is not. The tax tables change quarterly. A bug doesn't just annoy a customer, it underpays the IRS, and the downside is regulatory, not cosmetic. You can ship a broken checkout flow and fix it Tuesday. You cannot ship a broken withholding calculation and explain it away to a revenue agent.
So when someone asks me whether they should build payroll system in-house, my default answer is no. Loudly. Buy it.
And then I went and built one anyway, for a roughly 10-person multi-state employer I run.
I know how that sounds. Isn't this insane? You just spent four paragraphs telling me not to do the thing you did.
Fair. So let me earn it. There were specific conditions that made building the right call, and there's a specific mechanism that made the result trustworthy enough to actually file taxes from. Both matter. The conditions tell you when it's justified. The mechanism tells you how you stop it from being a disaster.
This is the article I wish existed before I started. Not a brag about my code, but a decision frame: when owning your payroll math is the safe choice instead of the reckless one, and how you prove the engine is right down to the penny before you trust it with money that legally isn't yours.
The Forcing Function: Leaving a Bundled System Mid-Year
I don't build things because they're interesting. I build them because the alternative is worse. Here, the alternative was genuinely worse.
The company was leaving a bundled accounting-plus-payroll product in the middle of the year. That's the whole story. The forcing function wasn't ambition, it was a migration with a hard deadline and two specific risks attached.
The trust-fund stakes nobody talks about
First, broken year-end forms. If the historical wage data didn't carry cleanly from the old system, the W-2s and 941s at year-end would be wrong. Wrong forms mean amended forms, penalties, and a very bad January.
Second, and this is the one that changed everything: missed trust-fund deposits.
When you withhold federal income tax and the employee's share of FICA from a paycheck, that money is not yours. It's trust-fund money you hold on behalf of the government. Miss those deposits and you're exposed to the Trust Fund Recovery Penalty.
Here's the part people gloss over. The TFRP can be assessed personally against "responsible persons." It pierces the corporate veil. And it is not dischargeable in bankruptcy. This is the rare business mistake that follows you home.
Why mid-year is the worst time to switch
Switching providers mid-year is the worst possible window. The clean answer is a migration vendor who carries your year-to-date totals over and reconciles everything. Simplest path on paper.
But that path meant trusting a black box during the most dangerous moment in the payroll calendar, with personal liability sitting on the table if the box was wrong.
I had two options. Trust someone else's math during the transition, or own the math myself and verify it. Given what was at stake, the black box was the riskier choice. I chose to own it.
What a Real Gross-to-Net Engine Actually Has to Do
The scope sounds infinite. It isn't. It's bounded, and once you see the boundaries, the whole thing gets less scary.
The four-taxable-bases rule
The single most important concept: there is no such thing as "taxable wages." There are multiple definitions of taxable wages, and different taxes apply to different ones.
The Four Taxable Bases Rule
Federal income tax, Social Security, Medicare, and state taxes each have their own taxable base. A 401(k) deferral reduces federal taxable wages but not Social Security wages. You cannot compute everything off one number. Get this wrong and every downstream calculation is quietly off.
Federal, FICA, and FUTA
Federal income tax comes from the IRS Publication 15-T percentage method schedules. Not the wage-bracket tables, the exact percentage method, because I wanted precision, not bracket approximation.
FICA is two pieces. Social Security at 6.2% with an annual wage base cap. Medicare at 1.45% with no cap, plus the Additional Medicare surtax that kicks in past a threshold. FUTA on top, federal unemployment.
The wage base cap and the surtax threshold are both mid-year events. An employee crosses them on some random paycheck, and the engine has to handle the transition cleanly.
Multi-state: CA, Utah, and one Ohio municipality
Then the state layer. California PIT using the Method B exact-calculation tables, plus California SDI. Utah state withholding. Ohio state, plus a single mapped municipal locality, because Ohio cities tax wages too.
Payroll Engine Tax Layers Architecture
Here's the surprise. The math wasn't the hard part. The hard part was discovering, through the actual payroll data, that the workforce was secretly multi-state. California plus Ohio plus Utah. Nobody had flagged it. The data told me.
One principle held throughout: this is math, not judgment. There's a right answer to every line. I let the model judge and the code compute everywhere in my stack, and payroll is the purest example. No AI touches the calculation. Ever. It's deterministic arithmetic against published tables.
The Replay Oracle: How You Prove a Payroll Engine Is Right
This is the section that answers your real question. Not "can you build it" but "how do you trust it."
The answer is a replay oracle.
Back-solving every employee's W-4 elections
I took the real historical paychecks from the old system and fed them in. Then, for each employee, I back-solved their W-4 elections, filing status, dependents, extra withholding, until my new engine reproduced the old check exactly.
This is the inversion that makes it work. I'm not guessing whether my engine is right in the abstract. I'm asking a concrete question with a known answer: given this employee, does my engine produce the exact check they already received?
Matching the old system to the penny
Then I diff every field. Not the bottom line. Every line. Gross, each tax, each deduction, net.
The Replay Oracle Validation Loop
If my engine and the old system disagree by a single penny on any line, that's a failing test. Not a rounding tolerance. A failure. Penny-perfect or it doesn't pass.
That sounds obsessive. It's the opposite. A penny gap means I don't understand something, and the thing I don't understand will be the thing that compounds into a wrong W-2. The penny is a canary.
This is how you earn the right to file taxes from a system you built yourself. You don't assert correctness. You replay every old result against the new engine and prove it reproduces a known-good reality before you let it touch a live run.
And the oracle paid off in an unexpected way. The hidden multi-state reality only resolved when I modeled the right states. An employee's withholding pattern wouldn't match until I correctly placed them in Ohio. The data confessed.
172 Golden Tests and a Deposit Calendar With an Alarm
Reproducing history once isn't enough. The tax code changes, my code changes, and I need every future change to scream if it moves a number it shouldn't.
What a golden test suite covers
So I built a golden test suite. 172 tests, each one pinning a known-good gross-to-net result across the edge cases that actually break payroll engines.
What they cover:
- Wage base caps crossed mid-year, where Social Security stops accruing
- The Additional Medicare surtax threshold
- Multi-state wage allocation across CA, OH, and UT
- The Ohio municipal locality
- Supplemental wage withholding (bonuses run on a different method)
A golden test means the correct answer is frozen. If any code change moves a single number on any of those 172 scenarios, the build breaks loudly. I cannot quietly introduce a regression. The test suite won't let me.
The overdue-deposit alarm
But correct calculation is necessary, not sufficient. Let me be blunt about where the real danger lives.
Two Failure Modes: Calculation vs Missed Deposit
The thing that creates personal liability isn't a wrong number. It's a missed deposit. You can calculate the withholding perfectly and still get hit with the Trust Fund Recovery Penalty by failing to deposit it on time.
So the system has two operational layers beyond the math. First, every payroll run posts as balanced double-entry to the general ledger, feeding the same Postgres ledger I built to replace QuickBooks, so wages and tax liabilities hit the books correctly and the trust-fund money is visible as a liability, not vaporized into a single expense line.
Second, a deposit and filing calendar with an overdue alarm. It knows when each deposit is due and it nags. Loudly.
Honest note: this calendar logic is the part I watch most closely. A silent failure here is the expensive one. A calculation bug breaks a test. A calendar bug breaks nothing, until the deadline passes and nobody got told. That asymmetry keeps me cautious.
When Building Payroll Is Justified (And When It's Insane)
I'm not telling you to build payroll. I'm telling you to actually run the decision instead of defaulting.
The conditions that made it worth it
Building was justified here because four conditions lined up:
- Small, knowable workforce. Ten people, not three hundred. I can hold the entire payroll in my head.
- A real forcing function. A mid-year migration off a broken vendor with trust-fund liability attached. The black box was riskier than the build.
- An operator who can own the tax research. Somebody has to read Pub 15-T and the California Method B tables and update them every quarter. That somebody existed. It was me.
- GL integration was needed anyway. Payroll had to feed the ledger cleanly. Building it meant the integration was native instead of a fragile sync.
When those four hold, building stops being reckless.
When you should absolutely just buy Gusto
When they don't, building is insane. Buy Gusto if:
Build vs Buy Payroll Decision Matrix
- You have high headcount churn, where every new hire is a new edge case
- You operate in a dozen-plus states, where the table maintenance alone is a full-time job
- You don't have anyone who will own quarterly tax updates forever
- You have no real reason to leave your current provider
That last one matters most. The maintenance burden is permanent, not one-time. The tables change every quarter, the surtax thresholds drift, states tweak their methods. If you're not prepared to maintain it indefinitely, you're building a liability with a fuse on it.
This is the same logic I apply to when to fire QuickBooks and when not to. The point was never "build it." The point is to know the conditions before you assume buying is automatic.
Owning the Math Is What Made It Safe
Here's the reframe I want to leave you with.
The instinct is that building payroll is the risky choice and buying is the safe one. For most companies, that instinct is correct.
But for this specific employer, in this specific mid-year window, with personal trust-fund liability on the table, owning the math was what made it safe. The failure mode I could not survive was a quiet black-box error during a year-end transition that I wouldn't catch until the W-2s were wrong and the deposits were late.
A black box gives you an unknowable risk. The replay oracle and 172 penny-perfect golden tests turned that into a verified one. I know my engine is right because I proved it reproduces reality, line by line, to the cent.
That's the whole move. Not "build everything." Most companies should never build payroll, and I'd talk most of them out of it.
But most companies have one or two systems where the right answer is to own the logic instead of renting a black box, and they never even ask the question. They assume buying is automatic, and sometimes that assumption costs them the one thing they can't afford to get wrong.
That's the conversation I have with operators. Not "what can we automate," but "where is the black box actually more dangerous than the build." If you've got a system like that and you're not sure which way it cuts, tell me where it actually hurts.
Want to explore what AI could do for your business?
Book a free 30-minute strategy call. No pitch deck, no sales team, just a real conversation about your operations and where AI actually fits.
Get AI insights for business leaders
Practical AI strategy from someone who built the systems — not just studied them. No spam, no fluff.
Hodgen.AI
Ready to automate your growth?
Book a free 30-minute strategy call with Hodgen.AI.
Book a Strategy Call