Back to selected work

Personal open-source project · Jun 2026 – Present

RunOmics

Running Analytics Platform

Privacy-first running analytics platform transforming Garmin Connect and FIT activity data into training, performance and route insights.

RunOmics dashboard showing synthetic training load, weekly progress and recent running activities
Public demo dashboard · Fictional athlete and activity data

01 / Case study

Overview

A complete product built to make complex training history useful without making private activity data public.

End-to-end design and full-stack development across a domain-oriented TypeScript application, Python data pipeline, automated testing, CI/CD and public demo deployment.

The engineering work spans the ingestion pipeline, domain rules, application queries, server-side repositories, interactive product surfaces and a reproducible public deployment. The technology is supporting evidence; the central challenge is keeping those concerns explicit and independently testable.

RunOmics uses TypeScript and Python, but its engineering evidence is backend-oriented: explicit domain rules, repository boundaries, server-side data access, repeatable pipelines and automated delivery.

02 / Case study

Problem & constraints

Running analytics begins as a data-engineering problem: varied sources, long histories, sensitive routes and a public demo that must work without personal credentials.

  1. Historical ingestion

    Import Garmin Connect history and FIT files, then normalise activity, weather and GPS data through a repeatable Python pipeline.

  2. Private by default

    Keep personal activity history and precise routes out of the public repository and outside browser-addressable assets.

  3. Useful without credentials

    Make a clean clone produce a complete reviewable application without Garmin, Firebase, Gemini or map-service credentials.

  4. One application model

    Let personal and public-demo deployments use the same repositories, use cases and presentation layer instead of maintaining a showcase fork.

03 / Case study

Architecture

Domain rules, application use cases, infrastructure repositories and presentation remain separate while personal and demo data follow different preparation paths.

Pure rules live under src/domain. Render-time queries and aggregation live in src/application/usecases, whilesrc/infrastructure/repositories owns server-only JSON and optional service adapters. Next.js routes and components consume those boundaries rather than reaching into the dataset directly.

A Python pipeline performs Garmin/FIT ingestion, normalisation, weather enrichment, route processing and privacy cropping before the TypeScript application reads the resulting model.

Two data paths, one application model

Personal deployment

  1. 01Garmin Connect / FIT
  2. 02Python ingestion & processing
  3. 03Separately secured data source
  4. 04Build-time hydration

Public demo

  1. 01Clean public clone
  2. 02Fixed-seed synthetic generator
  3. 03Fictional training history
  4. 04Zero private credentials
  1. StorageServer-only data/ JSON
  2. InfrastructureJSON repositories
  3. ApplicationUse cases & aggregation
  4. PresentationServer components
  5. BrowserSummaries & interactive UI

Raw JSON is not browser-addressable. Large GPS traces and sample arrays stay server-side except where the activity detail experience requires them.

Original simplified architecture based on the current public RunOmics repository. Both data sources feed the same repository, use-case and presentation layers.

04 / Case study

Data & privacy design

The design reduces accidental exposure by deciding where personal data may exist, when it crosses a boundary and what the browser actually receives.

Real Garmin history is never committed to the public source repository. Personal deployments may hydrate data during build or synchronisation; the public demo generates a fixed fictional season instead.

This is deliberate privacy-conscious architecture for a personal product—not a claim of enterprise-grade security. The value is in making the safe path explicit and reviewable.

Source separation

The public code repository contains no real Garmin history. A personal deployment can hydrate its dataset from a separately secured source during build or synchronisation.

Server-only storage

Runtime JSON lives in repository-root data/, outside public/. Server-only modules read it; the browser cannot request raw /data/*.json files.

Route protection

The Python pipeline can crop trace points around configured privacy zones before an activity is stored, reducing exposure of sensitive start and end locations.

Fictional public data

The demo generator creates deterministic athlete, activity, gear, race and GPS records from a fixed seed instead of sanitising personal history.

05 / Case study

Analytics domain

The product is more than CRUD and charts: deterministic rules aggregate training history into reusable signals before presentation begins.

Training state

CTL, ATL and TSB calculations turn daily training load into fitness, fatigue and form histories that can be projected and tested independently of the UI.

Performance

VO2max and lactate-threshold histories, heart-rate and pace zones, records and race predictions aggregate signals across the activity archive.

Activity analysis

Splits, route shapes, recurring runs, weather, effort and decoupling rules provide deterministic analysis for individual sessions.

Planning

Planned workouts, training phases and athlete-specific zones connect future sessions to the same domain model used for completed work.

06 / Case study

Testing & delivery

Quality is layered around the same boundaries as the architecture, from Python ingestion through browser workflows.

Python pipeline

pytest covers FIT parsing, mapping, privacy cropping, Garmin transforms and JSON persistence.

Domain & application

Vitest exercises pure rules, use cases, repositories and deterministic calculations.

Components & integration

Testing Library checks interactive behavior and integration boundaries in the TypeScript application.

Browser & accessibility

Playwright and axe-core cover key workflows, responsive surfaces and automated accessibility checks.

01Clean checkout
02Generate demo data
03Node & Python CI
04Playwright E2E
05Vercel deployment

GitHub Actions run production-dependency auditing, linting, type checks, Vitest, pytest and Playwright against generated demo data. The fixed input makes CI and the deployed review environment reproducible without private credentials.

07 / Case study

Product tour

Four public synthetic views connect the architecture to the product behavior it enables.

Synthetic RunOmics activity detail showing a threshold run route, summary metrics and heart-rate zones

01 / Product view

Activity detail

Route, conditions, splits and physiological signals are assembled for one session while large trace and sample arrays remain server-side until this view needs them.

Synthetic demo data
Synthetic RunOmics analytics view showing training-load, VO2max, pace and consistency trends

02 / Product view

Training analytics

Fitness, fatigue and form share a deterministic load model with VO2max, threshold, consistency and performance histories.

Synthetic demo data
Synthetic RunOmics heatmap showing fictional running routes and route-distribution statistics

03 / Product view

Route analysis

Processed GPS traces support recurring-route and territory analysis while the public capture uses fictional geometry and locations.

Synthetic demo data
Synthetic RunOmics records view showing fictional personal bests and race predictions

04 / Product view

Records & predictions

Personal-best extraction and race predictions are derived from the activity archive through reusable application queries, not embedded in presentation components.

Synthetic demo data

08 / Case study

Engineering decisions

The architecture stays intentionally proportionate to a personal, read-heavy analytics product while preserving clear change boundaries.

Server-only JSON instead of a database
The application is read-heavy and built around periodic synchronisation. Plain JSON keeps the personal deployment and public demo portable while repositories preserve a clean boundary if storage needs change later.
Synthetic demo instead of sanitised history
A fixed-seed fictional season avoids the residual privacy risk of partially anonymised health and GPS data, and gives every clean clone the same reproducible state.
Optional services instead of prerequisites
Firebase, Gemini and MapTiler enhance selected capabilities, but read-only analytics, rules-based insights and deterministic route previews remain available without them.
Server summaries instead of shipping the dataset
Server components and use cases expose serialisable view data. Large traces and sample arrays cross the boundary only for the activity detail experience that requires them.

09 / Current status

Review the complete system.

The public repository generates a deterministic fictional training history from a clean clone, so the complete application can be reviewed without a Garmin account, database or private credentials.