Skip to content

Work

Platforms built end to end, from the phone in an athlete's hand to the service behind it.

My current work centres on sport: coaching tools, competition software and the national records list for Archery Canada.

Live

Skadi’s Edge

Role
Product design, architecture and full-stack implementation
Timeline
2025 — present
Find it at
skadisedge.com
Get the app
Google Play

Skadi’s Edge gives archery coaches and the athletes they work with a shared picture of training. Athletes log sessions from their phone and get performance feedback on their shooting; coaches see the same data rolled up across their squad in a web dashboard.

Billing was the part that needed the most care. The coach platform subscribes through Stripe, while the in-app tier has to go through Play Billing and StoreKit, so the same entitlement arrives by three different routes. Stripe delivers webhooks at least once rather than exactly once, which means every handler for activation, renewal, cancellation and failed payment has to be idempotent — a replayed event must not double-charge anyone or resurrect a cancelled subscription.

It is in real use: four teams, and 75 archers with the app installed — 36 on Android, 39 on the iOS TestFlight build. Android is live on Google Play, published there in partnership with Stormpeak Performance, the coaching practice the product is built alongside; iOS is in TestFlight beta, and I am happy to add testers on request.

What was built

  • Flutter client athletes use to record sessions at the shooting line and review their feedback
  • Go REST API over Firestore, with Firebase Auth and one typed data model shared across mobile and web
  • Svelte dashboards giving coaches squad-wide visibility
  • Cloud sync keeping an athlete’s training data consistent across their devices
  • GitHub Actions builds and runs the test suite on every push and produces the release images; production deploy is still a manual cutover script
  • Each service in its own Docker container behind an NGINX reverse proxy, on a Linux VPS with no public ports but the proxy, and SSH restricted to key-only auth with passwords disabled
  • Flutter
  • Dart
  • Go
  • Firestore
  • Firebase
  • Svelte
  • Stripe
  • Linux / VPS
  • Docker
  • NGINX
Soft launch

Skadi Events

Role
Product design, architecture and full-stack implementation
Timeline
2026 — present

Skadi Events extends the Skadi platform from the practice field to the competition line. Where Skadi’s Edge handles day-to-day training, Events covers running a shoot: registration, scoring and results for organisers and participants.

Identity is the only thing the two products share, by design. Athletes and clubs carry one account across both through Firebase Auth, while Events keeps its own PostgreSQL database — competition data is relational in a way training logs are not, and a shared store would have coupled two products that change at different rates.

Money moves between three parties rather than two: entry fees are taken through Stripe Connect so that organisers are paid out for their own events, and webhooks reconcile each registration against its payment state. Because a failed reconciliation means someone shows up to a shoot without an entry, the payment path is exercised before it ships — against a simulated Stripe locally, and Stripe test mode on staging.

It is online and in soft launch — the platform is up and working, ahead of a wider push to organisers.

What was built

  • Registration and entry management for organisers
  • Scoring and live results on a PostgreSQL schema built for entries, rounds and standings
  • Flutter client and Svelte web front end against the same Go API
  • Full GitHub Actions CI/CD — staging and production images built on push and pulled onto the host by Watchtower
  • Flutter
  • Go
  • PostgreSQL
  • Svelte
  • Stripe Connect
  • Linux / VPS
  • Docker
  • NGINX
Live

Archery Canada Records List

Role
Design and implementation
Timeline
2026
Find it at
archerycanada.ca

Archery Canada keeps the national records in four Google Sheets — indoor and outdoor, men’s and women’s. The old page linked out to those sheets through a document viewer, so looking up a record meant knowing which of the four to open and then reading a spreadsheet.

The constraint that shaped the build was who maintains the data. The records are kept in sheets by people who are not going to switch tools, so the sheets stay the system of record. A WordPress plugin syncs them on a schedule, caches each sheet on its own so one failed fetch can’t take out the rest, and serves the merged set from a REST route the front end reads once. Staff carried on editing the same spreadsheets; nothing about their workflow changed.

The interesting part was that the four-way split isn’t how anyone actually looks for a record. Para and field events cut across all four sheets, and the categories are free text — “60+ Compound unlimited”. So each row is parsed into discipline, age class, bow type, gender and classification, and the browser re-groups all thousand-plus records into Indoor, Outdoor, 3D/Field and Para with filters underneath. Everything loads once and filters in the browser, which is what makes it feel instant.

It is bilingual because a national sport organisation publishes in both official languages, and that goes past the buttons: the record taxonomy and every round name are translated too. Search is indexed in English and French at the same time, so “compound” and “poulies” both find the same record whichever language the page is in.

What was built

  • Available as a Gutenberg block, a shortcode and a page template, so it drops into a page however that page happens to be built
  • Sheet ingestion with three transports — published CSV, keyless gviz, or the Sheets API with a key — so it runs without Google credentials if need be
  • Header-driven column mapping: sheets can gain, lose or reorder columns without breaking the import
  • Mounted in a shadow root with its styles inlined, so the widget and the site theme cannot reach into each other
  • Public read-only REST endpoint over the merged, deduplicated record set
  • GitHub Actions packages the installable plugin zip on every tag, and refuses to release if the tag and the plugin version disagree
  • React
  • JavaScript
  • Vite
  • Tailwind CSS
  • WordPress
  • PHP
  • Google Sheets API

Before Nanzero

Twelve years across startups and enterprise teams

phenoML

Senior Software Developer (Full Stack)

Aug 2023 — Apr 2024

  • Built Go backend services for data collection from client applications.
  • Set up Cloud Run and BigQuery to support data processing goals.
  • Contributed to cross-platform Flutter clients, including multiple mini-games.
  • Worked on systems supporting and monitoring mental health indicators.

Avenza Systems

Senior Software Developer / iOS Team Lead

Sep 2019 — Jul 2023

  • Technical lead on Avenza Maps, the company’s flagship iOS app — millions of downloads on the App Store.
  • Led a team of four iOS developers, ran code review and defined the coding standards.
  • Migrated the codebase from Objective-C to Swift, with selective SwiftUI adoption alongside the existing UIKit app.
  • Spearheaded the initiative that raised test coverage from 6% to 35%, cutting regressions release over release.
  • Worked in C++ core code with industry-standard GIS and geospatial PDF libraries; cut offline map processing time by up to 70% through targeted optimisation.

CloudStreet Gaming

Senior Software Developer (Backend)

Jan 2019 — Jul 2019

  • Built backend services in C# with Python tooling.
  • Implemented auditable, idempotent game logic over a relational database for a networked poker platform, where every hand had to be replayable and reconcilable.
  • Worked within casino gaming regulatory requirements.

FusePowered / Upsight

SDK Developer

May 2014 — Dec 2018

  • Developed mobile analytics and advertising SDKs for iOS, Android and Unity.
  • Worked on SDK internals and public APIs in Objective-C, Java and C#.
  • Integrated multiple third-party ad network SDKs.

Blammo Games

Game Programmer (Mobile)

Aug 2012 — May 2014

  • Developed mobile games on a C++-based engine.
  • Supported iOS and Android platforms.

nanzero.

Nanzero Interactive — software for sport and the web. Independent software practice based in Mississauga, Ontario, Canada.

Elsewhere

© 2026 Nanzero Interactive

Mississauga, Ontario, Canada