Skip to content
Portfolio

Choices I'd defend, and what they cost.

Architecture is mostly the things you decided not to do. These are calls made across the work — each one with what it bought and what it gave up, because a decision with no downside was never really a decision.

  1. 01

    Autonomous Car UI

    Build the vehicle interface in a browser, not natively

    An autonomous vehicle HMI has to render live LiDAR, camera and velocity data coming off a ROS2 and Autoware stack.

    The tradeoff

    Native is the obvious answer: direct DDS access, real threads, no garbage collector — precisely what high-rate sensor data wants. The browser offers none of that. What it offers instead is distribution. Engineers, testers and stakeholders all need to look at the same thing, and a URL reaches all of them with no install and no build per machine. Taking the browser means the rendering path stops being something you can assume and becomes something you have to engineer — typed arrays, a render loop decoupled from message arrival, and a hard budget per frame.

  2. 02

    LLM Scenario Generator

    Store generated scenarios in a graph, not a relational schema

    LLM-generated driving scenarios, kept so that coverage can be reasoned about rather than just counted.

    The tradeoff

    PostgreSQL would hold these rows perfectly well and is the choice nobody questions. But the useful questions about a scenario library are all traversals — what shares this actor, what sits one variation away from this case, where is the nearest gap in coverage. In a relational schema those become recursive joins that grow expensive exactly as the dataset grows interesting enough to ask them. A graph store costs a second database to operate and a narrower pool of people who know it; it buys the questions being cheap instead of the storage being familiar.

  3. 03

    AI Vision + LLM Pipeline

    Detector first, language model second — not one end-to-end model

    Reading standardised container codes off photographs of rusted, repainted, badly-lit steel.

    The tradeoff

    A single multimodal model is fewer moving parts and far less glue code. It also fails as one opaque unit, and its failure mode is the dangerous one: a fluent, correctly-formatted, wrong container code that is syntactically valid and flows downstream unchallenged. Splitting the pipeline means YOLO localises and the language stage only ever interprets a tight crop against a schema. That is more to deploy and monitor, in exchange for stages that can be measured separately and a failure that surfaces as a rejected parse rather than silent corruption.

  4. 04

    AMR Fleet Management

    Build on OpenRMF rather than a bespoke fleet coordinator

    Coordinating autonomous mobile robots from more than one vendor across a multi-floor site.

    The tradeoff

    A purpose-built coordinator is simpler on day one and fits the fleet in front of you exactly. It also means owning traffic deconfliction, task allocation and lift arbitration permanently, and rewriting a meaningful part of it the first time the site buys a robot from someone else. Adopting OpenRMF means inheriting its abstractions and its learning curve, and being constrained by decisions someone else made. What it buys is a vendor-neutral seam that already models the hard part of the problem.

  5. 05

    Cross-Platform Password Manager

    One API contract across three clients

    A credential vault running on iOS, Android and the web.

    The tradeoff

    Per-platform backends let each client be idiomatic and evolve at its own pace. For a vault they also create three places where cryptographic handling can quietly diverge — and the weakest of the three defines the security of the whole product. A single Node API is a bottleneck and a single point of failure, accepted deliberately: for something whose entire value is trust, one auditable path is worth more than three convenient ones.

  6. 06

    Recurring — vision pipelines, RAG systems, scenario generation

    FastAPI as the default for AI services

    The same backend question, answered the same way across several AI systems.

    The tradeoff

    Django and Spring Boot both give you more out of the box, and both are already on my CV. But AI services are overwhelmingly I/O-bound — model calls, vector queries, third-party APIs — and async-native with typed request and response models is simply the shape of the problem. The cost is assembly: auth, admin, migrations and the rest are not handed to you, and on a system that grows past its API surface that debt comes due.