Week 24: De studio, de opruiming en een bewuste verwijdering
Een terminal UI voor de podcaststudio, infrastructuur die zichzelf herstelt, mutation testing op legacy code, en de beslissing om een feature te verwijderen die ik nooit had moeten bouwen.
Er zijn weken die in het teken staan van iets nieuws bouwen. Week 24 was gedeeltelijk dat, maar meer nog de week van de stille verbetering: de infrastructuur die je niet ziet maar wel elke dag gebruikt, de opruiming die werk zichtbaarder maakt, en één beslissing die ik bewust nam in plaats van voor me uit te schuiven.
De studio
Begin van de week bouwde ik een terminal user interface voor de podcast workflow. Niet omdat het moest, maar omdat de huidige situatie steeds frustrerender werd: scripts aanroepen via de command line, switchen tussen Trello in de browser en de terminal, handmatig bijhouden welke stap in het productieproces je bent.
Met Textual — een Python library voor terminal UIs — heb ik de Studio gebouwd. Een scherm dat laat zien welke gasten in welke fase zitten (van ‘Akkoord’ tot ‘Klaar’), welke acties bij die fase horen, en een knop voor elk script dat je normaal handmatig aanroept. De Trello board is de enige bron van waarheid. De Studio visualiseert die status en maakt de juiste acties beschikbaar.
Het interessante van dit soort tooling is de mismatch die het blootlegt. Terwijl ik de fasen in Trello aan het mappen was naar knoppen in de UI, merkte ik dat de volgorde van stappen in mijn hoofd niet overeenkwam met de volgorde die ik eigenlijk altijd volgde. invite_guest na bevestiging, niet direct na eerste benadering. record_reflections pas nadat de opname volledig klaar is. Kleine nuances die je pas echt doordenkt als je ze concreet moet maken.
De Studio start nu met ./studio en is in gebruik.
Infrastructuur die zichzelf herstelt
Verspreid over meerdere dagen werkte ik aan de infrastructuur die de podcast workflow ondersteunt. Drie dingen die op zichzelf klein zijn, maar samen behoorlijk wat frictie wegnemen.
Eerst de calendar invites. Gasten die uitgenodigd worden voor een opname, kregen tot nu toe een email met uitleg en een verzoek om de datum te bevestigen. Dat werkte, maar Outlook — het systeem dat de meeste zakelijke gasten gebruiken — behandelt ingebedde calendar items in emails op zijn eigen manier. Na wat omwegen ben ik overgestapt naar native .ics bestanden: een standaard format voor kalenderuitnodigingen dat Outlook direct herkent, met Accept en Decline knoppen die gewoon werken.
Dan SearXNG, de zoekmachine die lokaal draait voor research-taken. Die was niet altijd beschikbaar als scripts hem nodig hadden. Nu detecteert het systeem of SearXNG bereikbaar is, en start Docker Compose automatisch op als dat niet het geval is. Tien seconden wachten, polling per seconde, doorgaan. Simpel, maar het elimineert een categorie van “oh, het werkt niet want de container staat niet” fouten.
Tot slot een vervelend terugkerend probleem met Python imports. Scripts die direct gerund werden (python src/producer/producer.py) hadden een andere sys.path dan scripts die als module werden aangeroepen (python -m producer.producer). De oplossing: een .pth file die de src/ directory permanent toevoegt aan het Python path in de virtual environment. Eén bestand, 1203 tests die daarna gewoon bleven werken.
Mutanten op legacy code
Begin van de week liep ik mutation testing op label_speakers.py, een module die al een tijdje in de codebase zit. Mutmut genereerde 592 mutanten — kleine aanpassingen aan de code, elk een mogelijke bug — en checkte of mijn tests die opmerkten.
Kill rate: 22%. Dat klinkt laag, maar voor code die zwaar leunt op I/O (bestandssysteem, externe APIs) is het niet verrassend. Die categorie is structureel lastig te testen via unit tests. Wat wél te testen was: de logica die niet I/O-afhankelijk is. Dat resulteerde in concrete fixes — een functie met cyclomatic complexity van 27 gereduceerd naar 6, 32 nieuwe tests voor de helper functies — en een gedetailleerd issue in GitHub voor de overlevende mutanten die wel testbaar zijn maar nog niet gedekt.
De les die herhaling verdient: 100% coverage zegt niets over de kwaliteit van je tests. Een test die controleert of een functie geen exception gooit, telt mee voor coverage. Een test die controleert of de output klopt, ook. Maar alleen de tweede beschermt je echt.
De harness als kwaliteitswacht
Nu de meeste tekortkomingen zijn weggewerkt — falende tests, mutation testing achterstanden, te complexe functies — is er een interessant moment ontstaan. Voor het eerst in lange tijd kan ik ervan uitgaan dat er geen “pre-existing failures” zijn in de codebase.
Dat klinkt als een kleine mijlpaal, maar het is meer dan dat. Claude Code heeft een gewoonte die ik steeds vaker tegenkwam: zodra tests falen, trekt het de conclusie dat het om “pre-existing failures” gaat — fouten die er al lagen vóór het aan de slag ging — en gaat gewoon door. Handig als ontsnapping. Onterecht als standaardredenering.
Dat is nu geblokkeerd. Zowel in de CLAUDE.md als via een hook staat expliciet: pre-existing failures bestaan niet. Niets gaat naar GitHub waar aantoonbare fouten in zitten. De harness dwingt dit af — niet via vertrouwen op het model, maar via het gereedschap dat de commit blokkeert als tests rood staan of complexiteit de drempel overschrijdt.
De volgende stap is dat de harness niet alleen blokkeert, maar ook verbetert. Het idee: wanneer een pre-commit hook faalt, triggert de harness automatisch een verbetering. De coding agent identificeert de fout en voert de fix door — niet “het lukt niet, ik ga door”, maar “het lukt niet, ik los het op”. Dat is de richting. Sensors die niet alleen signaleren maar ook corrigeren.
Betekent dit dat de codebase nu bug-vrij is? Was dat maar zo. Het betekent dat er geen aantoonbare fouten naar productie gaan. Dat is een fundamenteel ander doel — en eerlijker ook.
Wat het met mij deed
De beslissing die het langst bleef hangen was geen technische. Vrijdag werd de contradiction detector verwijderd uit de codebase.
De contradiction detector analyseerde gesprekken tussen gasten en markeerde inhoudelijke tegenstrijdigheden. Technisch gezien werkte het. Maar terwijl ik ernaar keek, werd ik steeds ongemakkelijker. Gasten komen naar het podium om hun verhaal te vertellen. Ze vertrouwen erop dat dat gesprek eerlijk en veilig is. Een systeem dat hun uitspraken automatisch op inconsistenties analyseert — ook intern, ook als de gast het nooit ziet — voelt als iets dat ik niet zou willen zijn als ik de gast was.
Het is een kleine beslissing in het grotere geheel. Maar ik merk dat dit soort beslissingen steeds bewuster worden. Niet “kan ik dit bouwen?” maar “moet ik dit bouwen?”
En dan is er de Studio zelf. Ik had verwacht dat het bouwen van een terminal UI voor de podcast productie flow een nuttige maar droge klus zou zijn. In de praktijk had het iets bevredigends. Niet omdat de UI indrukwekkend is — het is een paar schermen in de terminal — maar omdat het de workflow zichtbaar maakt. Elke stap heeft een naam. Elke actie heeft een knop. Het maakt expliciet wat daarvoor impliciet was.
Dat is ook wat ik merk bij de rest van de infrastructuurverbeteringen. De .ics bestanden, de SearXNG auto-start, de .pth fix: ze zijn onzichtbaar als ze werken. Zonder hen merk je het bij elke sessie. Goed gereedschap is onzichtbaar gereedschap.
Het patroon
Week 24 was een week van het systeem verbeteren. Niet spectaculair, maar solide. De Studio visualiseert wat al bestaat. De calendar invites elimineren een categorie handmatige stappen. De mutanten dwingen eerlijkheid over de kwaliteit van tests. En de contradiction detector is weg.
Soms is verwijderen ook voortgang.
Deze blog is geschreven met behulp van AI, op basis van mijn chats uit deze week in combinatie met mijn journal.