Contract data engineer · Ponte de Lima, Portugal · EU hours
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.
For CTOs, heads of data and engineering managers whose warehouse runs on dbt, on Redshift or Snowflake.
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 02Staging 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 2025A 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 countriesThe tools: dbt, Redshift, Snowflake, Python, SQL, AWS Lambda, SQS, Step Functions, Terraform
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 dimensionAn 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 modelAn 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 columnsA 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 layer34
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 didI 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 changedBoth 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, SQLFluff573
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 didI 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 changedThe 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, DataDogHow I lay out a warehouse
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.
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 2021A 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.