Ogni fallimento di migrazione che abbiamo mai visto in altri software nasce dallo stesso punto: trattare “prima pulisci i dati” come un problema del cliente invece che del prodotto. Non volevamo costruire quello. Così, presto, ci siamo imbattuti in un caso reale, e ci ha insegnato qualcosa che non ci aspettavamo: a volte la risposta giusta a “dovrebbe occuparsene l’IA” è no.
La migrazione che ci ha insegnato la differenza
Avevamo un vero export da un CRM legacy da importare: 349 aziende, 491 note, per lo più uno storico chiamate scritto in sloveno, 393 contatti. La mossa ovvia sarebbe stata farlo passare attraverso Neo, la nostra importazione assistita da IA, la stessa che legge un foglio di calcolo disordinato e ne propone record puliti.
Abbiamo deciso di no. I dati di origine erano già strutturati, già risolti, già corretti. Farli passare per un ciclo di IA non avrebbe aggiunto nulla se non rischio: un modello che parafrasa o riassume vere note di chiamata slovene, perdendo silenziosamente precisione in un testo che doveva restare esattamente com’era scritto. Così abbiamo costruito un secondo percorso, separato, deliberatamente privo di IA: copia esatta e deterministica, abbinata per ID del record legacy, con conservazione dei timestamp originali, senza alcun modello linguistico in nessun punto.
Costruire quel percorso ha fatto emergere lacune reali che altrimenti non avremmo trovato: un campo del form che il backend scartava silenziosamente da chissà quanto tempo perché la colonna del database dietro di esso non era mai esistita, contatti che venivano stipati in note di testo libero per mancanza di un posto vero dove stare. Abbiamo risolto entrambe le cose per sempre, non solo per questa migrazione.
Questa è ora la forma reale del Migration Engine: Neo per il disordine onesto, buttateci il vostro foglio di calcolo peggiore e vi diremo chiaramente cosa siamo riusciti a capire e cosa no, e un percorso di copia esatta e silenziosa per i dati già affidabili che devono solo arrivare intatti. Due problemi diversi. Abbiamo smesso di fingere che fossero uno solo.
Arrivare a qualcosa che valga la pena testare
Niente di quello che segue conta se sotto non c’è nulla di reale. Onestamente, parte di come siamo arrivati qui è iniziata per caso. Le registrazioni dello schermo venivano continuamente rimandate, soprattutto per pura riluttanza a sedersi e registrare qualcosa per bene, così il vero workaround era semplicemente guardare l’IA cliccare in giro per l’app dal vivo e prendere appunti su cosa succedeva. Si è rivelato uno dei migliori metodi di debug in cui ci siamo imbattuti in tutto il progetto, non un piano intelligente, ma un sottoprodotto del non voler fare qualcos’altro. Alcune funzionalità reali esistono oggi perché un bug è stato scoperto esattamente così.
La costruzione più ampia ha seguito una forma simile, meno per grande disegno e più per necessità: un pezzo reale alla volta, ognuno testato dall’inizio alla fine prima che iniziasse il successivo, perché un progetto di queste dimensioni non resta sano di mente in nessun altro modo. Quello che ci ha davvero sorpreso è che la suite di test di base ha finito per prevedere cose che nessuno le aveva chiesto di prevedere. Aggiungi una nuova funzionalità mesi dopo, e Velvet a volte scopriva che un pezzo adiacente era rimasto tutto quel tempo silenziosamente a metà, in attesa di essere notato. Non perché chi è coinvolto sia particolarmente acuto. Un test che clicca davvero il pulsante e controlla il risultato reale ha semplicemente un modo tutto suo di trovare cose che una persona che scorre il codice non troverebbe.
Velvet
Da qualche parte lungo il percorso, la nostra suite di test end-to-end ha ricevuto un nome invece di restare semplicemente “i test”. La chiamiamo Velvet. Circa cinquemila righe di Playwright, suddivise in dodici suite con nome, smoke, regressione, il Vault, l’AI Village, flussi di action-bus, casi limite duri, stress, tra le altre, eseguite contro cinque aziende sintetiche e popolate separate. Guida l’applicazione reale in esecuzione, backend e frontend entrambi dal vivo, nessun sostituto simulato per nessuno dei due.
All’interno delle suite di regressione con nome, le chiamate IA sono mockate su risposte deterministiche, di proposito, non come scorciatoia. A volumi reali, le chiamate IA genuine urtano i limiti di connessione del browser e producono fallimenti che non hanno nulla a che fare con il fatto che il prodotto funzioni davvero. Mockare lì isola una domanda onesta, l’impianto funziona, da un’altra, la risposta reale dell’IA è buona, che viene testata separatamente, per davvero.
La disciplina sotto ogni livello
Cinque regole valgono ovunque si testi in questo progetto, non solo in Velvet:
Mettere per iscritto una previsione prima di eseguire il test, così che un successo non possa essere silenziosamente ridefinito dopo come qualunque cosa sia capitata. Un test che conferma solo che un pulsante esiste non è un test; deve cliccare il pulsante, aspettare la risposta reale, inclusa la latenza reale dell’IA, e controllare lo stato risultante effettivo. Ogni funzionalità viene provata facendo ciò che dovrebbe fare, e provata nel modo in cui una persona reale, a volte disattenta, a volte ostile, la userebbe davvero in modo sbagliato. Quando un “fallimento” si rivela essere un bug nello script di test piuttosto che nel prodotto, anche questo viene segnalato, non sistemato in silenzio e dimenticato. E una correzione non è finita quando il codice compila; è finita quando lo scenario esatto che si era rotto è stato rieseguito per davvero ed è passato.
Oltre Velvet: sette livelli, non uno
Velvet è un livello su sette. La verifica IA reale, non mockata, gira separatamente, uno script una tantum contro l’app dal vivo con chiamate genuine al modello, ripulito dopo, specificamente per catturare la classe di bug che emerge solo perché la risposta di un modello reale varia da un’esecuzione all’altra. La simulazione a lungo orizzonte esegue storie aziendali simulate, un mese, un anno, cinque anni, contro tenant reali con chiamate IA dal vivo per tutto il tempo, ponendo le stesse domande canoniche in più punti per verificare se le risposte sono davvero diventate più precise man mano che la storia si accumulava, non solo se tornavano indietro comunque. Un livello avversariale deliberato, otto test ostili che chiamiamo Obstacle Course, cerca di rompere direttamente le affermazioni di onestà e sicurezza del prodotto: prompt injection, fuga di dati tra tenant, istruzioni contraddittorie, resistenza alla cancellazione, riconciliazione finanziaria reale, gestione di account duplicati. I test unitari e di integrazione backend coprono la Sacred Spine e l’ingestione dei documenti dall’inizio alla fine. Le prove guidate testano l’interfaccia reale a dimensioni schermo reali, desktop e un vero viewport mobile, con screenshot come prova, non una semplice spunta su una lista. Persino i video promozionali di presentazione hanno funzionato da livello di test: ognuno è uno script reale che colpisce l’app reale e dal vivo, e ogni momento che sembrava leggermente strano in fase di revisione è stato tracciato individualmente fino a un bug reale o a un problema di timing dello script, mai dato per scontato senza verifica.
In tutto questo, circa venticinque anni simulati di attività aziendale, diversi settori, oltre novemila voci reali del Vault generate lungo il percorso, undici bug reali trovati e corretti. Spesa API reale totale per l’intero programma: 9,57 dollari.
Cosa non affermeremo
Non abbiamo testato la scala enterprise, migliaia di tenant simultanei insieme. Non abbiamo testato sotto stress cosa succede al costo dell’IA con una chat quotidiana genuinamente intensa e aperta, a differenza dell’attività aziendale scriptata attorno a cui è stata costruita la maggior parte di questi test. Durante il lavoro descritto sopra, abbiamo trovato un vero rischio di scalabilità nel nostro stesso backfill degli embedding, il recupero una tantum di un grande Vault che si avvicinava scomodamente a un timeout della richiesta, e ve lo diciamo invece di sistemarlo in silenzio senza dire nulla.
Niente qui è perfetto, perché niente lo è. Quello di cui possiamo davvero rispondere è di aver guardato, a fondo, alle nostre stesse affermazioni prima di presentarvele, e di dirvi cosa abbiamo trovato in entrambi i casi. Questa è tutta la disciplina. Continueremo a farlo.