Selected work

Things I've shipped

Production systems, with the parts that were actually hard. Happy to walk through the architecture of any of these.

A coach marketplace on Stripe Connect

Payments · Full stack · API, React Native and web

Customers browse coaches in-app, pay for a session, and are shown the coach's private booking link — but only once the payment has genuinely settled. Coaches onboard themselves and get paid through destination charges, which split a platform fee automatically and settle the remainder to their own connected account.

The design decision that mattered: a booking is marked paid only from a signature-verified webhook, never from the browser redirect. The redirect tells you the customer came back — it doesn't tell you the money arrived, and it can be forged. Settlement, expiry, async failure, refunds and account updates are all handled as separate events, and every amount is recomputed server-side from the stored offering rather than trusted from the client.

The backend alone came to 63 files and 25 integration specs, with the booking URL and connected-account ID kept off every public serializer.

Stripe ConnectWebhooks Node.jsTypeScript MongoDBReact Native

A React Native app, empty repo to both stores

Mobile · Primary author, 140 of 153 commits

A professional networking app for iOS and Android, owned end to end — every version bump, signing certificate and store submission. Included an in-app video recorder with a scriptable teleprompter and AI-generated feedback on the recording.

The app was flagged by Google Play under the Photo and Video Permissions policy for requesting READ_MEDIA_IMAGES, which carries a removal risk. I migrated it to the Android system photo picker and capped the legacy storage permissions at the last API levels that honour them, so the app stopped asking for anything it couldn't justify — without losing camera capture.

Also handled the Android 15 target migration, the move to vision-camera, New Architecture compatibility, deep linking on both platforms, and per-PR builds that post a Play Internal App Sharing link straight into the pull request.

React NativeKotlinSwift FastlanePlay Console App Store Connect

Semantic search that stays fresh

AI · Vector search · Background workers

Employers needed to find candidates by meaning rather than keywords — matching on skills, experience and behavioural signals. That only works if the embeddings stay current as profiles change, so I built the indexing system behind it.

One authoritative write path for embeddings, so nothing else in the codebase can write them inconsistently. A batch worker for backfill and refresh, plus real-time incremental indexing over BullMQ and Redis, deliberately kept off the request path. Validation, retries, debouncing and dead-letter handling on top — the debouncing specifically because a burst of profile edits shouldn't trigger a burst of paid embedding calls.

Vector searchBullMQRedis OpenAIBackground workers

AI features that ship

LLM engineering · OpenAI and Gemini

An OpenAI ReAct engine that reads a résumé or LinkedIn profile and builds a structured candidate record from it, a streaming in-app AI companion, and a Gemini worker that analyses user video pitches and returns feedback.

A representative bug: deep search silently stopped reranking and returned raw vector order. The configured model rejected the temperature parameter we always sent, so every completion failed with a 400 — and three separate fallback paths each caught it, degraded quietly, and logged at debug level. The system looked healthy end to end while doing none of the work. Fixed the parameter handling and the JSON parsing behind it. The lesson stuck: a fallback should emit a metric, not a debug log.

OpenAIGeminiReAct agents Tool calling

A multi-tenant hiring platform, from scratch

Full stack · Built from an empty repository

An employer-facing product: signup and email verification, organisation onboarding, job authoring with draft/publish and visibility controls, applicant review pipelines, and automated closure after the application deadline. Multi-tenant with role-based access throughout.

CI, Docker and end-to-end tests went in before the first feature — on a greenfield repository that's the cheapest it will ever be, and it means nothing ever ships manually.

I also ran a security pass across the wider API, closing broken object-level authorisation gaps in six subsystems by enforcing ownership at both the route and the service layer — because a check at the route alone is one forgotten decorator away from a breach.

ReactNode.jsMulti-tenant RBACDockerCypress

Open source

automated-ai-code-review — a GitHub Action that runs AI-assisted review on pull requests.

html_to_quill_delta — a Python library converting HTML into Quill Delta rich-text format.

Plus upstream fixes to React Native libraries in production use — react-native-fast-image, react-native-video, react-native-image-picker and rollbar-react-native.