Claude Code dla poprawności importu/eksportu danych: zdefiniuj reguły walidacji, spójne formaty błędów i fuzz testy dla importów CSV/JSON, by zredukować trudne tickety wsparcia.

Importy rzadko zawodzą dlatego, że kod jest „zły”. Zawodzą, bo dane z życia są brudne, niespójne i wygenerowane przez osoby, które nie znały twoich założeń.
Problemy z CSV dotyczą zwykle kształtu i formatowania. Problemy z JSON dotyczą raczej znaczenia i typów. Oba formaty mogą psuć się w sposób wyglądający na drobny, a dający mylące efekty.
Te problemy pojawiają się w zgłoszeniach do supportu raz po raz:
Poprawność to nie tylko „czy się zaimportowało”. Musisz zdecydować, które rezultaty są dozwolone, bo użytkownicy zauważą ciche błędy częściej niż głośne awarie.
Większość zespołów potrafi zgodzić się na trzy wyniki:
Edge case’y zamieniają się w przeróbki, gdy ludzie nie wiedzą, co poszło nie tak i jak to szybko naprawić. Typowy scenariusz: klient przesyła CSV z 5000 wierszy, importer zwraca „Invalid format”, i robi trzy losowe próby naprawy. To sprowadza się do wielu ticketów i kogoś po twojej stronie próbującego odtworzyć plik lokalnie.
Ustal cele redukujące cykl: mniej powtórek, szybsze naprawy, przewidywalne rezultaty. Zanim napiszesz reguły, zdecyduj, co oznacza „partial” (i czy je dopuszczasz), jak zgłaszać problemy na poziomie wiersza i co użytkownik powinien zrobić dalej (edytować plik, mapować pola, czy wyeksportować poprawioną wersję). Jeśli używasz platformy wspomagającej, takiej jak Koder.ai (koder.ai), żeby szybko generować walidatory i testy, kontrakt importu i tak utrzyma spójność zachowania w miarę rozwoju produktu.
Zanim napiszesz pojedynczą regułę walidacyjną, ustal, co oznacza „prawidłowe wejście” dla twojego produktu. Większość błędów importu to niezgodność oczekiwań między tym, co użytkownik przesyła, a tym, co system zakłada w sposób ukryty.
Zacznij od formatów i bądź jednoznaczny. „CSV” może znaczyć przecinek lub średnik, wiersz nagłówka albo nie, UTF-8 albo „co wygenerował Excel”. Dla JSON zdecyduj, czy akceptujesz pojedynczy obiekt, tablicę rekordów, czy JSON Lines (jeden obiekt JSON na linię). Jeśli akceptujesz zagnieżdżony JSON, zdefiniuj, które ścieżki odczytujesz, a które ignorujesz.
Następnie zablokuj kontrakt pól. Dla każdego pola zdecyduj, czy jest wymagane, opcjonalne, czy opcjonalne z wartością domyślną. Wartości domyślne są częścią kontraktu, a nie szczegółem implementacyjnym. Jeśli country brakuje, czy domyślnie zapisujesz puste, wybierasz konkretny kraj, czy odrzucasz wiersz?
Zachowanie parsera to miejsce, gdzie „tolerancyjne” importy tworzą długoterminowe problemy. Zdecyduj z góry, jak rygorystyczni jesteście w kwestii obcinania spacji, normalizacji wielkości liter i akceptowania wariantów jak "yes"/"true"/"1". Tolerancja jest w porządku, jeśli jest przewidywalna i udokumentowana.
Duplikaty to kolejna decyzja kontraktowa wpływająca na poprawność i zaufanie. Zdefiniuj, co liczy się jako duplikat (ten sam email, ten sam external_id, ta sama kombinacja pól), gdzie wykrywasz duplikaty (w pliku, w stosunku do istniejących danych, albo obie) i co robisz, gdy wystąpią (zachowaj pierwszy, zachowaj ostatni, scal, albo odrzuć).
Lista kontrolna kontraktu, którą możesz wkleić do specyfikacji:
Przykład: import „customers”. Jeśli email jest kluczem unikalnym, zdecyduj, czy " [email protected] " równa się "[email protected]", czy brak email jest akceptowany, jeśli istnieje external_id, oraz czy duplikaty wewnątrz pliku mają być odrzucone nawet gdy baza nie ma zgodności. Kiedy kontrakt jest ustalony, spójne zachowanie w UI i API jest dużo łatwiejsze, niezależnie od tego, czy implementujesz to w Koder.ai czy gdzie indziej.
Bałagan przy importach często zaczyna się od jednej gigantycznej funkcji validate(). Czystsze podejście to warstwowe reguły z jasnymi nazwami i małymi funkcjami. To ułatwia przegląd zmian i pisanie testów.
Zacznij od reguł na poziomie pola: sprawdzeń, które pojedyncza wartość może zdać lub oblać samodzielnie (typ, zakres, długość, dozwolone wartości, regex). Trzymaj je nudnymi i przewidywalnymi. Przykłady: email pasuje do podstawowego wzorca email, age jest liczbą całkowitą między 0 a 120, status należy do active|paused|deleted.
Dodawaj reguły międzypolowe tylko tam, gdzie mają znaczenie. Te sprawdzenia zależą od wielu pól i tam kryją się błędy. Klasyczne przykłady: startDate musi być przed endDate, albo total równa się subtotal + tax - discount. Pisz te reguły tak, by wskazywały konkretne pola, a nie tylko „rekord nieprawidłowy”.
Oddziel reguły na poziomie rekordu od reguł na poziomie pliku. Reguła rekordowa sprawdza jeden wiersz (CSV) lub jeden obiekt (JSON). Reguła plikowa sprawdza całe przesłanie: czy wymagane nagłówki istnieją, czy klucz unikalny się nie powtarza, czy liczba kolumn odpowiada oczekiwaniom, lub czy plik deklaruje obsługiwaną wersję.
Normalizacja powinna być jawna, a nie „magiczna”. Zdecyduj, co normalizujesz przed walidacją i udokumentuj to. Typowe przykłady: obcinanie spacji, normalizacja Unicode (by wizualnie identyczne znaki porównywały się tak samo) oraz formatowanie numerów telefonów do jednego spójnego formatu przechowywania.
Struktura, która pozostaje czytelna:
Wersjonuj swoje reguły. Dodaj schemaVersion (lub profil importu) w pliku lub żądaniu API. Gdy zmienisz, co oznacza „prawidłowe”, nadal będziesz mógł ponownie zaimportować starsze eksperty używając starej wersji. To jedna decyzja, która zapobiega wielu ticketom „wczoraj działało".
Dobry importer kończy się w pomocny sposób. Niejasne błędy prowadzą do losowych prób i niepotrzebnej pracy supportu. Jasny format błędów pomaga użytkownikom szybko naprawić plik i pomaga wam poprawiać walidację bez łamania klientów.
Zacznij od stabilnego kształtu obiektu błędu i trzymaj go spójnie dla CSV i JSON. Możesz użyć Claude Code do zaproponowania schematu i kilku realistycznych przykładów, a potem zamrozić go jako część kontraktu importu.
Traktuj każdy błąd jako mały rekord z polami, które się nie zmieniają. Treść komunikatu może ewoluować, ale code i path powinny być trwałe.
code: krótki, stabilny identyfikator, np. REQUIRED_MISSING albo INVALID_DATEmessage: przyjazne zdanie dla UIpath: gdzie jest problem (JSON pointer jak /customer/email, albo nazwa kolumny jak email)row lub line: dla CSV podaj numer wiersza zaczynając od 1 (opcjonalnie oryginalny wiersz)severity: przynajmniej error i warningUczyń błędy działającymi. Dołącz, co oczekiwano i co faktycznie było widziane, a jeśli możliwe, pokaż przykład, który by przeszedł. Na przykład: oczekiwano YYYY-MM-DD, otrzymano 03/12/24.
Nawet jeśli zwracasz płaską listę, dołącz wystarczająco danych, by pogrupować błędy po wierszu i po polu. Wiele interfejsów chce pokazać „Wiersz 12 ma 3 problemy” i podświetlić kolumny. Zespoły wsparcia lubią grupowanie, bo wzorce stają się oczywiste (np. każdy wiersz brakuje country).
Kompaktowa odpowiedź może wyglądać tak:
{
"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"
}
]
}
Zaplanuj lokalizację bez zmiany kodów błędów. Traktuj code jako neutralny językowo i trwały, a message jako pole nadpisywalne. Jeśli później dodasz messageKey lub przetłumaczone komunikaty, starzy klienci nadal mogą polegać na tych samych kodach do filtrowania i analiz.
Aby uniknąć „tajemniczych importów”, odpowiedź API powinna odpowiedzieć na dwa pytania: co się stało i co użytkownik powinien zrobić dalej.
Nawet przy błędach zwracaj spójne podsumowanie, aby UI i narzędzia wsparcia mogły obsłużyć każde przesłanie w ten sam sposób.
Uwzględnij:
created, updated, skipped, failed (liczniki)totalRows (lub totalRecords dla JSON)mode (np. "createOnly", "upsert" lub "updateOnly")startedAt i finishedAt (znaczniki czasu)correlationId, o który support może zapytaćTen correlationId jest tego wart. Gdy ktoś zgłasza „nie zaimportowało się”, znajdziesz dokładny przebieg i raport błędów bez zgadywania.
Nie wrzucaj 10 000 błędów w odpowiedzi. Zwróć małą próbkę (np. 20), która pokazuje wzorzec, i zapewnij osobny sposób na pobranie pełnego raportu w razie potrzeby.
Każdy błąd powinien być specyficzny i stabilny:
Przykładowy kształt odpowiedzi (sukces z błędami w wierszach):
{
"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"
}
}
Zwróć uwagę na pole next. Nawet minimalny payload sukcesu powinien pomóc produktowi ruszyć dalej: pokaż ekran przeglądu, zaoferuj retry, albo otwórz zaimportowaną kolekcję.
Ludzie powtarzają. Sieci padają. Jeśli ten sam plik zostanie zaimportowany dwukrotnie, chcesz przewidywalnych rezultatów.
Bądź jawny co do idempotencji: akceptuj idempotencyKey (lub oblicz hash pliku) i zwróć istniejące importId, jeśli żądanie jest powtórzeniem. Jeśli tryb to upsert, zdefiniuj regułę dopasowania (np. „email jest kluczem unikalnym”). Jeśli to create-only, zwracaj „skipped” dla duplikatów, a nie „created again”.
Jeśli całe żądanie jest nieprawidłowe (zły auth, nieobsługiwany content type, nieczytelny plik), szybko odrzuć i zwróć status: "rejected" z krótką listą błędów. Jeśli plik jest czytelny, ale ma problemy na poziomie wierszy, traktuj go jako ukończone zadanie z failed > 0, żeby użytkownicy mogli poprawić i ponownie przesłać bez utraty podsumowania.
Użyteczny nawyk: każ delegatowi modelu napisanie kontraktu w ustrukturyzowanym formacie, a nie w akapitach. „Pomocne paragrafy” często pomijają szczegóły jak reguły trimowania, wartości domyślne i czy pusty cell znaczy "missing" czy "empty".
Użyj promptu, który wymusi tabelę, którą człowiek szybko sprawdzi, a deweloper przekształci w kod. Poproś o każdej regule: przykłady pass i fail oraz wyraźną notatkę o niejednoznacznościach (np. pusty 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).
Po pierwszym szkicu, doprecyzuj prosząc o po jednym pozytywnym i jednym negatywnym przykładzie na regułę. To wymusza pokrycie trudnych narożników jak puste stringi, wartości z samymi spacjami, brakujące kolumny, null vs "null", bardzo duże liczby, notacja naukowa, duplikaty ID i dodatkowe pola JSON.
Dla konkretnego scenariusza: import „customers” z CSV: email jest wymagany, phone opcjonalny, a signup_date domyślnie dziś, jeśli brak. Model powinien wykryć sprzeczność, jeśli też powiesz, że signup_date jest wymagany. Powinien zaproponować testy typu import_customers_missing_email_returns_row_error i określić kod błędu oraz kształt wiadomości.
Zrób jeszcze jedno przejście przed implementacją: poproś model, aby wypunktował reguły jako checklistę i wskazał miejsca, gdzie wartości domyślne, pola wymagane i normalizacja mogą kolidować. Ten krok przeglądu łapie wiele zachowań, które później generują tickety.
Fuzz testing powstrzymuje „dziwne pliki” przed staniem się zgłoszeniami do supportu. Zacznij od małego zestawu poprawnych plików, potem wygeneruj tysiące lekko uszkodzonych wariantów i upewnij się, że importer reaguje bezpiecznie i jasno.
Zacznij od małego zestawu przykładów reprezentujących realne użycie: najmniejszy prawidłowy plik, typowy plik i duży plik. Dla JSON uwzględnij jeden obiekt, wiele obiektów i zagnieżdżenia, jeśli je wspierasz.
Następnie dodaj automatyczny mutator, który zmienia jedną rzecz naraz. Zachowaj reprodukowalność przez logowanie ziarna losowości, aby można było odtworzyć błędy.
Wymiary fuzzingu, które łapią większość realnych problemów:
Nie zatrzymuj się na składni. Dodaj fuzz semantyczny: zamieniaj pokrewne pola (email vs username), ekstremalne daty, duplikaty ID, ujemne ilości lub wartości łamiące enumy.
Fuzz testy pomagają tylko, jeśli kryteria przejścia są ścisłe. Twój importer nigdy nie powinien padać lub wieszać się, a błędy powinny być spójne i działające.
Praktyczny zestaw kryteriów pass:
Uruchamiaj te testy w CI przy każdej zmianie. Gdy znajdziesz awarię, zachowaj dokładny plik jako fixture i dodaj test regresyjny, żeby to się nie powtórzyło.
Jeśli używasz Claude Code do tej pracy, poproś go o wygenerowanie seed fixtures zgodnych z twoim kontraktem, plan mutacji i oczekiwane wyjścia błędów. Nadal to ty decydujesz o regułach, ale zyskujesz szeroką powierzchnię testową szybko, zwłaszcza dla cytowania w CSV i narożników JSON.
Najwięcej zgłoszeń importu pochodzi z niejasnych reguł i nieużytecznego feedbacku.
Jedna pułapka to „best effort” parsowanie bez dokumentacji. Jeśli importer cicho obcina spacje, akceptuje przecinki i średniki, albo zgaduje formaty dat, użytkownicy budują workflowy wokół tych zgadywań. Mała zmiana lub inny generator plików psuje wszystko. Wybierz zachowanie, udokumentuj i przetestuj.
Kolejny powtarzający się problem to generyczny komunikat o błędzie. „Invalid CSV” czy „Bad request” zmusza użytkownika do zgadywania — wysyła ten sam plik pięć razy, a support i tak prosi o plik. Błędy powinny wskazywać wiersz, pole, jasny powód i stabilny kod.
Odrzucanie całego pliku za jeden zły wiersz też bywa bolesne. Czasem to konieczne (np. importy finansowe), ale wiele biznesowych importów może kontynuować i raportować podsumowanie, pod warunkiem że oferujesz opcję strict mode vs partial import.
Problemy z kodowaniem tekstu generują uporczywe tickety. UTF-8 to dobry domyślny wybór, ale realne CSV zawierają BOM, zawinięte cudzysłowy lub niełamliwe spacje z arkuszy. Obsługuj to konsekwentnie i raportuj, co wykryto, aby użytkownik mógł poprawić ustawienia eksportu.
Na koniec: zmiana kodów błędów między wersjami łamie klientów i automatyzacje. Poprawiaj treść komunikatów, jeśli chcesz, ale zachowuj kody i znaczenia. Wersjonuj tylko wtedy, gdy naprawdę trzeba.
Warto zabezpieczyć się przed:
Przykład: klient eksportuje CSV z Excela, który dodaje BOM i formatuje daty jako 03/04/2026. Twój importer zgaduje MM/DD, ale klient miał DD/MM. Jeśli raport błędu zawiera wykryty format, dokładne pole i proponowaną poprawkę, użytkownik skoryguje eksport bez długiego back-and-forth.
Większość problemów importu to małe niezgodności między tym, co użytkownik myśli, że plik oznacza, a tym, co system akceptuje. Traktuj to jako bramkę wydania.
Praktyczny test: użyj jednego celowo zabałaganionego pliku. Przykład: CSV z podwójnym nagłówkiem (dwa „email”), pole boolean jako „Y” i data „03/04/05”. Twój importer nie powinien zgadywać — albo zastosuj udokumentowane mapowanie, albo odrzuć z konkretnym błędem.
Dwa sprawdzenia, które zespoły często pomijają:
Po pierwsze, upewnij się, że importer raportuje błędy z wystarczającymi informacjami lokalizacyjnymi, by naprawić źródłowy plik. „Invalid date” to za mało. „Wiersz 42, kolumna start_date: oczekiwano YYYY-MM-DD, otrzymano 03/04/05” to jest.
Po drugie, uruchom ten sam nieprawidłowy plik dwa razy i porównaj wyniki. Jeśli kolejność błędów się zmienia, kody się zmieniają lub numery wierszy przesuwają, użytkownicy tracą zaufanie. Deterministyczne zachowanie jest nudne — i to jest jego zaleta.
Częstym realnym importem są zamówienia klientów z eksportu arkusza. Ktoś eksportuje CSV ze starego systemu, edytuje w Excelu, potem wrzuca. Większość ticketów powstaje, gdy importer cicho „poprawia” dane albo gdy komunikat błędu nie mówi, co zmienić.
Wyobraź sobie plik orders.csv z kolumnami: order_id,customer_email,order_date,currency,total_amount.
Oto trzy realistyczne złe wiersze (tak jak widziałby je użytkownik):
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
Wiersz 2 ma niepoprawny email i niejednoznaczny format daty. Wiersz 3 brakuje order_id, ma nieobsługiwany kod waluty (US zamiast USD) i ujemną kwotę.
Jeśli twoje API zwraca błędy, trzymaj kształt spójny i konkretny. Przykładowa odpowiedź wspierająca częściowy sukces:
{
"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]
}
}
Częściowy sukces ma znaczenie, bo użytkownik nie powinien przesyłać całego pliku ponownie. Prosty flow retry: napraw tylko nieudane wiersze, wyeksportuj mały CSV zawierający wiersze 2 i 3 i prześlij ponownie. Twój importer powinien traktować to jako idempotentne, gdy order_id istnieje, więc retry zaktualizuje te same rekordy zamiast tworzyć duplikaty.
Dla wsparcia, correlation_id to najszybsza ścieżka diagnozy. Agent supportu prosi tylko o tę wartość, znajduje przebieg importu w logach i potwierdza, czy parser zobaczył dodatkowe kolumny, zły separator czy nieoczekiwane kodowanie.
Kolejne kroki, które uczynią to powtarzalnym:
Większość awarii wynika z bałaganu w danych, a nie z „złego kodu”. Problemy z CSV zwykle dotyczą kształtu danych (nagłówki, separator, cytowanie, kodowanie), natomiast w JSON chodzi częściej o znaczenie (typy, null kontra pusty tekst, niespodziewane zagnieżdżenia). Traktuj oba formaty jako niezaufane wejście i waliduj je względem jasnego kontraktu.
Zdefiniuj trzy wyniki z góry:
Wybierz domyślne zachowanie (wiele produktów wybiera partial) i trzymaj się go konsekwentnie w UI i API.
Spisz import contract przed zaczęciem walidacji:
To zapobiega sytuacjom „wczoraj działało”, a dziś nie.
Wybierz jeden, niejednoznaczny format na pole (np. daty jako YYYY-MM-DD). Jeśli akceptujesz warianty, zrób to jawnie i przewidywalnie (np. przyjmuj true/false/1/0, ale nie wszystkie zgadywane formaty arkuszy). Unikaj zgadywania dwuznacznych dat typu 01/02/03; wymagaj ISO lub odrzuć z czytelnym błędem.
Zdecyduj:
Jeśli użytkownicy mogą ponawiać importy, połącz to z idempotencją, żeby to samo przesłanie nie tworzyło duplikatów.
Zamiast jednego gigantycznego validate(), zastosuj warstwy:
Zwracaj stabilny kształt błędu z polami:
Zawsze zwracaj spójne podsumowanie, nawet przy błędach:
Wspieraj ponawianie explicite:
idempotencyKey (lub użyj hasha pliku)importId, jeśli żądanie jest powtórzeniemBez tego zwykłe ponowienia mogą tworzyć duplikaty.
Zacznij od kilku prawidłowych plików-wzorów, potem generuj małe, pojedyncze mutacje:
NaN/Infinity w JSON)Test „przechodzi”, jeśli importer nie pada, nie wiesza się i zwraca deterministyczne, użyteczne błędy. Uruchamiaj te testy w CI i dodawaj regresje dla znalezionych przypadków.
Najczęściej zgłaszane problemy wynikają z niejasnych reguł i nieużytecznych komunikatów błędów.
Kilka powtarzających się pułapek:
Jeśli importer zgaduje format dat z „03/04/2026”, ale użytkownik miał na myśli DD/MM, raport z wykrytym formatem i proponowaną poprawką oszczędza wiele ticketów.
Przed wypuszczeniem traktuj importer jako bramkę wydania. Szybkie checklisty:
Małe, nazwane reguły są łatwiejsze w testowaniu i bezpieczniejsze przy zmianach.
code (stabilny identyfikator)message (czytelny dla użytkownika)path/field (nazwa kolumny lub JSON pointer)row/line (dla CSV)severity (error vs warning)Uczyń go działającym: podaj, czego oczekiwano i co faktycznie otrzymano, kiedy to możliwe.
created, updated, skipped, failed, plus totalRows/totalRecordsstatus (success, rejected, completed_with_errors)startedAt, finishedAt)correlationId dla wsparcia i debugowaniaDla dużych plików zwróć próbkę błędów (errorsSample) i sposób na pobranie pełnego raportu.
Przykładowy test: celowo zabałaganiony plik (podwójny nagłówek, boolean jako Y, data 03/04/05) — importer nie powinien zgadywać i powinien albo zastosować udokumentowane mapowanie, albo odrzucić z konkretnym błędem.