Claude Code для корректности импорта/экспорта данных: определите правила валидации, единые форматы ошибок и fuzz‑тесты для CSV/JSON, чтобы сократить тикеты по краевым случаям.

Импорты редко ломаются потому, что код «неправильный». Они ломаются потому, что реальные данные грязные, непоследовательные и созданы людьми, которые не видели ваших предположений.
Проблемы с CSV обычно связаны с формой и форматированием. Проблемы с JSON чаще относятся к смыслу и типам. Оба могут ломаться так, что кажется мелочью, но это даёт запутанные результаты.
Эти случаи часто появляются в тикетах поддержки:
"00123" превращается в 123, true/false становится "yes"/"no")Корректность — это не только «импортировалось ли». Нужно решить, какие исходы допустимы, потому что пользователи замечают молчаливые ошибки чаще, чем громкие падения.
Большинство команд сходятся на трёх исходах:
Крайние случаи превращаются в переработку, когда люди не понимают, что пошло не так и как быстро это исправить. Типичный сценарий: клиент загружает CSV из 5000 строк, импортёр пишет «Invalid format», и он пробует три раза с случайными правками. В итоге — несколько тикетов и кто‑то из вашей команды пытается воспроизвести файл локально.
Поставьте цели, которые сокращают цикл: меньше повторных попыток, быстрее исправления, предсказуемые результаты. До написания правил решите, что значит «частичный» (и разрешаете ли вы его), как вы будете сообщать построчные проблемы и что пользователю делать дальше (править файл, сопоставить поля или экспортировать исправленную версию). Если вы используете платформу вроде Koder.ai (koder.ai) для быстрой генерации валидаторов и тестов, контракт импорта всё равно остаётся тем, что удерживает поведение согласованным по мере развития продукта.
До того как вы напишете хоть одно правило валидации, договоритесь, что значит «валидный ввод» для вашего продукта. Большинство багов при импорте — это несоответствие ожиданий между тем, что пользователи загружают, и тем, что ваша система молча предполагает.
Начните с форматов и будьте предельно явными. «CSV» может означать запятую или точку‑с запятой, строку заголовка или её отсутствие, UTF-8 или «то, что выдал Excel». Для JSON решите, принимаете ли вы один объект, массив записей или JSON Lines (один объект JSON на строку). Если принимаете вложенный JSON — опишите, какие пути вы читаете, а какие игнорируете.
Затем зафиксируйте контракт по полям. Для каждого поля решите: обязательно, опционально или опционально со значением по умолчанию. Значения по умолчанию — часть контракта, а не техническая деталь. Если country отсутствует, вы подставляете пустое, выбираете конкретную страну или отклоняете строку?
Поведение парсинга — это место, где «терпимые» импорты создают долгосрочные проблемы. Решите заранее, насколько строги вы в вопросах обрезки пробелов, нормализации регистра и приёме вариантов вроде "yes"/"true"/"1". Терпимость допустима, если она предсказуема и задокументирована.
Дубликаты — ещё одно контрактное решение, влияющее на корректность и доверие. Определите, что считается дубликатом (тот же email, тот же external_id, или комбинация полей), где вы его обнаруживаете (внутри файла, относительно существующих данных или и там, и там) и что при этом делаете (оставляете первый, последний, объединяете или отклоняете).
Чеклист контракта, который можно вставить в спецификацию:
Пример: импорт «customers». Если email — уникальный ключ, решите, равны ли " [email protected] " и "[email protected]", разрешено ли отсутствие email если есть external_id, и нужно ли отклонять дубликаты внутри файла даже если в базе совпадений нет. Как только контракт зафиксирован, согласованное поведение в UI и API становится гораздо проще, независимо от того, реализуете вы это с помощью Koder.ai или иначе.
Грязные импорты часто начинаются с одного огромного validate()‑функции. Более чистый подход — слоистые правила с понятными именами и маленькими функциями. Это упрощает ревью изменений и написание тестов.
Начните с правил на уровне поля: проверка одного значения, которая может пройти или провалиться сама по себе (тип, диапазон, длина, допустимые значения, regex). Держите их скучными и предсказуемыми. Примеры: email соответствует базовому шаблону email, age — целое от 0 до 120, status — одно из active|paused|deleted.
Добавляйте кросс‑полевые правила только там, где это важно. Такие проверки зависят от нескольких полей, и именно там прячутся ошибки. Классические примеры: startDate должен быть раньше endDate, или total равен subtotal + tax - discount. Пишите такие правила так, чтобы они ссылались на конкретные поля, а не просто помечали «запись невалидна».
Отделяйте правила уровня записи от правил уровня файла. Правило уровня записи проверяет одну строку (CSV) или один объект (JSON). Правило уровня файла проверяет весь загрузочный файл: обязательные заголовки присутствуют, уникальный ключ не повторяется, число колонок совпадает с ожиданием, или файл объявляет поддерживаемую версию.
Нормализация должна быть явной, а не «магической». Решите, что нормализуется до валидации, и задокументируйте это. Обычно это обрезка пробелов, нормализация Unicode (чтобы визуально одинаковые символы сравнивались одинаково) и форматирование телефонов в единый формат хранения.
Структура, которая остаётся читабельной:
Версионируйте правила. Положите schemaVersion (или профиль импорта) в файл или API‑запрос. Когда вы меняете определение «валидного», у вас остаётся возможность реимпортировать старые выгрузки по старой версии. Это одно решение, которое предотвращает множество тикетов «вчера работало».
Хороший импортёр заваливается полезно. Неоднозначные ошибки приводят к случайным повторным заливкам и лишней работе поддержки. Чёткий формат ошибок помогает пользователям быстро исправить файл и даёт вам возможность улучшать валидацию, не ломая клиентов.
Начните со стабильной формы объекта ошибки и держите её одинаковой для CSV и JSON. Можно использовать Claude Code, чтобы предложить схему и примеры, а затем зафиксировать её как часть контракта импорта.
Рассматривайте каждую ошибку как маленькую запись с неизменяемыми полями. Текст сообщения может эволюционировать, но код и местоположение должны оставаться стабильными.
code: короткий стабильный идентификатор, например REQUIRED_MISSING или INVALID_DATEmessage: удобочитаемое предложение для UIpath: где проблема (JSON‑pointer вроде /customer/email или имя колонки email)row или line: для CSV указывайте 1‑основанный номер строки (и опционально оригинальную строку)severity: хотя бы error и warningДелайте ошибки действующими: включайте, что ожидалось и что было фактически, и по возможности приводите пример, который пройдёт. Например: ожидалось YYYY-MM-DD, получили 03/12/24.
Даже если вы возвращаете плоский список, включите данных достаточно для группировки по строкам и полям. Многие UI хотят «Строка 12 имеет 3 проблемы» и подсвечивают соответствующие колонки. Команды поддержки любят группировку — шаблоны становятся очевидными (например, все строки лишены country).
Компактный ответ может выглядеть так:
{
"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"
}
]
}
Планируйте локализацию, не меняя коды ошибок. Держите code нейтральным к языку и долговечным, а message — заменяемым. Позже вы можете добавить messageKey или переводы, но старые клиенты всё равно будут полагаться на те же коды для фильтрации, группировки и аналитики.
Чтобы избежать «загадочных» импортов, ответ API должен отвечать на два вопроса: что произошло и что пользователь должен сделать дальше.
Даже при наличии ошибок возвращайте согласованное резюме, чтобы UI и инструменты поддержки могли одинаково обрабатывать все импорты.
Включайте:
created, updated, skipped, failed — счётчикиtotalRows (или totalRecords для JSON)mode (например: createOnly, upsert или updateOnly)startedAt и finishedAt — временные меткиcorrelationId, который поддержка может запроситьЭтот correlationId стоит внедрить — когда кто‑то пишет «не импортировалось», вы можете найти конкретный прогон и отчёт об ошибках без догадок.
Не высыпайте 10 000 ошибок строк в ответ. Возвращайте небольшой сэмпл (например, 20), который показывает шаблон, и предоставляйте отдельный способ получить полный отчёт при необходимости.
Делайте каждую ошибку специфичной и стабильной:
Пример формы ответа (успех с некоторыми проваленными строками):
{
"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"
}
}
Обратите внимание на поле next. Даже минимальный успешный ответ должен помогать продукту двигаться дальше: показать экран обзора, предложить повторную загрузку или открыть импортированную коллекцию.
Люди повторяют попытки. Сети падают. Если тот же файл загружен дважды, вы хотите предсказуемое поведение.
Будьте явными по части идемпотентности: принимайте idempotencyKey (или вычисляйте хэш файла) и возвращайте существующий importId, если запрос повторён. Если режим — upsert, определите правило сопоставления (например, «email — уникальный ключ»). Если это create‑only, возвращайте «skipped» для дубликатов, а не «created" снова.
Если весь запрос недействителен (плохая аутентификация, неверный content type, нечитаемый файл), быстро отклоняйте с status: "rejected" и коротким списком ошибок. Если файл валиден, но есть проблемные строки, рассматривайте задачу как завершённую с failed > 0, чтобы пользователи могли исправить и повторно загрузить без потери резюме.
Полезная привычка: заставьте модель писать контракт в структурированном формате, а не в виде абзацев. «Полезные параграфы» часто пропускают детали вроде правил обрезки, значений по умолчанию и того, означает ли пустая ячейка «отсутствует» или «пусто».
Используйте промпт, который вынуждает вывести таблицу, удобную для быстрого просмотра человеком и пригодную для конвертации в код. Просите для каждого поля правила, примеры pass/fail и явную заметку по неоднозначным ситуациям (например, пустая строка 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).
После первого варианта ужесточите требования, попросив по одному положительному и одному отрицательному примеру для каждого правила. Это расширит покрытие трюковых углов: пустые строки, строки только из пробелов, отсутствующие колонки, null vs "null", очень большие целые, научная нотация, дублирующиеся ID и дополнительные поля JSON.
Для конкретного сценария: импорт «customers» из CSV: email обязателен, phone опционален, signup_date по умолчанию — сегодня при отсутствии. Модель должна указать противоречие, если вы одновременно утверждаете «signup_date обязателен». Она должна предложить тесты вроде import_customers_missing_email_returns_row_error и указать код ошибки и форму сообщения, которую вы возвращаете.
Сделайте ещё один проход перед реализацией: попросите модель восстановить правила в виде чеклиста и указать, где значения по умолчанию, обязательность и нормализация могут конфликтовать. Этот шаг часто ловит поведение, которое потом выльется в тикеты поддержки.
Fuzz‑тестирование предотвращает превращение «странных файлов» в тикеты поддержки. Начните с небольшого набора корректных файлов, затем генератором создайте тысячи слегка «ломаных» вариантов и убедитесь, что импортёр реагирует безопасно и понятно.
Начните с минимального набора валидных примеров, которые представляют реальное использование: самый маленький валидный файл, типичный файл и большой файл. Для JSON включите один объект, много объектов и вложенные структуры, если вы их поддерживаете.
Далее добавьте автоматический мутатор, который меняет по одному параметру. Держите мутации воспроизводимыми, логируя случайное зерно, чтобы воспроизвести ошибки.
Измерения фазаф‑мутаций, которые ловят большинство проблем:
Не останавливайтесь на синтаксисе. Добавьте семантический fuzz: поменяйте похожие поля местами (email vs username), экстремальные даты, дублированные ID, отрицательные количества или значения, нарушающие enum.
Fuzz‑тесты помогают только при жёстких критериях прохождения. Ваш импортёр никогда не должен падать или зависать, а ошибки должны быть последовательными и полезными.
Практический набор правил прохождения:
Запускайте эти тесты в CI при каждом изменении. Когда вы находите сбой, сохраните точный файл как фикстуру и добавьте регрессионный тест, чтобы он больше не возвращался.
Если вы используете Claude Code для этого, попросите модель сгенерироватьseed‑фикстуры, план мутаций и ожидаемые выходы ошибок. Вы всё ещё решаете правила, но быстро покрываете большую поверхность тестов, особенно для кавычек в CSV и краёв JSON.
Большинство тикетов по импорту происходит из‑за неясных правил и неинформативных сообщений.
Одна распространённая ловушка — «best effort» парсинг, который не задокументирован. Если импортёр тихо обрезает пробелы, принимает запятые и точки‑с‑запятыми или угадывает форматы дат, пользователи выстраивают рабочие процессы вокруг этих догадок. Малое изменение или другой генератор файлов ломают всё. Выберите поведение, задокументируйте и протестируйте его.
Другой частый виновник — общее сообщение об ошибке. «Invalid CSV» или «Bad request» заставляет пользователей гадать. Они загружают тот же файл пять раз, а поддержке приходится просить файл. Ошибки должны указывать строку, поле, конкретную причину и стабильный код.
Отклонение всего файла из‑за одной плохой строки тоже обычная боль. Иногда это правильно (финансовые импорты, где частичный успех опасен). Многие бизнес‑импорты могут продолжать и выдавать сводку, если вы явно предлагаете режим strict vs partial.
Проблемы с кодировкой текста создают упорные тикеты. UTF-8 — правильный дефолт, но реальные CSV часто содержат BOM, фигурные кавычки или неразрывные пробелы, скопированные из таблиц. Обрабатывайте это последовательно и указывайте, что вы обнаружили, чтобы пользователь мог изменить настройки экспорта.
Наконец, изменение кодов ошибок между релизами ломает клиентов и автоматизации. Улучшайте формулировки, если нужно, но держите коды и их смысл стабильными. Версионируйте только когда действительно необходимо.
Стоит защититься от следующих ловушек заранее:
Пример: клиент экспортирует CSV из Excel, который добавляет BOM и форматирует даты как 03/04/2026. Ваш импортёр угадывает MM/DD, а клиент ожидал DD/MM. Если в отчёте ошибок вы укажете обнаруженный формат, точное поле и предложенное исправление, пользователь сможет поправить экспорт без лишних кругов.
Большинство проблем импорта — небольшие расхождения между тем, что пользователи думают про файл, и тем, что система принимает. Рассматривайте это как релизный критерий.
true/false, 0/1, yes/no), дат и временных меток (правила по часовым поясам). Предпочитайте один принятый формат на поле.null и пустой текст.Практический тест: возьмите преднамеренно «мусорный» файл. Например: заголовок повторяется дважды (две колонки email), булево поле использует Y, а дата в виде 03/04/05. Ваш импортёр не должен угадывать. Он либо применит задокументированное правило маппинга, либо отклонит с конкретной ошибкой.
Два проверки, которые команды часто пропускают:
Во‑первых, убедитесь, что импортёр сообщает ошибки с достаточной детализацией местоположения, чтобы исправить исходный файл. «Invalid date» неинформативно. «Строка 42, колонка start_date: ожидалось YYYY-MM-DD, получили 03/04/05» — уже можно исправить.
Во‑вторых, прогоните один и тот же неверный файл дважды и сравните результаты. Если порядок ошибок меняется, коды меняются или номера строк сдвигаются — пользователи теряют доверие. Детерминированность — это скучно, и в этом её сила.
Обычный реальный импорт — заказы клиентов из экспортированной таблицы. Кто‑то экспортирует CSV из старой системы, правит в Excel, затем загружает. Большинство тикетов возникает, когда импортёр молча «исправляет» данные, или сообщение об ошибке не говорит, что менять.
Представьте файл orders.csv со столбцами: order_id,customer_email,order_date,currency,total_amount.
Вот три реалистичных ошибочных строки (как их увидит пользователь):
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
В строке 2 — неверный email и неоднозначный формат даты. В строке 3 — отсутствует order_id, неподдерживаемый код валюты (US вместо USD) и отрицательная сумма.
Если API возвращает ошибки, держите форму ответа согласованной и конкретной. Пример ответа, поддерживающего частичный успех:
{
"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]
}
}
Частичный успех важен, ведь пользователям не нужно заново загружать весь файл. Простой поток повторной попытки: исправить только проваленные строки, экспортировать маленький CSV со строками 2 и 3 и повторно загрузить. Импортёр должен обрабатывать это идемпотентно, если order_id присутствует, чтобы «повтор» обновлял те же записи, а не создавал дубликаты.
Для поддержки correlation_id — самый быстрый путь к диагностике. Агент поддержки просит единственное значение, находит прогон в логах и подтверждает, видел ли парсер лишние колонки, неверный разделитель или неожиданную кодировку.
Дальнейшие шаги, которые делают процесс повторяемым:
Большинство сбоев происходят из-за «грязных» данных, а не из-за «плохого кода». Проблемы с CSV чаще связаны с формой (заголовки, разделитель, кавычки, кодировка), тогда как с JSON — с смыслом (типы, null vs пустая строка, неожиданная вложенность). Рассматривайте оба формата как недоверенный ввод и валидируйте по явному контракту.
Определите три возможных исхода заранее:
Выберите поведение по умолчанию (многие продукты выбирают partial) и сделайте его единообразным в UI и API.
Пропишите import contract до написания правил валидации:
Это предотвращает сюрпризы «вчера работало, сегодня нет».
Предпочитайте один однозначный формат на поле (например, даты в YYYY-MM-DD). Если вы принимаете варианты, оговорите их явно и предсказуемо (например, true/false/1/0, но не все «угадайки» из таблиц). Избегайте интерпретации неоднозначных дат вроде 01/02/03 — либо требуйте ISO, либо возвращайте понятную ошибку.
Решите заранее:
Комбинируйте это с идемпотентностью — тогда повторная загрузка не создаст дубликатов.
Разбейте валидацию на слои вместо одного большого validate():
Возвращайте стабильную форму ошибки с полями:
Всегда возвращайте согласованное резюме, даже при ошибках:
Поддерживайте повторные попытки явно:
idempotencyKey (или используйте хэш файла)importId для повторного запросаИначе обычные повторные загрузки могут создать дубликаты.
Начните с небольшого набора валидных файлов-образцов, затем генерируйте мутации по одной в каждом тесте:
NaN/Infinity в JSON)Тест считается пройденным, если парсер не падает/не зависает и возвращает детерминированные, понятные ошибки.
Малые именованные правила легче тестировать и безопаснее менять.
code (стабильный идентификатор)message (для человека)path/field (имя колонки или JSON-путь)row/line (для CSV)severity (error vs warning)Делайте ошибки действующими: где возможно, указывайте что ожидалось и что получили.
created, updated, skipped, failed, плюс totalRows/totalRecordsstatus (success, rejected, completed_with_errors)startedAt, finishedAt)correlationId для поддержкиДля больших файлов возвращайте маленькую errorsSample и способ получить весь отчёт позже.