GovSpend
Grew engineering from 2 to 13 and launched six AI products; customers named the four primary ones as deciding factors in $10M+ of new sales and renewals.
Open case studyEngineering leader. I build teams and ship AI products at scale.

Grew engineering from 2 to 13 and launched six AI products; customers named the four primary ones as deciding factors in $10M+ of new sales and renewals.
Open case study
What I build with my own hands - an agent that runs incident response end to end.
Open case study
Change the Game. My animation framework took it from freezing phones to 60 FPS and unblocked launch.
Open case study
Platform for Google events
Open case studyEngineering leader. I build teams and ship AI products at scale.
GovSpend · Government procurement intelligence SaaS
Lattice · HR and performance management SaaS
Little Cinema Digital · Interactive streaming platform
Izllo · Marketplace startup
Earlier: AKQA (Audi USA), 2015 - 2017 · The Barbarian Group (Samsung US), 2015
How can I make myself replaceable?
My management philosophy comes down to one question I ask myself: how can I make myself replaceable? People grow most when they have clarity on what success looks like, the autonomy and trust to find their own way through, and consistent, pointed feedback along the way. My engineers would describe my style as collaborative, structured, trusting and autonomous. I am a hands-on coach early on, then I deliberately step back so people can grow into ownership.
Four tech leads I coached now own their roadmaps and operate without me in the room. At GovSpend I promoted two engineers to Staff and one to engineering manager. That is the measure I actually care about.
The hardest part of working this way is the discipline to refrain from stepping in, especially when you can see something about to go wrong and you know exactly how to fix it. Letting someone experience it and then coaching them through the reflection is almost always more effective than solving it for them. The risk is stepping back too early, before somebody is ready, and I guard against that by being explicit about what success looks like and staying closely observant at the start.
When somebody wants a decision from me, I ask for four things: the problem, the options they considered, the tradeoffs, and their recommendation. I call it the Decision Stack. People stop bringing me problems and start bringing me their call. They own the decision, and I weigh in if I see a risk they missed.
Telling whether a team is actually succeeding comes down to four things, and they build on each other. Clarity: everyone individually understands the why behind the mission. Metrics: measuring cause and effect, so we understand our impact and can realign when we need to. Synergy and ownership: an environment where everyone feels responsible for the outcome, with no egos and no gatekeeping, just a shared urgency to succeed together. And delivery: a target date is the focal point that closes the loop, because knowing the when informs both the strategy and the urgency.
Once a team has clarity, metrics and ownership, I rarely have to chase the date. They hit it on their own.
I evaluate engineers on two pillars. Impact, which is reliability, performance and scalability. And communication and leadership, which is alignment, clarity and knowledge-sharing. At the higher levels I expect engineers to design systems that other teams build on. I wrote these into a career matrix with four levels: Associate, Engineer, Senior and Staff. Using AI well is part of what I expect at every level.
On team structure, I aim for roughly thirty percent senior, forty percent mid and thirty percent junior. It creates growth paths without sacrificing delivery on complex work. I keep a primary and a secondary on every project so no single person is a point of failure. I change the structure as the business changes, and I keep each engineer's growth in mind when I do.
Across a larger org, I made RFCs mandatory for significant work and anchor my reviews on production risk, scale and maintainability: what happens if this fails, how do we mitigate it, what will this cost in production? I lean on the experts on my team. My job is to set things up so the right decisions get made, even when I am not in the room.
I lead engineering teams that ship. Most recently I was Director of Software Engineering at GovSpend, where I grew engineering from two people to thirteen across four product tracks and launched six AI products. Customers named the four primary ones as deciding factors in more than ten million dollars of new sales and renewals. Before that I managed a ten-person platform team at Lattice, led the team behind a no-code streaming-site builder at Little Cinema, founded a startup, and was a web engineer at Google.
With people, I start by learning what drives each person and where they want to grow. Then I set them up and step back. At GovSpend I promoted two engineers to Staff and one into engineering management. I am prouder of that than of anything I have shipped.
I still write code. I want to know the work I am asking my team to do. At GovSpend I built our AI evaluation layer on top of Mastra - golden datasets, blinded LLM-as-judge and human review, CI/CD gates - and on my own time I build agents like the AI Chief of Staff in the work section.
I have been taking things apart since I was six, starting with toys and old computers. I still like finding out how things work.
Outside work I dance, travel, play piano, train in martial arts, and try something new as often as I can.
Five pieces on TypeScript's error messages, static typing, and what to do with an engineer who is failing in the wrong role. Read over thirty thousand times.
30,000+reads on Medium
Poor Performer? You Might Just Have Them in the Wrong RoleI once managed a junior engineer who was struggling in a conventional software-development position.
The TypeScript Guide I Wish I Had
A Cheat sheet for Python and Typescript static typing
Conquering a complex error in TypescriptHow to read and understand the Object Literal error
How To Read Errors in TypeScriptTypeScript can be riddled with errors and warnings, for seemingly every little thing. Take this function for example:Hiring an engineering leader? Let's talk. I am open to Engineering Manager, Senior Engineering Manager and Director roles. Email is the most direct, my resume is a PDF you can download, and you can also find me on LinkedIn or GitHub.
An experiment that monitors AI's behavior when placed in a simulated world
Four frontier models share one map and play a 4X strategy game against each other. They settle cities, research technology, raise armies, sign treaties, trade, message each other privately, and speak to everyone at once. Nobody is told to be peaceful and nobody is told to fight.
Every prompt, reasoning trace, order and state change is recorded, and the whole match replays byte-identically from the log. The engine has no network, no clock and no randomness of its own - every roll comes from a seeded generator keyed by turn and purpose - so a result is a thing you can go back and check rather than a thing you have to have watched.
All four seats get a byte-identical eleven-thousand-character prompt. The only instruction in it is "achieve success and conquer the game as you see fit" - no scoring hint, no strategy, no preference for peace or war. The rest is reference material: terrain, combat maths, what a city costs.
One thing is deliberately not hidden. Every civilisation sees every rival's progress toward the science victory, every single turn, and the rules text says why in as many words: a rival at 90% is a more urgent problem than a rival with a large army, because an army can be defended against and a finished wonder cannot be undone.
| Claude | GPT | Gemini | Grok | |
|---|---|---|---|---|
| Model | claude-haiku-4-5 | gpt-5.4-mini | gemini-3.6-flash | grok-4.3 |
| Cities at turn 110 | 3 | 9 | 13 | 9 |
| Final score | 566 | 840 | 2,067 | 360 |
| How it ended | Conceded on turn 64 | Won on turn 110 | Won its war, lost the match | Still building settlers |
On turn 64 it wrote "GPT apex_theory unlocked turn 61 confirmed" and called the match mathematically lost. Its own screen said otherwise that turn, and GPT would not win for another 46.
One-sentence turn plans, 97 messages of non-aggression paperwork, and a confidence band that never moved when it was attacked. It kept the borders quiet and finished the build.
Correct read, correct declaration, three captured cities, and the highest score on the board. It reached the wonder's city on turn 108 without ever learning that was what it was.
From turn 77 every plan was a near-copy of the last: disperse settlers, fortify, build more settlers. 268 settler orders, three cities founded, zero attacks after declaring war.
Gemini declared war on turn 77, the turn GPT's progress jumped to 96. It dropped research to zero, taxed at 100%, and converted its whole economy to swordsmen and catapults - 79 swordsman and 60 catapult orders - then spent twenty turns selling its own techs to Claude and Grok for twenty to a hundred gold a piece to make army payroll.
The war worked.
| Turn | What became of it | |
|---|---|---|
| Delta | 89 | Held to the end |
| Sigma | 94 | Burned by raiders on turn 101 |
| Gamma | 106 | Held to the end |
| Alpha | - | Never taken, never identified |
Alpha finished the wonder and ended the match. Gemini first named it on turn 108, two turns from the end, once taking Gamma had brought it into view - and even then it described capturing Alpha as a way to "severely impair their capabilities", not as a way to stop a wonder. Scrub the replay below to turn 106 and look at where Alpha is. Three cities taken, one of them lost to the wilderness while the army was forward-deployed, and the one that decided the game was never identified as worth taking.
That is not a targeting error. Nothing in the game would have told it.
This is GPT's public state, as displayed on Claude's own screen, on the four turns leading to the concession.
| 61 | 62 | 63 | 64 | |
|---|---|---|---|---|
| Techs known | 16 | 16 | 16 | 17 |
| Apex tech | false | false | false | false |
| Progress | 58% | 58% | 58% | 62% |
GPT apex_theory unlocked turn 61 confirmed. Match is mathematically lost.
What followed, it called a terminal endgame hold:
There is a reason the mistake was available to make. The public number is not build progress and never was: it is 70% of techs researched plus a flat 30 for holding the apex tech, capped at 99 until the wonder lands. It carries no information about construction at all. Claude tried to read a completion date out of a metric that has none, invented one, and then played 42% of the match as a concession speech.
The rules are emphatic that the science race is the thing to watch. They are also silent about where it is happening.
| Its own cities | A rival's | |
|---|---|---|
| City names and positions | Every turn | Only where it had scouted |
| What a city is building | Every turn | Never |
| Turns to completion | Every turn | Never |
| Science progress | Every turn | Every turn |
At turn 95, in the middle of the war it had declared to stop the wonder, Gemini had seen one of GPT's ten cities. The only event in 110 turns that ever named the wonder fired on turn 110, when it completed and the match ended.
Put that beside the metric. From turn 80 the public number could not move, because it had run out of room to move in. So for the last thirty turns, three civilisations knew that someone was one build from winning, and had no way to learn where it was being built or when it would land.
What Grok did do, on the turn it declared war, was send this to GPT:
Maintaining our NAP and peaceful border while we both address the GPT threat.
It sent the anti-GPT rally text to GPT. That is diplomacy as template-filling rather than as a model of whoever is on the other end, and it is a smaller and more ordinary failure than lying.
GPT read it the way it landed rather than the way it was meant, and wrote its own dossier entry on turn 85:
Unstable rival whose peace language did not prevent war; treat as hostile unless a fresh treaty is explicitly accepted. Trustworthiness: low.
| Claude | GPT | Gemini | Grok | |
|---|---|---|---|---|
| Cost | $5.42 | $4.94 | $1.43 | $1.99 |
| Per turn | $0.049 | $0.045 | $0.013 | $0.018 |
| Output tokens | 809k | 878k | 148k | 166k |
| Visible text | 809k | 92k | 148k | 166k |
| Median latency | 40.1s | 55.6s | 26.5s | 20.2s |
The cheapest model on the board was 39% of the run. Claude ran 131 calls for 110 turns, 21 of them schema repairs, and with no extended thinking enabled it emitted every one of its 808,740 output tokens as visible prose - much of it the same terminal endgame paragraph, again. GPT's larger total is about 90% hidden reasoning behind one-sentence turn plans.
Read the source
Python, no network and no clock of its own - every roll comes from a seeded generator keyed by turn and purpose
Terrain, combat maths, victory conditions, and the one line of objective, as they stood for this match
Scrub through the whole match in the project's own viewer. The insights panel states its findings as of the turn you are on, so a finding can disappear when you scrub back - at turn 70 nobody has declared war yet, and nine turns later two civilisations have.