Senior Frontend Engineer with 14 years of experience delivering production software across enterprise, agency, and startup environments, frontend-weighted throughout. I'm driven by making ideas a reality, and these days that means being brought into senior engagements to own architecture and delivery from day one, stepping into complex domains to set technical direction.
My expertise spans the entire stack, from React and TypeScript on the frontend, to Node and Postgres, down to AWS, Kubernetes, and Terraform infrastructure. I pride myself on leaving teams stronger than I found them, mentoring engineers to independent ownership, streamlining deployments, and establishing engineering standards that have cut QA bug reports by 35%.
Selected work
PaymentsMandM2026
Two payment providers, one contract
Problem
Adyen and Klarna disagree about what an error is. Adyen returns a declined card as HTTP 200 with a resultCode of Refused in the body. Klarna returns the same refusal as a 4xx. One verdict is read from the payload, the other from the status line, and the storefront had to know which provider it was talking to before it could tell whether a payment had failed.
Decision
One BFF inside the Next.js app, owning a single error contract defined at that layer rather than in the client, with card entry left inside the providers' own hosted components.
Trade-off
A single translation layer is a single point of failure, and provider detail can get lost inside it. Klarna types its errors well enough to drive retry logic, Adyen leaves refusals as prose, so that judgement had to be rebuilt in the mapping rather than passed through.
Outcome
The client stopped branching on provider. Our services handled only session IDs, payment references and tokens, so no card number reached application scope.
DirectionTLA2025–26
Getting the domain out of two people's heads
Problem
A sales-leads CRM to build from nothing, in a business where the domain lived with two senior colleagues rather than in any documentation. Nothing written to build against, and no settled account of what the business logic was meant to do.
Decision
Joint technical authority with the Head of Development, two of us setting direction and defending it. The first work was not code but structured sessions with the two people who held the domain, turning what they knew into a written model that then shaped the data tables, the API contract feeding the front end, and the library choices. Fastify on the backend, Azure Container Apps for hosting, Shadcn and Base UI on the front, a custom OpenTelemetry setup, Playwright for end-to-end coverage, and curated Claude contexts as standard tooling rather than a side experiment.
Trade-off
Every one of those picks hands you the maintenance, whether that is Fastify over a batteries-included framework or hand-rolled OpenTelemetry over a vendor SDK. With two people holding authority that was a cost we took on deliberately, and it never came back to bite.
Outcome
Internal sales staff started feeding back positively on it, the first real signal the direction was right.
InfrastructureDecentralizard2025–26
A two-tier production platform serving content
Problem
I did not want the platform coupled to Vercel, and I wanted somewhere to run a genuine EKS setup, Terraform-provisioned with full observability, without paying to leave a cluster idle. If the hosting decision was going to shape the architecture, I wanted to be the one making it, and to understand every layer underneath.
Decision
Two tiers, both mine. Tier one is a Dockerised Next.js site on a low-cost ARM VPS, released blue-green, with public traffic arriving over a Cloudflare Tunnel, so no open port and no public IP anywhere in the setup. Tier two is a Terraform-provisioned EKS cluster with full observability, created on demand and destroyed after use.
Trade-off
I own the operating system, the patching, the deployments and the pager. And at this scale there is no saving. The VPS is cheap, but so is the managed platform I turned down.
Outcome
Tier one is live and serving. Tier two comes up when I need it and goes straight back down, which is the only reason it costs nothing. The saving is not the point at this size. The pattern is the one that keeps a real platform's bill down.
PerformanceJaguar Land Rover2022–24
A six-level hierarchy data structure that had to feel instant
Problem
A recursive view of stores, departments and dealers worldwide, six nested levels deep, up to a hundred paginated rows at each, every row carrying twenty to thirty columns. It sat on a .NET backend whose deep query took around ten seconds to answer.
Decision
Move the data flow onto WebSockets with client-side caching and progressive expansion. The backend rewrite that would have fixed it at source sat with another team on another roadmap, so the front end absorbed the latency instead.
Trade-off
Perceived speed bought at the cost of consistency, connection management and a considerably more complicated client.
Outcome
The interface stayed usable while the backend caught up. I was sole developer on the portal for long stretches, taking features from ticket through data flow and UI to release.
JudgementWyser AI2025
The desktop app I argued for, and the extension I shipped
Problem
Citizens Advice advisers needed reliable background audio capture on Windows machines. I was the sole R&D voice on it.
Decision
The research pointed clearly at an Electron desktop app. The business went the other way, so I built the Chrome extension as an interim.
Trade-off
I shipped something I had argued against, and told users plainly what it could and could not do rather than overselling it. The proof of concept took two weeks. The cost came after, as features and fixes accreted onto it while clients were already testing.
Outcome
Demoed to Citizens Advice clients directly. Adopted across the Scotland and Somerset regional offices and AdviceUK.
Now
Last updated
Building an embeddable AI support widget on Cloudflare's developer platform. It answers from a site's own documentation with cited sources, over Workers for the API, a Durable Object per conversation for the streaming reply loop, AI Search for retrieval and Workers AI through AI Gateway for generation. Neon Postgres via Hyperdrive is the system of record, with R2 and Queues behind ingestion.
Below a confidence threshold it hands off to a human rather than guessing. That costs the automation rate this kind of product is usually sold on, and it is the decision I would defend first.