Claude Code per la correttezza import/export dati: definisci regole di validazione, formati di errore coerenti e fuzz test per import CSV/JSON per ridurre i ticket su casi limite.

Gli import raramente falliscono perché il codice è “sbagliato”. Falliscono perché i dati reali sono disordinati, incoerenti e prodotti da persone che non conoscono le tue assunzioni.
I problemi CSV riguardano di solito la forma e il formato. I problemi JSON riguardano più spesso il significato e i tipi. Entrambi possono rompersi in modi che sembrano minori ma creano risultati confusi.
Questi problemi riappaiono continuamente nei ticket di supporto:
La correttezza non è solo “è stato importato”. Devi decidere quali esiti sono permessi, perché gli utenti notano gli errori silenziosi più dei fallimenti rumorosi.
La maggior parte dei team può accordarsi su tre esiti:
I casi limite diventano rielaborazioni quando le persone non capiscono cosa è andato storto o come correggerlo rapidamente. Uno scenario comune: un cliente carica un CSV di 5.000 righe, l'importer dice “Invalid format”, e riprova tre volte con modifiche casuali. Questo genera più ticket più qualcuno dalla tua parte che cerca di riprodurre il file in locale.
Fissa obiettivi che riducano il ciclo: meno retry, correzioni più veloci, risultati prevedibili. Prima di scrivere regole, decidi cosa significa “parziale” (e se lo permetti), come riporterai i problemi a livello di riga e cosa dovrebbe fare l'utente dopo (modificare il file, mappare i campi o esportare una versione corretta). Se usi una piattaforma tipo Koder.ai (koder.ai) per generare rapidamente validator e test, il contratto di import rimane comunque ciò che mantiene il comportamento coerente man mano che il prodotto evolve.
Prima di scrivere una singola regola di validazione, decidi cosa significa “input valido” per il tuo prodotto. La maggior parte dei bug di import sono aspettative disallineate tra ciò che gli utenti caricano e ciò che il sistema assume silenziosamente.
Inizia dai formati, e sii esplicito. “CSV” può significare virgola o punto e virgola, riga di header o no, UTF-8 o “quello che ha prodotto Excel”. Per JSON, decidi se accetti un singolo oggetto, un array di record o JSON Lines (un oggetto JSON per riga). Se accetti JSON nidificato, definisci quali path leggi e quali ignori.
Poi blocca il contratto dei campi. Per ogni campo, decidi se è required, optional, o optional con default. I default sono parte del contratto, non un dettaglio di implementazione. Se country manca, fai il default a vuoto, scegli un paese specifico o rigetti la riga?
Il comportamento di parsing è dove gli import “tolleranti” creano dolore a lungo termine. Decidi in anticipo quanto sei severo nel rimuovere spazi, normalizzare le maiuscole e accettare varianti come "yes"/"true"/"1". La tolleranza va bene se è prevedibile e documentata.
I duplicati sono un'altra decisione di contratto che influenza correttezza e fiducia. Definisci cosa conta come duplicato (stessa email, stesso external_id, o la stessa combinazione di campi), dove lo rilevi (all'interno del file, contro i dati esistenti, o entrambi), e cosa fai quando succede (keep first, keep last, merge, o reject).
Una checklist di contratto che puoi incollare in una spec:
Esempio: importare “customers”. Se l'email è la chiave unica, decidi se " [email protected] " è uguale a "[email protected]", se l'email mancante è consentita quando esiste external_id, e se i duplicati all'interno del file devono essere rifiutati anche se il database non ha corrispondenze. Una volta fissato questo contratto, il comportamento coerente tra UI e API è molto più semplice, sia che tu lo implementi in Koder.ai sia altrove.
I import disordinati spesso iniziano con un unico enorme validate(). Un approccio più pulito è usare regole stratificate con nomi chiari e funzioni piccole. Questo rende le modifiche più facili da revisionare e i test più semplici da scrivere.
Inizia con regole a livello di campo: verifiche che un singolo valore possa passare o fallire da solo (tipo, range, lunghezza, valori ammessi, regex). Mantienile noiose e prevedibili. Esempi: email corrisponde a un pattern email di base, age è un intero tra 0 e 120, status è uno di active|paused|deleted.
Aggiungi regole cross-field solo dove contano. Questi controlli dipendono da più campi, e i bug si nascondono qui. Esempi classici: startDate deve essere prima di endDate, o total deve essere uguale a subtotal + tax - discount. Scrivi queste regole in modo che possano indicare campi specifici, non solo “record invalid”.
Separa le regole a livello di record da quelle a livello di file. Una regola record-level controlla una riga (CSV) o un oggetto (JSON). Una regola file-level controlla tutto l'upload: header richiesti esistono, una chiave unica non si ripete tra le righe, il numero di colonne corrisponde alle aspettative, o il file dichiara una versione supportata.
La normalizzazione dovrebbe essere esplicita, non “magica”. Decidi cosa normalizzi prima di validare e documentalo. Esempi comuni includono trim degli spazi, normalizzazione Unicode (così caratteri visivamente identici sono confrontati allo stesso modo) e formattazione dei numeri di telefono in un formato coerente di memorizzazione.
Una struttura che resta leggibile:
Versiona le tue regole. Metti un schemaVersion (o un profilo di import) nel file o nella richiesta API. Quando cambi cosa significa “valido”, puoi ancora re-importare esportazioni vecchie usando la versione precedente. Questa scelta evita molti ticket “funzionava ieri”.
Un buon importer fallisce in modo utile. Messaggi vaghi portano a retry casuali e lavoro di supporto evitabile. Un formato di errore chiaro aiuta gli utenti a correggere il file rapidamente e ti aiuta a migliorare la validazione senza rompere i client.
Inizia con una forma stabile dell'oggetto errore e mantienila coerente tra CSV e JSON. Puoi usare Claude Code per proporre uno schema e alcuni esempi realistici, poi fissarlo come parte del contratto di import.
Tratta ogni errore come un piccolo record con campi che non cambiano. Il message può evolvere, ma il codice e la location devono rimanere stabili.
code: un identificatore corto e stabile come REQUIRED_MISSING o INVALID_DATEmessage: una frase leggibile dall'utente per la UIpath: dove si trova il problema (JSON pointer come /customer/email, o un nome colonna come email)row o line: per CSV, includi il numero di riga 1-based (e opzionalmente la riga originale)severity: almeno error e warningRendi gli errori azionabili. Includi cosa ti aspettavi e cosa hai effettivamente visto, e quando possibile mostra un esempio che passerebbe. Per esempio: expected YYYY-MM-DD, got 03/12/24.
Anche se ritorni una lista piatta, includi dati sufficienti per raggruppare errori per riga e per campo. Molte UI vogliono “Riga 12 ha 3 problemi” e poi evidenziare ogni colonna. I team di supporto apprezzano il raggruppamento perché i pattern diventano ovvi (per esempio, ogni riga manca country).
Una risposta compatta potrebbe sembrare così:
{
"importId": "imp_123",
"status": "failed",
"errors": [
{
"code": "INVALID_DATE",
"message": "Signup date must be in YYYY-MM-DD.",
"path": "signup_date",
"row": 12,
"severity": "error",
"expected": "YYYY-MM-DD",
"actual": "03/12/24"
},
{
"code": "UNKNOWN_FIELD",
"message": "Column 'fav_colour' is not recognized.",
"path": "fav_colour",
"row": 1,
"severity": "warning"
}
]
}
Pianifica la localizzazione senza cambiare i code degli errori. Mantieni code neutrale rispetto alla lingua e durevole, e tratta message come testo sostituibile. Se in seguito aggiungi messageKey o messaggi tradotti, i client vecchi possono comunque affidarsi agli stessi codici per filtrare, raggruppare e fare analytics.
Per evitare “import misteriosi”, la risposta della tua API dovrebbe rispondere a due domande: cosa è successo e cosa dovrebbe fare l'utente dopo.
Anche quando ci sono errori, restituisci un riepilogo coerente così UI e strumenti di supporto possono gestire ogni import allo stesso modo.
Includi:
created, updated, skipped, failedtotalRows (o totalRecords per JSON)mode (per esempio: "createOnly", "upsert" o "updateOnly")startedAt e finishedAtcorrelationId che il supporto può richiedereQuel correlationId vale molto. Quando qualcuno segnala “non è stato importato”, puoi trovare l'esecuzione esatta e il report degli errori senza indovinare.
Non riversare 10.000 errori di riga nella risposta. Restituisci un piccolo campione (per esempio 20) che mostri il pattern e fornisci un modo separato per recuperare il report completo se necessario.
Rendi ogni errore specifico e stabile:
Esempio di forma di risposta (successo con alcuni fallimenti di riga):
{
"importId": "imp_01HZY...",
"correlationId": "c_9f1f2c2a",
"status": "completed_with_errors",
"summary": {
"totalRows": 1200,
"created": 950,
"updated": 200,
"skipped": 10,
"failed": 40
},
"errorsSample": [
{
"row": 17,
"field": "email",
"code": "invalid_format",
"message": "Email must contain '@'.",
"value": "maria.example.com"
}
],
"report": {
"hasMore": true,
"nextPageToken": "p_002"
},
"next": {
"suggestedAction": "review_errors"
}
}
Nota il campo next. Anche un payload minimo di successo dovrebbe aiutare il prodotto a procedere: mostrare una schermata di revisione, offrire un retry o aprire la raccolta importata.
Le persone ritentano. Le reti falliscono. Se lo stesso file viene importato due volte, vuoi risultati prevedibili.
Sii esplicito sull'idempotenza: accetta un idempotencyKey (o calcola un hash del file) e restituisci lo stesso importId se la richiesta è una ripetizione. Se la modalità è upsert, definisci la regola di matching (per esempio, “email è la chiave unica”). Se è create-only, per i duplicati restituisci “skipped” e non “created again”.
Se la richiesta è invalida (auth mancante, content type sbagliato, file illeggibile), fallisci subito e restituisci status: "rejected" con una breve lista di errori. Se il file è valido ma ha problemi a livello di riga, trattalo come un job completato con failed > 0 così gli utenti possono correggere e ricaricare senza perdere il riepilogo.
Un'abitudine utile: fai scrivere al modello il contratto in un formato strutturato, non in prosa. Le “frasi utili” spesso saltano dettagli come regole di trim, valori default e se una cella vuota significa “missing” o “empty”.
Usa un prompt che forzi una tabella che un umano può revisionare rapidamente e uno sviluppatore può trasformare in codice. Chiedi per ogni campo la regola, esempi di pass e fail, e una nota esplicita per qualsiasi ambiguità (per esempio, empty string vs null).
You are helping design an importer for CSV and JSON.
Output a Markdown table with columns:
Field | Type | Required? | Normalization | Validation rules | Default | Pass examples | Fail examples
Rules must be testable (no vague wording).
Then output:
1) A list of edge cases to test (CSV + JSON).
2) Proposed test names with expected result (pass/fail + error code).
Finally, list any contradictions you notice (required vs default, min/max vs examples).
Dopo la prima bozza, stringila chiedendo un esempio positivo e uno negativo per regola. Questo spinge la copertura di angoli difficili come stringhe vuote, valori fatti solo di spazi, colonne mancanti, null vs "null", interi molto grandi, notazione scientifica, ID duplicati e campi JSON extra.
Per uno scenario concreto, immagina di importare “customers” da CSV: email è richiesto, phone è opzionale e signup_date di default è oggi se mancante. Il modello dovrebbe segnalare una contraddizione se dichiari anche “signup_date è obbligatorio”. Dovrebbe proporre test come import_customers_missing_email_returns_row_error e specificare il codice errore e la forma del messaggio che restituisci.
Fai un altro passaggio prima dell'implementazione: chiedi al modello di ripetere le regole come checklist e indicare dove default, campi obbligatori e normalizzazione potrebbero entrare in conflitto. Questo step di revisione cattura molti comportamenti che genererebbero ticket.
I fuzz test fermano i “file strani” dal diventare ticket di supporto. Parti da un piccolo set di file validi, poi genera migliaia di variazioni leggermente difettose e assicurati che il tuo importer reagisca in modo sicuro e chiaro.
Inizia con un piccolo corpus di esempi validi che rappresentino l'uso reale: il file valido più piccolo, un file tipico e un file grande. Per JSON, includi un oggetto singolo, molti oggetti e strutture nidificate se le supporti.
Poi aggiungi un mutator automatizzato che modifica una cosa per volta. Mantieni le mutazioni riproducibili loggando il seed random così puoi riprodurre i fallimenti.
Dimensioni di fuzz che catturano la maggior parte dei problemi reali:
Non fermarti alla sintassi. Aggiungi anche fuzz semantico: scambia campi simili (email vs username), date estreme, ID duplicati, quantità negative o valori che violano enum.
I fuzz test aiutano solo se i criteri di passaggio sono severi. Il tuo importer non dovrebbe mai crashare o bloccarsi, e gli errori dovrebbero essere coerenti e azionabili.
Un set pratico di regole di passaggio:
Esegui questi test in CI ad ogni modifica. Quando trovi un fallimento, salva il file esatto come fixture e aggiungi un test di regressione così non ritorna.
Se usi Claude Code per questo lavoro, fagli generare fixture seed che corrispondono al tuo contratto, un piano di mutazione e gli output di errore attesi. Tu scegli ancora le regole, ma ottieni rapidamente una superficie di test ampia, specialmente per quoting CSV e angoli JSON.
La maggior parte dei ticket di import proviene da regole poco chiare e feedback inutili.
Una trappola comune è il parsing “best effort” non documentato. Se il tuo importer tronca spazi in modo silenzioso, accetta virgole e punto e virgola, o indovina formati di data, gli utenti costruiscono workflow intorno a queste supposizioni. Poi un piccolo cambiamento, o un generatore di file diverso, rompe tutto. Scegli il comportamento, documentalo e testalo.
Un altro problema ricorrente è il messaggio generico. “Invalid CSV” o “Bad request” costringe gli utenti a indovinare. Caricano lo stesso file cinque volte e il supporto alla fine chiede comunque il file. Gli errori dovrebbero puntare a riga, campo, una ragione chiara e un codice stabile.
Rigettare tutto il file per una riga sbagliata è spesso doloroso. A volte è corretto (es. import finanziari dove i dati parziali sono pericolosi). Molti import business possono proseguire e riportare un riepilogo, purché tu offra un'opzione esplicita come strict mode vs partial import.
I problemi di encoding di testo creano ticket ostinati. UTF-8 è il default giusto, ma i CSV reali spesso contengono BOM, virgolette tipografiche o spazi non separabili copiati dai fogli. Gestiscili in modo coerente e segnala cosa hai rilevato così l'utente può correggere le impostazioni di esportazione.
Infine, cambiare i codici errore tra release rompe client e automazioni. Migliora il wording se vuoi, ma mantieni i code e i significati stabili. Versionali solo quando è strettamente necessario.
Trappole da prevenire fin da subito:
Esempio: un cliente esporta un CSV da Excel, che aggiunge un BOM e formatta le date come 03/04/2026. Il tuo importer indovina MM/DD, ma il cliente si aspettava DD/MM. Se il tuo report di errore include il formato rilevato, il campo esatto e una correzione suggerita, l'utente può sistemarlo senza avanti e indietro.
La maggior parte dei problemi di import sono piccoli disallineamenti tra cosa gli utenti pensano che il file significhi e cosa il tuo sistema accetta. Trattalo come una soglia di rilascio.
code e la stessa forma del messaggio ogni volta.Un test pratico: usa un file intenzionalmente disordinato. Esempio: un CSV dove l'header appare due volte (due colonne "email"), un campo booleano usa "Y" e una data è "03/04/05". Il tuo importer non dovrebbe indovinare. Dovrebbe o applicare una regola di mapping documentata o rifiutare con un errore specifico.
Due controlli che i team spesso saltano:
Primo, verifica che il tuo importer riporti errori con sufficiente dettaglio di posizione per correggere il file sorgente. “Invalid date” non è azionabile. “Riga 42, colonna start_date: expected YYYY-MM-DD, got 03/04/05” lo è.
Secondo, esegui lo stesso file invalido due volte e confronta i risultati. Se l'ordine degli errori cambia, i codici cambiano o i numeri di riga scivolano, gli utenti perdono fiducia. Il comportamento deterministico è noioso, ed è questo lo scopo.
Un import reale comune sono ordini clienti provenienti da un'esportazione di spreadsheet. Qualcuno esporta un CSV da un sistema vecchio, lo modifica in Excel e poi lo carica. La maggior parte dei ticket succede quando l'importer “sistema” i dati in modo silenzioso, o quando il messaggio di errore non dice cosa cambiare.
Immagina un file chiamato orders.csv con colonne: order_id,customer_email,order_date,currency,total_amount.
Ecco tre righe realisticamente sbagliate (come le vedrebbe l'utente):
order_id,customer_email,order_date,currency,total_amount
A-1001,[email protected],2026-01-05,USD,129.99
A-1002,not-an-email,01/06/2026,USD,49.00
,[email protected],2026-01-07,US, -10
La riga 2 ha un'email non valida e una data ambigua. La riga 3 manca order_id, ha un codice valuta non supportato (US invece di USD) e un importo negativo.
Se la tua API restituisce errori, mantieni la forma coerente e specifica. Ecco un esempio di risposta che supporta il partial success:
{
"correlation_id": "imp_20260109_7f3a9d",
"import_id": "ord_01HZZ...",
"status": "partial_success",
"summary": {
"total_rows": 3,
"imported_rows": 1,
"failed_rows": 2
},
"errors": [
{
"row_number": 2,
"field": "customer_email",
"code": "invalid_email",
"message": "Email must contain a valid domain.",
"value": "not-an-email"
},
{
"row_number": 2,
"field": "order_date",
"code": "invalid_date_format",
"message": "Use ISO-8601 (YYYY-MM-DD).",
"value": "01/06/2026"
},
{
"row_number": 3,
"field": "order_id",
"code": "required",
"message": "order_id is required.",
"value": ""
},
{
"row_number": 3,
"field": "currency",
"code": "unsupported_currency",
"message": "Allowed values: USD, EUR, GBP.",
"value": "US"
},
{
"row_number": 3,
"field": "total_amount",
"code": "must_be_positive",
"message": "total_amount must be greater than 0.",
"value": " -10"
}
],
"retry": {
"mode": "upload_failed_only",
"failed_row_numbers": [2, 3]
}
}
Il partial success è importante perché gli utenti non dovrebbero dover ricaricare l'intero file. Un flusso di retry semplice è: correggi solo le righe fallite, esporta un piccolo CSV contenente le righe 2 e 3 e ricaricalo. Il tuo importer dovrebbe trattare questo come idempotente quando order_id è presente, così il “retry” aggiorna gli stessi record invece di crearne di duplicati.
Per il supporto, correlation_id è la via più veloce per la diagnosi. Un agente di supporto può chiedere quel valore singolo, trovare l'esecuzione di import nei log e confermare se il parser ha visto colonne extra, un delimitatore sbagliato o un encoding inatteso.
Passi successivi per rendere questo ripetibile:
La maggior parte dei problemi deriva da dati reali disordinati, non da “codice sbagliato”. I problemi CSV riguardano in genere la forma (header, delimitatore, quoting, encoding), mentre i problemi JSON riguardano il più le informazioni (tipi, null vs vuoto, nidificazioni inattese). Tratta entrambi come input non attendibili e valida rispetto a un contratto esplicito.
Definisci tre esiti in anticipo:
Scegli un comportamento predefinito (molti prodotti optano per partial) e mantienilo coerente in UI e API.
Scrivi un import contract prima di implementare la validazione:
Questo evita sorprese del tipo “funzionava ieri” quando il comportamento cambia.
Preferisci un formato univoco per campo (ad esempio date come YYYY-MM-DD). Se accetti varianti, specifica esplicitamente quali (ad esempio accetta true/false/1/0, ma non tutte le congetture prodotte dai fogli di calcolo). Evita di indovinare date ambigue come 01/02/03; richiedi il formato ISO o rigetta con un errore chiaro.
Decidi:
Se gli utenti possono ritentare import, combina questa logica con l'idempotenza così lo stesso upload non crea duplicati.
Usa livelli invece di un unico grande validate():
Restituisci una forma stabile di errore con:
Restituisci sempre un riepilogo coerente, anche quando ci sono errori:
Supporta i retry esplicitamente:
idempotencyKey (o usa l'hash del file)importId esistente se la richiesta è ripetutaSenza questo, i retry degli utenti possono creare record duplicati.
Inizia con pochi file seed buoni, poi genera molte mutate piccole (una modifica alla volta):
NaN/Infinity in JSON)Un test di fuzz “passa” quando l'importer non va in crash/hang e restituisce errori deterministici e utilizzabili.
Le regole piccole e nominate sono più facili da testare e modificare.
code (identificatore stabile)message (leggibile dall'utente)path/field (nome colonna o JSON pointer)row/line (per CSV)severity (error vs warning)Rendilo azionabile includendo l'aspettativa e il valore effettivo quando possibile.
created, updated, skipped, failed, più totalRows/totalRecordsstatus (success, rejected, completed_with_errors)startedAt, finishedAt)correlationId per support/debugPer file grandi, includi un piccolo errorsSample e un modo per recuperare il report completo in seguito.