deSchouwVloot verdiepen
Metadata in een CI/CD-pijplijn: vastleggen, niet reconstrueren
Een pijplijn laat zien dát er iets gebeurde, maar zelden wát. Welke velden je op het moment zelf moet opschrijven voordat er iets te meten valt — en wie dat opschrijft.
Dit stuk staat hier omdat ik het begonnen ben, en niet omdat er onderzoek voorbijkwam dat erom vroeg. De aanleiding is deSchouwVloot — de gedeelde pijplijn waar deze site en drie andere repo's op varen. Die doet inmiddels genoeg om vragen op te roepen die ik niet kan beantwoorden. Hoe vaak houdt een poort werkelijk iets tegen? Hoe lang ligt een wijziging open voordat 'ie binnen is? Hoeveel gaat er zonder mij doorheen, en wat moest daarvan achteraf terug?
Ik weet dat niet. Niet omdat de pijplijn stil is — er is per run van alles te zien — maar omdat zien iets anders is dan meten. Wat volgt is dus half onderzoek en half ontwerpnotitie: eerst wat er buiten deze repo over bekend is, en daarna wat ik ermee zou doen.
Een meting is geen gebeurtenis die je later terugleest. Het is een waarde die op het moment zelf is opgeschreven, door iets wat er geen belang bij heeft.
Wat de pijplijn nu van zichzelf weet
Op het oog een heleboel. Elke run heeft een status, elke poort een uitkomst, elke PR een rij checks, en labels sturen het geheel aan — claude-task start een bouw, needs-human zet 'm stil. Wie een probleem heeft, opent de run en ziet wat er gebeurde.
Dat werkt uitstekend voor één geval en niet voor honderd. De vraag “wat ging hier mis” is een leesvraag; de vraag “gaat dit beter dan vorig kwartaal” is een rekenvraag, en daar heb je waarden voor nodig in plaats van pagina's. Er staat op dit moment nergens een getal dat zegt hoe vaak de bouwpoort rood werd. Er staat ook nergens welk model een PR schreef, of hoeveel pogingen het kostte. Dat zijn geen ontbrekende dashboards — het zijn ontbrekende velden.
Er is nog iets, en dat is de vervelende helft: wat er wél te zien is, verdwijnt.
De houdbaarheidsdatum van een log
GitHub is daar expliciet over. In de documentatie staat dat artefacten en logbestanden van workflows standaard 90 dagen bewaard worden voordat ze automatisch verwijderd worden. Je mag die termijn verzetten — tussen 1 en 90 dagen voor een publieke repo, tussen 1 en 400 voor een private — maar met een voetnoot die er precies één keer toe doet: een gewijzigde termijn geldt alleen voor nieuwe artefacten en logs, en werkt niet met terugwerkende kracht.
Dat is de val. Je merkt dat je iets had willen weten op het moment dat je het wilt weten — en dat moment ligt na de gebeurtenis. Een kwartaalvraag over een pijplijn met een bewaartermijn van een kwartaal komt structureel te laat, en de reparatie (“zet de termijn op 400 dagen”) helpt pas over 400 dagen.
Er ligt al een woordenboek
De verleiding is om zelf veldnamen te verzinnen. Dat hoeft niet: OpenTelemetry heeft voor precies dit onderwerp semantische conventies, en die zijn publiek na te lezen. Aan de pijplijnkant staan daar onder meer cicd.pipeline.run.duration, cicd.pipeline.run.errors en cicd.pipeline.run.active; aan de versiebeheerkant vcs.change.count, vcs.change.duration, vcs.change.time_to_approval en vcs.change.time_to_merge.
Die laatste twee zijn interessanter dan ze klinken, want ze zijn precies de vragen waar ik mee begon. vcs.change.time_to_merge is in de conventie omschreven als de tijd die sinds het aanmaken van een wijziging verstreek voordat 'ie in de doelbranch werd samengevoegd — niet mijn definitie, maar een definitie waar anderen al over gediscussieerd hebben.
Wat de statusaanduidingen bij die velden betekenen
De conventie markeert per veld hoe stabiel 'ie is. De CI/CD-metrieken en de meeste VCS-metrieken staan op release candidate; vcs.change.time_to_merge en vcs.change.time_to_approval staan een trede lager, op development.
Dat is geen reden om ze te laten liggen, wél een reden om ze op te schrijven in een vorm die je kunt hernoemen. Een veld in een bestand van jezelf hernoem je met een script; een veld waar een dashboard van een leverancier aan hangt, niet.
Wie het opschrijft, doet ertoe
Er is een tweede les te lenen, uit een hoek die er op het eerste gezicht niets mee te maken heeft: de SLSA-specificatie voor provenance. Die beschrijft hoe een bouwplatform vastlegt waar, wanneer en hoe een artefact gemaakt is — met velden voor de externe parameters, de opgeloste afhankelijkheden, de identiteit van de bouwer en de begin- en eindtijd van de run.
Het punt zit niet in de velden maar in wie ze invult. Provenance wordt opgesteld door het platform dat de bouw uitvoert, en niet door de bouw zelf. Het onderscheid tussen externe parameters (door de gebruiker aangeleverd, dus stroomafwaarts te controleren) en interne parameters (door het platform gezet, dus vertrouwd) is daar de hele architectuur.
Dat principe kent deze repo al
In de instructies aan de bouwagent staat letterlijk dat 'ie geen goedkeuringslabels op z'n eigen PR mag zetten. Dezelfde redenering: wie beoordeeld wordt, schrijft het oordeel niet op. Voor metingen geldt dat net zo goed — een agent die zelf noteert hoeveel pogingen 'ie nodig had, meet zichzelf.
Waarom dit nu de moeite waard is
DORA publiceerde in 2025 een rapport over AI-ondersteunde softwareontwikkeling, op basis van bijna 5.000 respondenten. De hoofdlijn is dat AI werkt als versterker: het vergroot wat er al goed ging en wat er al misging. Twee cijfermatige bevindingen horen daarbij: 90% van de respondenten gebruikt AI op het werk, en waar het verband met doorvoer vorig jaar nog negatief was, is het dat nu niet meer — terwijl het verband met stabiliteit negatief blijft.
Meer doorvoer, minder stabiliteit. De verklaring die het rapport zelf geeft is dat een toename in het aantal wijzigingen tot instabiliteit leidt zolang er geen stevige controlemechanismen onder liggen: geautomatiseerd testen, volwassen versiebeheer, snelle terugkoppeling.
Dat is voor een pijplijn met agents erin geen alarmbel maar een reden om te tellen. Of deze pijplijn aan de goede of de verkeerde kant van dat verband staat, is een empirische vraag — en op dit moment eentje die ik niet kan beantwoorden. Dat het instelbaar is hoeveel er zonder mens doorgaat maakt het scherper: een stand die je kunt verzetten zonder te weten wat 'ie oplevert, is een knop en geen keuze.
Vanaf hier: mijn interpretatie
Alles hierboven is terug te lezen in de bronnen onderaan. Wat nu volgt staat daar niet in: het is het ontwerp dat ik eruit afleid voor deze pijplijn. Neem het als voorstel, niet als bevinding — en zeker niet als beschrijving van iets wat er al staat.
Wat ik zou vastleggen
Eén regel per samengevoegde wijziging, geschreven door de workflow die de merge doet. Niet meer dan een handvol velden, want elk veld is onderhoud. Per vraag die ik werkelijk heb:
- Hoe lang lag dit open? —
vcs.change.time_to_merge, in seconden, plus de tijd tot de eerste goedkeuring als die er was. Beide komen uit de GitHub-API en hoeven niet geschat te worden. - Wat hield het tegen? — de naam van de check die als laatste rood stond, en hoe vaak er een rode ronde was. Dit is het veld met de meeste haast: het staat nu in de run, en de run heeft een einddatum.
- Ging dit zonder mens door? — de autonomiestand die op dat moment gold, en of er een menselijke goedkeuring aan te pas kwam. Die stand kan tussen twee PR's verschillen, dus 'm achteraf uit het contractbestand lezen geeft de stand van vandaag en niet die van toen. Dit veld is alleen juist als het op het moment zelf gezet wordt.
- Wie heeft dit geschreven? — mens, agent, of allebei; en bij een agent welk model en welke pin van de vloot. Zonder dat veld is de vraag “is het beter geworden sinds de modelwissel” achteraf niet meer te stellen, want een commit weet niet wie 'm schreef.
- Wat kwam er daarna terug? — of er binnen een afgesproken venster een revert of een herstel-PR op volgde. Dit is het enige veld dat pas later ingevuld kan worden, en juist daarom bepaalt het de vorm: een record is een bestand dat je bijwerkt, geen momentopname die je archiveert.
Dat laatste veld is ook de reden dat dit niet met logs op te lossen is, hoe lang je ze ook bewaart. Een faalpercentage vraagt om een verband tussen twee gebeurtenissen die weken uit elkaar liggen; een log kent alleen zichzelf. En het sluit aan op iets wat elders op deze site al staat: dat een groene poort geen bewijs van correctheid is. Wat er ná de merge gebeurde, is de enige controle die dat wél benadert.
De kleinste eerste stap
Met drie randvoorwaarden die van deze repo zelf komen: statisch, geen nieuwe externe bestemming, geen tracking. Die sluiten een observability-backend uit, en dat is minder beperkend dan het klinkt.
- Schrijf naar de repo, niet naar een dienst. Eén JSON-regel per merge, toegevoegd aan een bestand. Dat is versiebeheerd, doorzoekbaar met gereedschap dat er al is, en het voegt geen bestemming toe die iemand later moet vertrouwen.
- Neem de namen over die er al zijn. Ook zonder collector en zonder één regel OpenTelemetry-code: een veld dat
vcs.change.time_to_mergeheet, draagt een definitie waar iemand anders al over nagedacht heeft. - Laat de meetstap de merge niet kunnen breken. Een meting die de pijplijn die 'ie meet rood kan maken, is een nieuwe faalklasse in ruil voor een getal. Falen hoort hier zichtbaar te zijn en verder onschadelijk.
- Begin met vijf velden en één vraag. Niet met een schema dat alles aankan. De vraag waarmee ik zou beginnen is de goedkoopste van de vijf hierboven: hoe vaak werd welke poort rood. Dat antwoord is er over 90 dagen niet meer, en is dus het duurste om uit te stellen.
Waar het ophoudt
Vier voorbehouden, waarvan één die het hele idee kan ondermijnen
Het getal wordt een doel. DORA waarschuwt er in de eigen gids over de vijf metrieken zelf voor: metrieken tot doel verheffen en de wet van Goodhart negeren levert perverse prikkels op, en het onderling vergelijken van teams is een valkuil. Hier is dat risico klein en anders van vorm — er zijn geen teams om te vergelijken, alleen mijn eigen verleden — maar een doorlooptijd die korter wordt doordat ik poorten weghaal, is dezelfde fout in het klein.
Vier repo's en één eigenaar. Wat hier uitkomt is een beschrijving van één pijplijn en geen bewijs over pijplijnen. Elk getal dat hieruit voortkomt hoort met die herkomst erbij geciteerd te worden, ook door mij.
De veldnamen kunnen nog schuiven. Twee van de VCS-velden die ik het hardst nodig heb, staan in de conventie op development. Dat is geen reden om ze niet te gebruiken, wel om ze in een bestand te zetten dat ik zelf kan hernoemen.
Een veld zonder lezer is onderhoud. De eerlijke toets komt over een kwartaal: is er één besluit anders genomen doordat dit er stond? Zo niet, dan is het geen meting maar administratie, en hoort het weg. Dat geldt ook voor het idee als geheel.
De pijplijn waar dit over gaat
Wat deSchouwVloot is, welke poorten er staan en wat er per repo instelbaar is, staat op de projectpagina. Verandert er iets naar aanleiding van dit stuk, dan is dat daar te zien — en dan hoort dit stuk een datum onder “bijgewerkt” te krijgen.
Toets het op je eigen systeem
Laat je eigen pijplijn opsommen welke vragen hij over zichzelf niet kan beantwoorden, en op welk moment het antwoord had moeten worden opgeschreven.
Je gaat uitzoeken wat deze pijplijn over zichzelf vastlegt, en wat hij alleen laat zien. Dat is niet hetzelfde: laten zien is een run die je kunt openen, vastleggen is een waarde die er over een jaar nog staat. Stap 1 — inventariseer wat er nu bewaard blijft. Loop de workflows, de scripts en de repo-instellingen na. Noem per plek: welke gegevens ontstaan daar, waar komen ze terecht (log, artefact, commit, bestand, issue, extern systeem), en hoe lang blijven ze daar. Zoek de bewaartermijn op in plaats van 'm aan te nemen, en zeg erbij waar je 'm vond. Stap 2 — stel vijf vragen die je pas over drie maanden zou stellen. Bijvoorbeeld: hoe vaak werd deze poort rood, hoe lang lag een wijziging gemiddeld open, hoeveel pogingen kostte een geslaagde bouw, hoeveel ging er zonder menselijke goedkeuring door, hoeveel daarvan werd daarna teruggedraaid. Verzin er ook één die specifiek is voor dit project. Stap 3 — beantwoord per vraag: is het antwoord er over drie maanden nog? Deel in bij (a) staat als waarde ergens die blijft, (b) staat in iets met een bewaartermijn, (c) is achteraf te reconstrueren uit git, (d) is er dan niet meer. Wees streng bij (c): reconstructie uit commit-titels is een gok zodra iemand een titel anders formuleert. Stap 4 — wijs per vraag uit categorie (b) en (d) het moment aan waarop het antwoord had kunnen worden opgeschreven: welke stap in welke workflow, en welk veld. Gebruik waar mogelijk een bestaande veldnaam uit de OpenTelemetry-conventies voor CI/CD en versiebeheer in plaats van een eigen naam. Stap 5 — noem wat je zou weglaten. Een veld dat over een kwartaal geen enkel besluit anders maakt, is onderhoud zonder lezer. Geef er minstens één, met de reden.
De promptknop plakt de bronnen en de vindplaats van dit stuk eronder.
Bronnen
- Semantic conventions for CI/CD metricsOpenTelemetry Authors · OpenTelemetry Semantic Conventions, geraadpleegd 25 augustus 2026 · 2026
- State of AI-assisted Software DevelopmentDORA (Google Cloud) · Jaarrapport 2025, bijna 5.000 respondenten · 2025
- DORA metrics: the five keysDORA (Google Cloud) · DORA-gids, geraadpleegd 25 augustus 2026 · 2026
- Configuring the retention period for GitHub Actions artifacts and logs in your organizationGitHub · GitHub Docs, geraadpleegd 25 augustus 2026 · 2026
- SLSA ProvenanceSLSA / OpenSSF · Specificatie v1.2, vastgesteld november 2025 · 2025