Projects

A deeper look at the systems I've designed and built -- the context behind them, the challenges I faced, and the decisions that shaped each one.

Web Performance / Infrastructure

Snappi RUM Ingestion Pipeline

2025 - Present

Snappi · Early-stage startup spun out of Viasat, providing web acceleration services powered by proprietary CDN technologies and machine learning

Context: Snappi is an early-stage startup spun out of Viasat that provides web acceleration services using proprietary CDN technologies and machine learning. RUM (Real User Monitoring) is how we collect the performance data that feeds the acceleration engine. When I joined, the product was being built from the ground up -- there was no ingestion system, no pipeline, no infrastructure. I designed and built the data platform from scratch as the company scales its customer base.

Challenge: We needed a pipeline designed from day one to scale with the business. Back-of-the-envelope calculations showed the architecture must support up to 100M events/month as the customer base grows, handle traffic spikes of ~390 RUMs/sec (10x average load), and write to multiple destinations in parallel (ClickHouse for real-time analytics, Parquet for the data lake, JSON for ML training) with per-destination error isolation. All while keeping operational overhead low for a small engineering team.

What I built: I proposed and implemented a fully event-driven architecture. RUM data lands in S3, which triggers a notification to SQS -- but the key design insight was keeping payloads in S3 rather than embedding them in the queue message. This sidesteps the 256KB SQS limit while supporting arbitrarily large payloads. Lambda functions consume from SQS with dual-trigger batching (count-based OR time-based, whichever fires first), ensuring both throughput and latency. Each Lambda writes to all three destinations in parallel with independent error handling -- if a Parquet write fails, ClickHouse and JSON writes still succeed.

Scale headroom: With 20 reserved concurrent Lambda executions processing batches of 200 events each, the architecture has a throughput ceiling of ~800 RUMs/sec. Even at projected 10x traffic spikes (~390 RUMs/sec), the pipeline operates at under 50% capacity -- well suited for a growing startup that needs infrastructure to stay ahead of demand rather than chase it. The unit economics work out to ~$0.000008 per RUM, keeping costs proportional to growth.

Outcome: The pipeline auto-scales with traffic and requires zero manual intervention. The entire infrastructure is defined in AWS CDK (TypeScript) with per-environment concurrency tiers (prod=20, preprod=10, dev=5), CloudWatch dashboards, and Slack-integrated alarms.

AWS CDK Lambda SQS S3 ClickHouse Parquet TypeScript

SDK / Browser Instrumentation

Snappi RUM SDK

2025 - Present

Snappi · The browser SDK that collects performance data feeding Snappi's web acceleration engine

Context: Snappi's web acceleration relies on understanding how real users experience customer websites. The SDK is injected into customers' production sites and collects the performance data that feeds the acceleration engine and ML models. It runs on sites with millions of visitors, so every byte and every millisecond of execution time matters -- the SDK can't degrade the very performance it's measuring.

Challenge: The SDK needed to collect a wide range of signals -- Web Vitals (LCP, CLS, INP), Long Animation Frames, resource waterfalls, SPA soft navigations -- while navigating a complex landscape of consent regulations across different CMPs (OneTrust, Osano, Shopify, GPC). The team also needed A/B testing capabilities to safely roll out new SDK features and configurations per domain, and the whole thing had to be lightweight enough that it wouldn't show up in customers' own performance budgets.

What I maintain and extend: A TypeScript SDK with a plugin architecture (12+ plugins). I designed a composable consent framework that adapts to whichever CMP the customer uses, rather than hardcoding consent logic per provider. I built an A/B testing infrastructure using CloudFront Key-Value Store for domain-level traffic routing, enabling the team to safely roll out and test new SDK features per domain without any client-side branching. Dynamic configuration is fetched at init time and cached, keeping the runtime footprint minimal. The SDK is bundled via esbuild with tree-shaking so customers only ship the plugins they use.

TypeScript esbuild Web Vitals LoAF Plugin Architecture CloudFront KV

