SYS/LOG · 2026-08-205 MIN LEESTIJD

Herkomst is het product

Twee dashboards, één metriek, twee getallen. Uitzoeken welke klopte kostte langer dan het bouwen van allebei, en het antwoord was dat geen van beide te controleren viel.

Twee dashboards. Dezelfde metriek. De één zei 4.812. De ander 5.067.

De vraag die ik kreeg was "welke klopt". Ik wil laten zien hoe lang het duurde om dat uit te zoeken, want het antwoord bleek geen getal te zijn.

Eerst: zijn de queries hetzelfde?

De voor de hand liggende verklaring is dat iemand het filter anders heeft geschreven. Het ene dashboard telt iets mee dat het andere uitsluit, en dat vind je door de SQL naast elkaar te leggen.

Dus legde ik de SQL naast elkaar. Ze waren niet identiek, maar wel gelijkwaardig: de één gebruikte een LEFT JOIN met een WHERE ... IS NOT NULL, de ander een INNER JOIN. Dezelfde resultaatverzameling. Ik draaide ze allebei op hetzelfde moment tegen dezelfde tabel en kreeg er hetzelfde getal uit.

Feitelijk dezelfde query dus. Verschillende antwoorden op de dashboards. Het lag niet aan de query.

Daarna: lezen ze dezelfde tabel?

Ja. Zelfde schema, zelfde tabel, zelfde database. Ik heb de connectiestrings nagekeken.

Daarmee moest de vraag veranderen, want ik had beide helften van "een dashboard is een query tegen een tabel" uitgesloten. Als de query hetzelfde is en de tabel hetzelfde is, blijft alleen wanneer over.

Dus: wanneer heeft elk ervan gelezen?

Hier werd het interessant. Het ene dashboard werd om 06:00 door een job gematerialiseerd. Het andere ververste bij het openen.

Dat alleen verklaart geen gat van 255 records, tenzij de bron nog beweegt, en de bron was een register van een derde partij dat, zo was ons verteld, één keer per dag om 05:00 publiceerde.

Ik vroeg om de logs van de collector. De collector draaide om 06:00, haalde een bestand op, laadde het, en overschreef de tabel. Eén regel per run: tijdstempel, aantal records, exitcode. Niets over het bestand.

Wat zat er eigenlijk in dat bestand?

Niets in de pipeline had het bewaard. De collector downloadde, parste, werkte de tabel bij en gooide het bestand weg. Het enige spoor dat een bepaald bestand ooit had bestaan was een aantal in een logregel.

Dus liet ik de collector het ruwe bestand naar object storage schrijven vóór het parsen, met het ophaaltijdstip in de sleutel, en wachtte twee dagen.

Het register publiceerde niet één keer per dag om 05:00. Het publiceerde om 05:00 een eerste versie en vulde de ochtend door correcties aan, met de laatste wijzigingen rond 13:30. De snapshot van 06:00 was echt, compleet voor 05:00, en halverwege de ochtend al achterhaald. Het dashboard dat bij openen ververste las een tabel die inmiddels door de run van de volgende dag om 06:00 was overschreven, dus om 14:00 lazen de twee dashboards op een willekeurige dag data die negen uur uit elkaar was vastgelegd, uit een bron die beweegt.

Allebei de getallen klopten. Het waren antwoorden op verschillende vragen, en geen van beide dashboards vermeldde welke vraag het beantwoordde.

Wat er werkelijk stuk was

De fout zat in geen van beide dashboards. De fout was dat de pipeline precies die informatie weggooide waarmee de discussie in vijf minuten beslecht had kunnen worden in plaats van in twee dagen: wat er is opgehaald, en wanneer.

UPDATE is hier de vijand. Elke overschrijving vernietigt het vermogen om te beantwoorden wat we gisteren geloofden, en waarom. Dat vermogen voelt als overhead, precies tot het moment dat iemand een getal betwist, en dan is het het enige dat telt.

Wat we hebben veranderd

Drie dingen, geen ervan slim.

Het ruwe record blijft. Elke ophaalactie schrijft het onbewerkte antwoord naar object storage, met bron en ophaaltijd als sleutel. Opslag is goedkoop. Een gezipt dagelijks registerbestand is minder dan een megabyte, dus een jaar aan dagelijkse snapshots kost minder dan de vergadering die we over het verschil hadden.

Vastlegtijd is een kolom, geen logregel. Elke afgeleide rij draagt het ophaaltijdstip van het record waar hij uit komt. Niet de eigen updated_at van de rij, want die vertelt je wanneer jouw pipeline hem heeft geschreven. Het moment waarop de bron is waargenomen.

Bewerkingen zijn stappen, geen overschrijving. Ruw, genormaliseerd, afgeleid. Elke laag leest die eronder en schrijft zijn eigen tabel. Een verkeerde aanname in de normalisatie is nu opnieuw af te spelen vanuit de ruwe laag, in plaats van dat je opnieuw moet ophalen bij een bron die inmiddels verder is.

Elk cijfer op beide dashboards draagt nu de vastlegtijd waaruit het berekend is, in de hoek, in mono, een beetje lelijk. Het heeft sindsdien vier discussies beslecht.

Waar de modellen blijven

Dit komt inmiddels bij elke data-opdracht langs, dus om het maar gewoon te zeggen: taalmodellen zijn nuttig in dit soort systemen, en ze horen niet in de bewijsketen.

Triage, samenvatten, voorstellen welke van drie records waarschijnlijk dezelfde entiteit beschrijven, een eerste opzet voor een taxonomie. Allemaal prima, allemaal dingen die echte uren besparen.

Maar een model kun je niet ondervragen. Je kunt er over een half jaar niet aan vragen welke invoer welke uitvoer opleverde en een antwoord krijgen dat je kunt verdedigen tegenover iemand die er niet bij was. Dus is de uitvoer van een model een voorstel dat een mens of een deterministische regel accepteert, en die acceptatie is wat wordt vastgelegd, met het voorstel ernaast bewaard. Zodra het oordeel van een model rechtstreeks in de afgeleide laag wordt geschreven zonder vast te leggen wat het zag, is de herleidbaarheid weg en sta je weer bij twee dashboards en een discussie.

Kort samengevat

Iedereen kan een dataset ophalen en er een grafiek van maken. De grafiek is niet het moeilijke deel en niet het waardevolle deel.

Het waardevolle deel is dat je in het tweede jaar kunt beantwoorden waar dit getal vandaan komt, wanneer de bron zijn schema heeft omgegooid, de analist ergens anders werkt, en iemand die er niet bij was het resultaat betwist. Dat vermogen voeg je niet later toe. Het is een eigenschap van hoe de pipeline dingen bewaart, en overschrijft de pipeline, dan is het al weg.

Herkomst is geen papierwerk rond het product. In dit soort systemen ís het het product. De grafiek is alleen het stuk waar mensen naar kijken.