Engineering
How the things I've built actually work
Write-ups of production systems — the decisions, the trade-offs and the failures. Client names and data are deliberately absent; these are about the engineering, not who paid for it.
Payments
Reliability
- The outage that logged at debug levelA feature degraded silently in production for weeks because three separate fallback paths turned a hard API failure into a quiet one. A postmortem on defensive code that defends the wrong thing.
- The one row that ate the queueA background worker with no terminal-failure state retried a single unprocessable item forever, starving every healthy item behind it and burning paid API quota continuously.
AI systems
- Keeping embeddings fresh without blocking a requestSemantic search is only as good as the freshness of its embeddings. An indexing pipeline with one authoritative write path, batch backfill, real-time incremental updates, debouncing and dead-letter handling.
- Getting a data structure out of a language modelA tool-calling ReAct agent that turns unstructured documents and third-party profile data into a validated record — and what breaks when you treat model output as trustworthy.
- Asking a model to score a match, and storing the reasonsGenerating a 0–100 relevance score for an application, with the specific evidence behind it — why the explanation matters more than the number, and where scoring quietly goes wrong.
Mobile
- Owning a React Native release end to endTaking a cross-platform app from an empty repository to live releases on both stores — signing, provisioning, per-PR builds and the parts of mobile delivery that have nothing to do with JavaScript.
- Shipping through a store policy deadlineAn app flagged under a media-permissions policy with removal as the stated consequence. What the policy actually required, and how to comply without breaking the feature that depended on the permission.
- The notification that opened the wrong screenDeep linking and push routing across iOS and Android — association files, cold start versus warm start, and why tap handling is where the bugs actually live.
Security
- Authorisation belongs in two placesFinding and closing broken object-level authorisation across six subsystems of a growing API — why the route layer alone isn't enough, and what makes these bugs so easy to ship.
- The endpoint that sends an SMS to anyone who asksAny unauthenticated endpoint that triggers a real-world action is an abuse vector. Layered rate limiting, a challenge, and the tracking you need to enforce either.
Platform
- Multi-tenancy is a decision you make on day oneBuilding a multi-tenant employer product from an empty repository — where tenant scoping actually belongs, and why retrofitting it is so much worse than it sounds.
- A join request is a state machine wearing a buttonBuilding communities with membership, join requests, privacy settings and admin announcements — and why the simplest-looking social feature has the most states.
- Cutting over a search index without a maintenance windowReplacing a live search collection with a redesigned one — a quality gate that keeps thin records out, derived fields computed correctly, and a switch nobody had to schedule downtime for.
- Receiving webhooks you don't control the shape ofBuilding an endpoint that consumes cohort events from one vendor and pushes segments into another — multiple payload shapes, idempotency, retries with backoff, and durable sync state.
- Pagination breaks the moment you reorder page onePinning admins and the current user to the top of a member list sounds trivial. It quietly guarantees duplicates and omissions on every page after it.