CLI Tooling / Developer Experience

Snappi CLI & AI Workflows

2025 - Present

Snappi · Internal tooling for the engineering and support teams

Context: As a small team managing 100+ enterprise customers, operational efficiency is critical. Engineers and support staff regularly need to query ClickHouse for customer data, trace events through the pipeline, inspect JRUM payloads, and run WebPageTest audits. Before the CLI, these tasks involved switching between multiple dashboards, writing ad-hoc SQL, and manually piecing together information.

What I built: A unified Go CLI tool using Cobra that consolidates all operational workflows into one interface. It supports ClickHouse queries with templated customer lookups, pipeline event tracing from ingestion to storage, JRUM payload inspection, and WebPageTest integration for on-demand audits. Output formats are configurable (JSON, table, YAML) depending on whether you need human-readable results or machine-parseable data for scripts.

I also created a set of Claude Code skills for the engineering team -- structured prompts and workflows that streamline debugging, deployment, data exploration, and testing into AI-assisted processes. This was one of the first things I introduced at Snappi and it changed how the team approaches day-to-day engineering tasks.

Go Cobra AWS SDK ClickHouse Athena Claude Code

Fintech / Banking

Birmingham Bank Digital Platforms

2023 - 2025

Birmingham Bank · UK challenger bank going through digital transformation, transitioning from phone/branch-only to online banking

Context: Birmingham Bank was a traditional UK bank that had never had a customer-facing digital product. All interactions happened through branches or phone calls. The bank decided to launch its first online savings platform to compete with digital-first challengers, and I was brought on as the main developer to build it from scratch.

Phase 1 -- Online Savings Platform: I built the entire customer-facing application using Next.js 14 App Router. The biggest technical challenge was security in a banking context: I designed a server-side authentication layer with AES-256-GCM cookie encryption and a cookie-slicing mechanism to handle payloads that exceeded browser cookie size limits. I built a generic HTTP client with retry logic and circuit breaker patterns for communicating with the bank's backend APIs, integrated Application Insights for production telemetry, and implemented the full customer signup flow -- mobile OTP verification, nominated account linking, product selection, and deposit workflows. The platform attracted over GBP 30 million in new deposits within its first 6 months.

