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.
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.
Historical ingestion
Import Garmin Connect history and FIT files, then normalise activity, weather and GPS data through a repeatable Python pipeline.
Private by default
Keep personal activity history and precise routes out of the public repository and outside browser-addressable assets.
Useful without credentials
Make a clean clone produce a complete reviewable application without Garmin, Firebase, Gemini or map-service credentials.
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
- 01Garmin Connect / FIT
- 02Python ingestion & processing
- 03Separately secured data source
- 04Build-time hydration
Public demo
- 01Clean public clone
- 02Fixed-seed synthetic generator
- 03Fictional training history
- 04Zero private credentials
- StorageServer-only data/ JSON
- InfrastructureJSON repositories
- ApplicationUse cases & aggregation
- PresentationServer components
- 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.
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.
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.

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
02 / Product view
Training analytics
Fitness, fatigue and form share a deterministic load model with VO2max, threshold, consistency and performance histories.
Synthetic demo data
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
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 data08 / 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.
