Back to the lesson·Lesson 4 of 8·The same answer twice
Four things that change the answer when nothing changed
The same deck as the downloads, rendered as a page. Start the slideshow to present it full screen — arrow keys or a click advance one slide, Escape leaves.
What this lesson covers
- One: the clock
- Two: the seed
- Three: the order files come back in
- Four: the locale
- The test that catches all four
- The deterministic-output habit
- Report it whole
- What comes next
Speaker notes
The clock, the seed, the file order and the locale. Each produces a different result from identical code and identical data, each is invisible in a diff, and this platform hit three of the four in production.One: the clock — In Python
from datetime import date report_date = date.today() # different every day, by designOne: the clock
- Anything that reads the current time makes every run differ — A generated-on footer, a filename with today's date, a…
- The fix is to take the date from the data or from a parameter
Speaker notes
Anything that reads the current time makes every run differ. A generated-on footer, a filename with today's date, a filter for "the last 30 days", a random seed derived from the time. The fix is to take the date from the data or from a parameter.One: the clock — In Python
import pandas as pd survey = pd.read_csv("data/raw/household-survey-2025.v1.csv") as_of = pd.to_datetime(survey["interview_date"]).max() # from the dataOne: the clock
- This platform got this wrong and the failure is instructive — The course handouts and lesson decks originally took…
- The date is now scoped to the artefact — A handout's date comes from its own course and lessons, a deck's from its own…
Speaker notes
This platform got this wrong and the failure is instructive. The course handouts and lesson decks originally took their generation date from the newestupdatedfield across the entire content library. Deterministic — and still wrong, because publishing one new course restamped every existing handout and rewrote all ninety-six deck binaries. The date is now scoped to the artefact. A handout's date comes from its own course and lessons, a deck's from its own lesson, a report's from its own project. A deterministic wrong answer is still a wrong answer, and "the diff is enormous but correct" is how nobody reviews it.Two: the seed — In Python
import numpy as np rng = np.random.default_rng(20260729) # stated, committed, reproducible sample = rng.choice(households, size=200, replace=False)Two: the seed
- Anything random needs a seed and the seed belongs in the code — Bootstrap intervals, random sampling for verification,…
- Set it once, at the top, visibly — A seed buried three functions deep is a seed someone will move
- And say so in the output — The regression course's bootstrap interval is reported as "400 resamples, seed 20260729" for…
- Every dataset on this platform is generated by a seeded script — which is what makes
pnpm datasets:generatea…
Speaker notes
Anything random needs a seed and the seed belongs in the code. Bootstrap intervals, random sampling for verification, simulation, train/test splits, jittered scatter points. Set it once, at the top, visibly. A seed buried three functions deep is a seed someone will move. And say so in the output. The regression course's bootstrap interval is reported as "400 resamples, seed 20260729" for exactly this reason — an interval nobody can reproduce is not an interval. Every dataset on this platform is generated by a seeded script, which is what makespnpm datasets:generatea verification step: it rewrites all twenty CSVs andgit statusreports nothing changed.Three: the order files come back in — In Python
import glob for path in glob.glob("data/raw/*.csv"): # order is filesystem-dependent ...Three: the order files come back in
globandos.listdirreturn files in an order that differs between operating systems and sometimes between runs — If…
Speaker notes
globandos.listdirreturn files in an order that differs between operating systems and sometimes between runs. If anything downstream depends on order — a concatenation, a first-wins deduplication, a row index — the result differs.Three: the order files come back in — In Python
for path in sorted(glob.glob("data/raw/*.csv")): ...Three: the order files come back in — In R
for (path in sort(list.files("data/raw", full.names = TRUE))) { }Three: the order files come back in
- Sort it. Always. It costs six characters
- The same applies to dictionary and group order —
groupbyin pandas sorts by default anddplyr::group_bydoes not; a…
Speaker notes
Sort it. Always. It costs six characters. The same applies to dictionary and group order.groupbyin pandas sorts by default anddplyr::group_bydoes not; a chart whose bar order came from an unsorted group is a chart whose order can change without the data changing.Four: the locale — In Python
float("1,234") # ValueError, or 1.234, depending on where you areFour: the locale
- The decimal separator, the thousands separator, the date format and the sort order of accented characters are all…
Speaker notes
The decimal separator, the thousands separator, the date format and the sort order of accented characters are all locale-dependent, and this platform's audience works across both conventions by definition.Four: the locale — In Python
survey = pd.read_csv(path, decimal=".", thousands=None) # stated, not inferred dates = pd.to_datetime(survey["date"], format="%Y-%m-%d") # explicitFour: the locale — In R
readr::read_csv(path, locale = locale(decimal_mark = ".", date_format = "%Y-%m-%d"))Four: the locale
- State the format rather than letting the reader infer it —
pd.to_datetimewithout a format is a function that… - Sorting is the subtler one —
sorted()on French commune names ordersÉtroitafterZoneunder one locale and…
Speaker notes
State the format rather than letting the reader infer it.pd.to_datetimewithout a format is a function that guesses, and it guesses differently on03/04/2025depending on what else is in the column. Sorting is the subtler one.sorted()on French commune names ordersÉtroitafterZoneunder one locale and beforeFondunder another, so a chart's category order becomes machine-dependent.- State the format rather than letting the reader infer it —
The test that catches all four — Shell
python run.py && cp -r outputs outputs-first python run.py && diff -r outputs outputs-firstThe test that catches all four
- Run it twice and diff — Anything that differs is one of the four, and the diff names the file
- Run it twice on different machines — and you additionally catch the locale and the file ordering, which a single…
- Put it in CI — which is a clean machine every time, and the check runs whether or not anyone remembers
Speaker notes
Run it twice and diff. Anything that differs is one of the four, and the diff names the file. Run it twice on different machines and you additionally catch the locale and the file ordering, which a single machine cannot. Put it in CI, which is a clean machine every time, and the check runs whether or not anyone remembers.The deterministic-output habit
Source of churn Fix Timestamps in file metadata SOURCE_DATE_EPOCH, or strip themA generation date in a footer Take it from the content Compression with a timestamp A zip whose entry times are pinned Floating-point summation order Sort before reducing where it matters An embedded random ID Derive it from a hash of the content Speaker notes
For anything a build produces, byte-identical output on unchanged input is the goal, and it is achievable more often than people assume.The deterministic-output habit
- This platform pins
SOURCE_DATE_EPOCHfor both pdfTeX and pandoc — because a.pptxis a zip whose entry times and… - The payoff is that a diff means something — When rebuilding produces no change, a change in the diff is a real change —…
Speaker notes
This platform pinsSOURCE_DATE_EPOCHfor both pdfTeX and pandoc, because a.pptxis a zip whose entry times and document properties would otherwise churn every deck on every rebuild. The payoff is that a diff means something. When rebuilding produces no change, a change in the diff is a real change — and reviewing becomes possible.- This platform pins
Report it whole — Example
Determinism All randomness is seeded: seed 20260729, set in src/config.py and reported with every bootstrap interval in this report. Dates are taken from the data (the latest interview date) rather than from the clock. No output contains a generation timestamp. File iteration is sorted. Group order is stated explicitly rather than inherited from the grouping library's default. CSV reading states the decimal mark and the date format rather than inferring them. Verified: running the pipeline twice produces byte-identical outputs, and CI runs it on a clean machine on every push.Report it whole
- The last line is the claim and the four above it are how it was achieved — A report that asserts determinism without…
Speaker notes
The last line is the claim and the four above it are how it was achieved. A report that asserts determinism without saying which of the four it handled has probably handled the seed and none of the others.What comes next
- A reproducible pipeline that produces one report is worth having.
Speaker notes
A reproducible pipeline that produces one report is worth having. The next lesson makes it produce twelve, from one template, without a copy anywhere.