James Phillip Sanders

Contract data engineer · Ponte de Lima, Portugal · EU hours

The pipeline ran green. The numbers are still wrong.

When a dashboard and the system it reads from disagree, I find the model and the rows where they part, fix the cause there, and add the test that would have caught it. Most recently 34 dbt models across two markets. Before that, a livestock genomics data system.

Tell me what is broken

  • Available now
  • Remote, EU hours
  • Python · dbt · Redshift · AWS
01 / What you can hire me for

Three kinds of work, each one I have done before.

For CTOs, heads of data and engineering managers whose warehouse runs on dbt, on Redshift or Snowflake.

Find out why the numbers are wrong

A total moved and nobody knows why, or a report disagrees with the system it reads from. I trace it to the model and the rows, fix the cause, and add the test that fails the build next time.

Four production defects of this kind found and fixed, in section 02

Take over or extend a dbt project

Staging and dimensional models, tests that fail the build rather than decorate it, linting that holds across a team, and documentation in the same pull request as the model.

34 dbt models built across two markets, Aug to Dec 2025

Add a market or a source without a fork

A new country or system comes in through the same staging layer and lands in the same core models, so there is still one place to fix any number.

Brought a second market into the core models of a retail warehouse in two countries

The tools: dbt, Redshift, Snowflake, Python, SQL, AWS Lambda, SQS, Step Functions, Terraform

02 / What I get called for

Four failures I have actually found and fixed.

Revenue comes in short, and every test passes

The build is green. A surrogate key arrives empty on a slice of rows, the join silently drops them, and the revenue number is quietly low.

I trace it back to the staging model, fix the key, and add the test that would have caught it.

Fixed null surrogate keys on a transactions fact table and on its shop dimension

A rerun doubles the numbers instead of repairing them

An incremental model without a reliable unique key does not heal when you run it again. It doubles. The table looks fine until someone sums it.

I fix the key strategy so a rerun is safe, which is the property you wanted from incremental in the first place.

Fixed duplicate rows in a product attributes staging model

Customer names arrive garbled in a report

An encoding mismatch between the source and the warehouse turns an apostrophe into three characters of noise. Nothing errors. It reaches a customer facing report weeks later.

I find the boundary where the encoding breaks and fix it there, not in the report.

Fixed mojibake in string columns

Old numbers sit next to live ones

A source system gets switched off. Nobody removes its models, so they keep building from a table that stopped updating, and stale rows sit beside live ones with nothing to tell them apart.

I cut the dead lineage out rather than leave it to fail quietly.

Removed a discontinued source from the staging layer
03 / Evidence

Two engagements: the problem, what I did, what changed.

34

dbt models, two markets

Problem

A retail business operating in two countries needed one analytics warehouse. The countries named categories differently, and the second market had to come in without forking the project.

What I did

I built the staging layer, covering products, categories, shops, terminals, transactions and cart items, then the dimensional models on top of it. I brought the second market into the core models, used fuzzy matching to reconcile category ids across the two countries, and added a Step Function trigger for the shared product catalog.

What changed

Both markets run on the same core models, so a fix lands once. The models ship with tests that fail the build.

83 commits, Aug 2025 to Dec 2025 · 24 staging, 3 dimension, 1 fact, plus core and mapping models · dbt, Redshift, Step Functions, Python, SQLFluff

573

commits on a genotype data system

Problem

A livestock genomics data platform was replacing a legacy ingestion process. Illumina IDAT files had to be detected, linked to metadata, checked for authorisation, loaded as genotypes, then checked for discordance against other samples from the same animal.

What I did

I built the sample QA framework, including the manual override path and the checks that run before a flag is allowed to change. I added the discordance trigger, wrapped the job in a transaction, and chained parentage verification off it.

What changed

The discordance job runs from a trigger inside one transaction, so a failure does not leave half a result, and parentage verification follows from it. Every manual override leaves an audit record of who made it and why.

573 commits across 20 repositories, Apr 2023 to Nov 2024 · 31% of the task dispatcher, 29% of the discordance analyser, 14% of core · Python, AWS Lambda, SQS, Snowflake, Terraform, SQLAlchemy, pytest, DataDog

How I lay out a warehouse

source A ─┐ source B ─┼─► staging ─────► core ──┬──► dimensions source C ─┘ one model per │ └──► facts source. renamed, │ typed, tested conformed once, not per report
Illustrative. The rule is that nothing downstream reads a raw table, so there is exactly one place to fix any given defect
04 / What I do not do

The honest list, so you find out now rather than in week three.

05 / What you keep when I leave

The handover is part of the work, not a favour at the end.

Every model I write ships with its tests and its documentation in the same pull request. You get a runbook for anything that needs an operator, the reasoning behind the schema choices written down rather than carried in my head, and a session with whoever is picking it up.

I do the work myself. There is no bench behind me and nobody more junior arrives in week two.

06 / Rate and terms

EUR 400 per day.

07 / Before software

I spent seventeen years in professional kitchens.

Apprentice to head chef. Ski field hotels in New Zealand, fine dining on the Gold Coast, Soho House in Mayfair, and seven years at a private members club in Melbourne. I retrained in my thirties, earning a Diploma and then a Computer Science degree with a Data Science major while still working full time on the line.

It is on this page because it is the part of my background that actually shows up in the work. A kitchen teaches you that preparation is most of the job, that a service runs to a clock somebody else set, that the handover to the next shift is not optional, and that nothing broken leaves the pass. Those habits transfer to a production pipeline more directly than most of my degree did.

Soho House, Little House Mayfair, Aug 2012 to Jun 2013 · The Melbourne Club, Aug 2015 to Dec 2022 · BCompSci, Data Science major, Swinburne, 2022, GPA 3.333, 83.3% High Distinction · Two analyses published by KNIME, Nov 2021
08 / Next step

Tell me what is broken.

A short description of the stack and the symptom is enough to start. I will tell you honestly whether it is something I have done before.

hello@jamesphillipsanders.com