Elke migratiefout die we ooit in andere software hebben gezien, komt uit dezelfde hoek: “maak eerst de data schoon” behandelen als het probleem van de klant in plaats van het product. Dat wilden we niet bouwen. Dus liepen we er vroeg zelf tegenaan, bij een echt geval, en dat leerde ons iets wat we niet verwachtten: soms is het juiste antwoord op “moet de AI dit doen” gewoon nee.
De migratie die ons het verschil leerde
We hadden een echte export uit een oud CRM binnen te halen: 349 bedrijven, 491 notities, meestal belgeschiedenis geschreven in het Sloveens, 393 contacten. De voor de hand liggende zet zou zijn geweest dit door Neo te laten lopen, onze AI-ondersteunde import, dezelfde die een rommelige spreadsheet leest en er nette records uit voorstelt.
We besloten van niet. De brondata was al gestructureerd, al opgelost, al correct. Er een AI-doorgang doorheen sturen zou niets hebben toegevoegd behalve risico: een model dat echte Sloveense belnotities parafraseert of samenvat, en daarbij stilletjes precisie verliest in tekst die exact moest blijven zoals hij geschreven was. Dus bouwden we een tweede, apart, bewust AI-vrij traject: exact, deterministisch kopiëren, gekoppeld op legacy-record-ID, met behoud van originele tijdstempels, zonder ergens een taalmodel erin.
Het bouwen van dat traject bracht echte gaten aan het licht die we anders niet hadden gevonden: een formulierveld dat de backend al wie weet hoelang stilletjes had laten vallen omdat de databasekolom erachter nooit had bestaan, contacten die bij gebrek aan een echte plek werden volgepropt in vrije-tekstnotities. Beide voorgoed opgelost, niet alleen voor deze ene migratie.
Dat is nu de daadwerkelijke vorm van de Migration Engine: Neo voor de eerlijke rommel, gooi ons jullie slechtste spreadsheet voor de voeten en we vertellen jullie ronduit wat we ervan konden maken en wat niet, en een stil, exact kopieertraject voor data die al betrouwbaar is en gewoon intact moet aankomen. Twee verschillende problemen. We zijn gestopt met doen alsof het er één was.
Sowieso tot iets komen dat het testen waard is
Niets van het testen hieronder doet ertoe als er niets echts onder ligt. Eerlijk gezegd begon een deel van hoe we hier kwamen bij toeval. Schermopnames werden steeds uitgesteld, vooral uit gewone onwil om echt eens te gaan zitten en iets netjes op te nemen, dus de eigenlijke omweg was gewoon kijken hoe de AI live door de app klikte en aantekeningen maken van wat er gebeurde. Dat bleek een van de beste debugmethodes die we in het hele project tegenkwamen, geen slim plan, maar een bijproduct van iets anders niet willen doen. Een paar echte functies bestaan vandaag omdat een bug precies zo werd gevonden.
De grotere opbouw volgde een vergelijkbare vorm, minder door groot ontwerp dan door noodzaak: één echt stuk tegelijk, elk end-to-end getest voordat het volgende begon, omdat een project van deze omvang op geen andere manier behapbaar blijft. Wat ons echt verraste, is dat de basistestsuite uiteindelijk dingen voorspelde die niemand haar had gevraagd te voorspellen. Voeg maanden later een nieuwe functie toe, en Velvet betrapte soms dat een aangrenzend stuk de hele tijd stilletjes half af had liggen wachten om opgemerkt te worden. Niet omdat iedereen die erbij betrokken is bijzonder scherp is. Een test die daadwerkelijk op de knop klikt en het echte resultaat controleert, vindt op zijn eigen manier gewoon dingen die iemand die de code doorbladert niet zou vinden.
Velvet
Ergens onderweg kreeg onze eigen end-to-end testsuite een naam in plaats van gewoon “de tests” te blijven. We noemen haar Velvet. Ruwweg vijfduizend regels Playwright, verdeeld over twaalf benoemde sets, smoke, regressie, de Vault, het AI Village, action-bus-stromen, harde randgevallen, stress, onder andere, gedraaid tegen vijf aparte synthetische, gevulde testbedrijven. Ze stuurt de echte, draaiende applicatie aan, backend en frontend allebei live, geen gemockte vervanging voor een van beide.
Binnen de benoemde regressiesets zijn AI-aanroepen gemockt naar deterministische antwoorden, met opzet, niet als kortere weg. Bij echte volumes botsen echte AI-aanroepen tegen de verbindingslimieten van de browser en veroorzaken ze fouten die niets te maken hebben met of het product daadwerkelijk werkt. Mocking isoleert daar één eerlijke vraag, werkt de bedrading, van een andere, is het echte antwoord van de AI goed, die apart wordt getest, echt.
De discipline onder elke laag
Vijf regels gelden overal waar in dit project wordt getest, niet alleen in Velvet:
Leg een voorspelling schriftelijk vast voordat je de test draait, zodat een geslaagde test achteraf niet stilletjes herdefinieerd kan worden als wat toevallig gebeurde. Een test die alleen bevestigt dat een knop bestaat, is geen test; hij moet op de knop klikken, op het echte antwoord wachten, inclusief echte AI-latentie, en de daadwerkelijke resulterende toestand controleren. Elke functie wordt geprobeerd zoals hij hoort te werken, en geprobeerd zoals een echte, soms slordige, soms vijandige persoon hem daadwerkelijk verkeerd zou gebruiken. Wanneer een “fout” achteraf een bug in het testscript blijkt te zijn in plaats van in het product, wordt dat ook gemeld, niet stilletjes gepatcht en vergeten. En een fix is niet klaar wanneer de code compileert; hij is klaar wanneer precies het scenario dat kapot was echt opnieuw is gedraaid en geslaagd is.
Voorbij Velvet: zeven lagen, niet één
Velvet is één laag van zeven. Echte, niet-gemockte AI-verificatie draait apart, een eenmalig script tegen de live app met echte modelaanroepen, daarna opgeruimd, specifiek om het type bug te vangen dat alleen opduikt omdat het antwoord van een echt model van run tot run varieert. Langetermijnsimulatie draait gesimuleerde bedrijfsgeschiedenissen, een maand, een jaar, vijf jaar, tegen echte tenants met de hele tijd live AI-aanroepen, waarbij op meerdere punten dezelfde standaardvragen worden gesteld om te controleren of antwoorden daadwerkelijk scherper werden naarmate de geschiedenis zich opstapelde, niet alleen of ze überhaupt terugkwamen. Een bewuste vijandige laag, acht vijandige tests die we het Obstacle Course noemen, probeert de eerlijkheids- en veiligheidsclaims van het product direct te breken: prompt injection, datalekken tussen tenants, tegenstrijdige instructies, weerstand tegen verwijdering, echte financiële afstemming, omgaan met dubbele accounts. Backend unit- en integratietests dekken de Sacred Spine en documentinname end-to-end. Begeleide doorlopen testen de daadwerkelijke UI bij echte schermformaten, desktop en een echt mobiel viewport, met screenshots als bewijs, geen simpel vinkje op een lijst. Zelfs de presentatietrailervideo’s dienden als testlaag: elke video is een echt script tegen de echte, live app, en elk moment dat bij review net iets vreemd oogde, is individueel herleid tot óf een echte bug óf een timingprobleem in het script, nooit zomaar aangenomen zonder controle.
Over alles heen, ruwweg vijfentwintig gesimuleerde jaren bedrijfsactiviteit, meerdere sectoren, ruim negenduizend echte Vault-items onderweg gegenereerd, elf echte bugs gevonden en opgelost. Totale echte API-uitgave voor het hele programma: 9,57 dollar.
Wat we niet gaan claimen
We hebben enterprise-schaal niet getest, duizenden gelijktijdige tenants tegelijk. We hebben niet stresstest wat er met de AI-kosten gebeurt bij een echt intensieve, open-eind dagelijkse chat, in tegenstelling tot de gescripte bedrijfsactiviteit waar het grootste deel van dit testen omheen is gebouwd. Tijdens het bovenstaande werk vonden we een reëel schaalrisico in onze eigen embeddings-backfill, een eenmalige inhaalslag van een grote Vault die onaangenaam dicht bij een request-timeout kwam, en dat vertellen we jullie in plaats van het stilletjes te patchen en niets te zeggen.
Niets hier is perfect, want niets is dat. Waar we daadwerkelijk voor kunnen instaan, is dat we grondig naar onze eigen claims hebben gekeken voordat we ze aan jullie voorlegden, en dat we vertellen wat we vonden, hoe het ook uitpakte. Dat is de hele discipline. Daar blijven we mee doorgaan.