Boring where it counts
I’ll reach for the proven option in the parts that have to stay up, and save the newer tooling for places where being wrong is cheap.
My process
Three phases, roughly.
They overlap more than this page suggests, but the order is real: understand the problem, build it properly, then make sure it holds up once people are using it.

Step 01 / 03
I start by getting clear on the problem rather than the feature list. Who uses this, what breaks today, and what has to be true for the thing to be worth building.
That usually means reading whatever already exists — the current codebase, the data model, the support tickets — and asking the questions that surface constraints early. Auth boundaries, tenancy rules, and anything real-time are worth finding now rather than halfway through.
I come back with a plan: what I’d build first, what I’d deliberately leave out, and where the risky parts are.
Included in this phase

Step 02 / 03
I build in slices that ship. Rather than finishing the frontend and then starting the backend, I take one real path through the product — interface, API, data — and get it working end to end, then repeat.
Types are shared across the stack so the frontend and the API can’t quietly disagree, and tests get written alongside the code rather than bolted on before a deadline. I use AI coding tools throughout; they make the mechanical parts faster, they don’t replace the review.
You see it running as it goes. No long silences followed by a big reveal — it’s easier to change direction in week two than in week ten.

Step 03 / 03
Shipping is the start of the interesting part. I care about how the thing behaves once it has real traffic on it — slow queries, race conditions, the edge cases nobody hit in staging.
Before launch I go through the boring checklist properly: accessibility, error states, loading states, what happens on a bad network. These are the parts that get skipped under deadline pressure and they’re the parts users actually notice.
After launch I’d rather hand over something you can maintain than something that only I understand. That means readable code, written decisions, and a working local setup.
What this includes
How I decide
Most technical decisions are trade-offs, not right answers. These are the ones I lean on when a call has to be made.
I’ll reach for the proven option in the parts that have to stay up, and save the newer tooling for places where being wrong is cheap.
The frontend, the API, and the database agreeing on shape catches a whole class of bug before anyone runs the app.
Keyboard navigation, focus states, and real semantics from the first commit. Retrofitting accessibility costs several times more.
In multi-tenant systems, tenant isolation is enforced at the data layer — not left to whichever query someone writes next.
Before optimising anything I want a number for how slow it is now. Most performance intuitions are wrong.
I use Claude Code and Copilot daily. They write a lot of the first draft; nothing lands without me reading it.