Phase 2 -- Broker Portal (Promotion to Senior): After the savings platform's success, I was promoted to Senior and moved to lead the broker portal. This was the bank's strategic priority -- lending was their primary revenue driver, and they needed mortgage brokers to submit applications digitally. I worked directly with the Chief Commercial Officer and Chief Product Officer under tight delivery timelines. The portal required Azure MSAL enterprise authentication (brokers log in with their firm's Azure AD), a complex multi-step Decision in Principle (DIP) form system supporting 4 borrower types (Individual, Partnership, SPV, LLP) each with different validation rules (Zod schemas), and document upload capabilities for mortgage applications.

Hardest problem: The file upload proxy was the most technically demanding piece. Brokers needed to upload 100MB+ documents that had to be streamed to SharePoint via Microsoft Graph API. I built a memory-safe streaming proxy in Node.js that used chunked upload sessions -- the file never fully loads into server memory, it streams through in chunks. I load-tested it extensively to verify zero memory leaks under sustained load, and delivered the entire thing in one sprint as a solo effort.

Next.js 14 Azure MSAL AES-256-GCM Zustand Zod SharePoint Graph API Application Insights

E-Commerce / Serverless

Dream Car Brasil

2025 - Present

Seaworthy Software client · Brazilian raffle e-commerce platform where participants buy ticket numbers for a chance to win cars

Context: Dream Car Brasil is a raffle platform popular in Brazil where participants purchase numbered tickets for a chance to win vehicles. The client needed a full-stack platform that could handle high traffic during raffle launches (thousands of concurrent users racing to pick their lucky numbers), process PIX payments reliably, and keep infrastructure costs minimal since margins on individual ticket sales are thin.

Architecture decisions: I chose DynamoDB with a single-table design because the platform has many distinct access patterns (user lookups, raffle listings, ticket availability, order history, leaderboards, payment status) that would require expensive joins in a relational database. The single table with 7 GSIs maps each access pattern to a single, fast query. I modeled all entities -- users, raffles, tickets, orders, payments -- in one table using composite keys with entity-type prefixes. Each GSI projects only the attributes it needs, keeping read costs low.

Hardest problems: Ticket number reservation needed to be atomic -- when two users try to claim the same number simultaneously, exactly one must succeed. I used DynamoDB TransactWriteItems to atomically update both the ticket record and the raffle's available count in a single transaction. Payment reliability was another challenge: Brazilian PIX providers have inconsistent uptime, so I implemented a multi-provider strategy with automatic fallback chains (Asaas as primary, Wisepix as fallback). Each payment attempt is recorded separately in DynamoDB, creating a full audit trail. Webhook handlers for each provider update order status and trigger downstream Lambda functions.

Event-driven workflows: Eight Lambda functions handle asynchronous processing: checkout expiration timers (EventBridge Scheduler creates a per-checkout reminder), image processing for raffle banners, leaderboard recomputation, and WhatsApp messages via Wati for purchase confirmations and raffle results. Everything is triggered by DynamoDB Streams or EventBridge events, keeping the system loosely coupled.

Outcome: The platform serves 20,000+ participants with total AWS infrastructure cost under $30/month -- possible because of on-demand DynamoDB pricing, Lambda's per-invocation model, and S3 for media storage. The TypeScript monorepo (Turborepo) with shared packages keeps the codebase maintainable as a solo developer.

Next.js 15 AWS CDK DynamoDB Lambda EventBridge Wati Turborepo Resend

Performance / Streaming / Browser Extension

Astro Advanced Analytics

2025 - 2026

Seaworthy Software client · Product analytics startup helping e-commerce companies track pricing and assortment data

Context: Astro Advanced Analytics provides competitive pricing intelligence for Amazon sellers. Their platform processes massive product datasets -- tens of thousands of ASINs/UPCs with daily price snapshots -- and presents them in dashboards that analysts use to make pricing decisions. When I joined, the existing frontend was hitting severe performance walls: the API returned huge JSON payloads that would freeze the browser during parsing, and the data tables couldn't handle the volume of rows analysts needed to see.

Streaming strategy: The platform had two core performance problems: the API returned entire datasets as single JSON responses, and a bloated React app with per-cell popovers that triggered massive re-rendering cascades on every UI update, causing constant long tasks and poor INP scores. I tackled both sides -- redesigning the data flow to use NDJSON (newline-delimited JSON) streaming so rows appear as soon as the first results arrive, and fixing the frontend with React performance hooks (useTransition to defer updates in favour of user input, useDeferredValue for optimistic UI) plus a singleton pattern for the cell popover component reused across the entire table. The combined effect drastically cut perceived latency from the backend and eliminated the long-task jank that had been degrading the user experience.

New SPA: I built a new single-page application from scratch using Vite + React 19 + MUI, replacing the legacy frontend. The key feature was virtual scrolling (TanStack Virtual) for tables with 10K+ rows -- only the visible rows are rendered in the DOM, keeping memory usage constant regardless of dataset size. I also set up the Azure deployment pipelines for staging and production with push-button deployments, and introduced Playwright e2e tests and unit tests running in GitHub Actions CI.

Chrome extension (built in one week): Astro also needed a browser extension so analysts could capture product data directly from e-commerce sites while browsing. I built the entire extension solo in one week -- from concept to Chrome Web Store publication. The architecture uses a site adapter pattern: each supported e-commerce site gets an adapter that implements a standard interface (getProductTitle, getPrice, getCategory, etc.), so adding new sites means writing a single file with no changes to core logic. The extension uses Shadow DOM to isolate its UI from host page styles, typed messaging between content scripts and the service worker, and Google Sheets integration for direct data export.

React 19 Vite WXT Chrome MV3 Shadow DOM Azure MUI NDJSON