8 min

Claude Code para la corrección de importación/exportación de datos: pasos prácticos

Claude Code para corrección de importación/exportación de datos: define reglas de validación, formatos de error consistentes y pruebas fuzz para importaciones CSV/JSON y reduce tickets por casos límite.

Claude Code para la corrección de importación/exportación de datos: pasos prácticos

Qué falla con las importaciones CSV y JSON

Las importaciones rara vez fallan porque el código esté “mal”. Fallan porque los datos reales son desordenados, inconsistentes y los genera gente que nunca vio tus supuestos.

Los problemas con CSV suelen ser de forma y formato. Los problemas con JSON suelen ser de significado y tipos. Ambos pueden romperse de maneras que parecen menores pero generan resultados confusos.

Estos problemas aparecen una y otra vez en tickets de soporte:

  • Campos requeridos ausentes (email, id, country) o columnas renombradas por alguien que “limpió” el archivo
  • Fechas raras (01/02/03, 2024-13-01, números seriales de Excel, zonas horarias mezcladas)
  • Columnas extra u objetos JSON anidados inesperados añadidos por otra herramienta
  • Deriva de tipos ("00123" se convierte en 123, true/false se convierte en "yes"/"no")
  • Duplicados y casi duplicados (mismo id dos veces, o la misma persona con distinta capitalización)

La corrección no es solo “¿se importó?”. Hay que decidir qué resultados están permitidos, porque los usuarios notan los errores silenciosos más que los fallos ruidosos.

La mayoría de los equipos pueden acordar tres resultados:

  • Aceptado: todas las filas o registros son válidos y se importan
  • Rechazado: no se importa nada porque el archivo no es confiable (encabezados equivocados, codificación mala, JSON ilegible)
  • Parcialmente aceptado: registros válidos se importan, los inválidos se omiten con razones claras

Los casos límite se convierten en reprocesos cuando la gente no puede saber qué salió mal ni cómo arreglarlo rápido. Un escenario común: un cliente sube un CSV de 5.000 filas, el importador dice “Formato inválido”, y vuelve a intentar tres veces con ediciones aleatorias. Eso genera múltiples tickets más alguien de tu equipo intentando reproducir el archivo localmente.

Pon objetivos que reduzcan el ciclo: menos reintentos, arreglos más rápidos, resultados predecibles. Antes de escribir reglas, decide qué significa “parcial” (y si lo permites), cómo informarás problemas a nivel de fila y qué debe hacer el usuario después (editar el archivo, mapear campos o exportar una versión corregida). Si usas una plataforma de generación de código como Koder.ai (koder.ai) para generar validadores y pruebas rápidamente, el contrato de importación sigue siendo lo que mantiene ese comportamiento consistente a medida que el producto evoluciona.

Decide el contrato de importación antes de escribir reglas

Antes de escribir una sola regla de validación, decide qué significa “entrada válida” para tu producto. La mayoría de los errores de importación son expectativas desajustadas entre lo que los usuarios suben y lo que tu sistema asume en silencio.

Empieza por los formatos y sé explícito. “CSV” puede significar coma o punto y coma, una fila de encabezado o no, UTF-8 o “lo que Excel produjo”. Para JSON, decide si aceptas un solo objeto, un array de registros o JSON Lines (un objeto JSON por línea). Si aceptas JSON anidado, define qué rutas lees y cuáles ignoras.

Luego fija el contrato por campo. Para cada campo, decide si es requerido, opcional u opcional con un valor por defecto. Los valores por defecto son parte del contrato, no un detalle de implementación. Si country falta, ¿por defecto dejas vacío, eliges un país específico o rechazas la fila?

El comportamiento de parsing es donde las importaciones “tolerantes” crean problemas a largo plazo. Decide desde el principio cuán estrictos son con recortar espacios, normalizar mayúsculas/minúsculas y aceptar variantes como "yes"/"true"/"1". La tolerancia está bien si es predecible y está documentada.

Los duplicados son otra decisión del contrato que afecta la corrección y la confianza. Define qué cuenta como duplicado (mismo email, mismo external_id, o la misma combinación de campos), dónde lo detectas (dentro del archivo, contra los datos existentes o ambos) y qué haces cuando ocurre (mantener el primero, mantener el último, fusionar o rechazar).

Una lista de verificación del contrato que puedes pegar en una especificación:

  • Formatos y codificación aceptados (delimitador CSV, JSON vs JSON Lines, soporte anidado)
  • Reglas de campo (requerido/opcional/defaults, valores permitidos)
  • Reglas de normalización (trim, mayúsculas/minúsculas, formatos de fecha/número)
  • Definición y manejo de duplicados (alcance de detección, comportamiento elegido)
  • Dónde se valida (cliente, servidor o ambos)

Ejemplo: importar “customers.” Si el email es la clave única, decide si " [email protected] " equivale a "[email protected]", si el email puede faltar cuando existe external_id, y si los duplicados dentro del archivo deben rechazarse incluso si la base de datos no tiene coincidencias. Una vez fijado este contrato, el comportamiento consistente en UI y API es mucho más fácil, ya sea que lo implementes en Koder.ai o en otro lado.

Reglas de validación legibles y fáciles de probar

Las importaciones desordenadas a menudo empiezan con una única función gigante validate(). Un enfoque más limpio es usar reglas por capas con nombres claros y funciones pequeñas. Eso facilita revisar cambios y escribir pruebas.

Empieza con reglas a nivel de campo: verifican que un solo valor pase o falle por sí mismo (tipo, rango, longitud, valores permitidos, regex). Mantenlas aburridas y predecibles. Ejemplos: email coincide con un patrón básico de correo, age es un entero entre 0 y 120, status es uno de active|paused|deleted.

Agrega reglas entre campos solo donde importen. Estas verificaciones dependen de varios campos, y ahí se esconden bugs. Ejemplos clásicos: startDate debe ser anterior a endDate, o total debe ser subtotal + tax - discount. Escribe estas reglas para que puedan señalar campos específicos, no solo “registro inválido”.

Separa reglas a nivel de registro de reglas a nivel de archivo. Una regla a nivel de registro revisa una fila (CSV) o un objeto (JSON). Una regla a nivel de archivo revisa la subida completa: que existan encabezados requeridos, que una clave única no se repita entre filas, que la cuenta de columnas coincida con la esperada, o que el archivo declare una versión soportada.

La normalización debe ser explícita, no “mágica”. Decide qué normalizas antes de validar y documéntalo. Ejemplos comunes incluyen recortar espacios, normalización Unicode (para que caracteres visualmente idénticos comparen igual) y formatear teléfonos a un formato de almacenamiento consistente.

Una estructura que se mantiene legible:

  • Normalize: transformar la entrada cruda en una forma canónica.
  • Validate fields: comprobaciones pequeñas y reutilizables por campo.
  • Validate relationships: comprobaciones entre campos con objetivos claros.
  • Validate file rules: encabezados, duplicados y soporte de versión.
  • Test each layer: pruebas unitarias para cada regla, además de algunos fixtures de extremo a extremo.

Versiona tus reglas. Pon un schemaVersion (o “perfil” de importación) en el archivo o en la petición API. Cuando cambies lo que significa “válido”, aún podrás reimportar exportaciones antiguas usando la versión anterior. Esa decisión evita muchos tickets de “antes funcionaba”.

Diseña un formato de reporte de errores que la gente pueda accionar

Un buen importador falla de forma útil. Los errores vagos llevan a reintentos aleatorios y trabajo de soporte evitable. Un formato de error claro ayuda a los usuarios a arreglar el archivo rápido y te ayuda a mejorar la validación sin romper clientes.

Empieza por una forma de objeto de error estable y mantenla consistente para CSV y JSON. Puedes usar Claude Code para proponer un esquema y algunos ejemplos realistas, y luego fijarlo como parte del contrato de importación.

Un objeto de error estable

Trata cada error como un pequeño registro con campos que no cambian. El mensaje puede evolucionar, pero el código y la ubicación deben permanecer estables.

  • code: un identificador corto y estable como REQUIRED_MISSING o INVALID_DATE
  • message: una frase amigable para mostrar en la UI
  • path: dónde está el problema (puntero JSON tipo /customer/email, o un nombre de columna como email)
  • row o line: para CSV, incluye el número de fila basado en 1 (y opcionalmente la línea original)
  • severity: al menos error y warning

Haz los errores accionables. Incluye qué esperabas y qué viste, y cuando sea posible muestra un ejemplo que pase. Por ejemplo: esperado YYYY-MM-DD, recibido 03/12/24.

Agrupamiento para UI y depuración

Aunque devuelvas una lista plana, incluye suficientes datos para agrupar errores por fila y por campo. Muchas UIs quieren “Fila 12 tiene 3 problemas” y luego resaltar cada columna. Los equipos de soporte gustan de agrupar porque los patrones se hacen evidentes (por ejemplo, todas las filas faltan country).

Una respuesta compacta podría verse así:

{
  "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"
    }
  ]
}

Planifica la localización sin cambiar los códigos de error. Mantén code neutral al idioma y durable, y trata message como texto reemplazable. Si luego añades messageKey o mensajes traducidos, los clientes viejos aún pueden confiar en los mismos códigos para filtrar, agrupar y hacer analítica.

Qué debe devolver la API de importación en éxito y en fallo

Own the importer code
Keep full control by exporting the generated source code and adapting it to your stack.

Para evitar “importaciones misteriosas”, la respuesta de tu API debe contestar dos preguntas: qué pasó y qué debe hacer el usuario después.

Devuelve un resumen claro de la importación (siempre)

Incluso cuando hay errores, devuelve un resumen consistente para que la UI y las herramientas de soporte manejen cada importación de la misma manera.

Incluye:

  • conteos: created, updated, skipped, failed
  • totalRows (o totalRecords para JSON)
  • mode (por ejemplo: "createOnly", "upsert" o "updateOnly")
  • timestamps startedAt y finishedAt
  • un correlationId que soporte pueda pedir

Ese correlationId vale la pena. Cuando alguien reporta “no se importó”, puedes encontrar la ejecución exacta y el reporte de errores sin adivinar.

Incluye errores accionables y una forma de obtener el reporte completo

No vuelques 10.000 errores de fila en la respuesta. Devuelve una muestra pequeña (por ejemplo 20) que muestre el patrón y ofrece una vía separada para recuperar el reporte completo si es necesario.

Haz cada error específico y estable:

  • ubicación: número de fila (CSV) o ruta tipo puntero JSON (JSON)
  • nombre del campo
  • código de error (legible por máquina)
  • mensaje (legible por humanos)
  • valor rechazado (cuidado con datos sensibles)

Ejemplo de forma de respuesta (éxito con algunos fallos de fila):

{
  "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"
  }
}

Fíjate en el campo next. Incluso una carga mínima de éxito debe ayudar al producto a avanzar: mostrar una pantalla de revisión, ofrecer un reintento o abrir la colección importada.

Define idempotencia para que cargas repetidas no creen duplicados

La gente reintenta. Las redes fallan. Si el mismo archivo se importa dos veces, quieres resultados previsibles.

Sé explícito sobre idempotencia: acepta una idempotencyKey (o calcula el hash del archivo) y devuelve el mismo importId si la petición es un duplicado. Si tu modo es upsert, define la regla de coincidencia (por ejemplo, “email es la clave única”). Si es solo crear, devuelve “skipped” para duplicados, no “created again.”

Usa el estado correcto para fallos, pero mantén la forma estable

Si la petición entera es inválida (auth mala, content type incorrecto, archivo ilegible), falla rápido y devuelve status: "rejected" con una lista corta de errores. Si el archivo es válido pero tiene problemas a nivel de fila, trátalo como un trabajo completado con failed > 0 para que los usuarios puedan arreglar y volver a subir sin perder el resumen.

Cómo usar Claude Code para generar reglas y ejemplos

Un hábito útil: hacer que el modelo escriba el contrato en un formato estructurado, no en prosa. “Párrafos útiles” a menudo omiten detalles como reglas de recorte, valores por defecto y si una celda vacía significa “faltante” o “vacía”.

Usa un prompt que fuerce una tabla que una persona pueda revisar rápidamente y que un desarrollador pueda convertir en código. Pide la regla de cada campo, ejemplos de éxito y fracaso, y una nota explícita para cualquier ambigüedad (por ejemplo, cadena vacía 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).

Después del primer borrador, afínalo pidiendo un ejemplo positivo y uno negativo por regla. Eso empuja la cobertura de esquinas complicadas como cadenas vacías, valores solo con espacios, columnas faltantes, null vs "null", enteros muy grandes, notación científica, IDs duplicados y campos JSON extra.

Para un escenario concreto, imagina importar “customers” desde CSV: email es requerido, phone es opcional y signup_date por defecto es hoy si falta. El modelo debe señalar una contradicción si también dices “signup_date es requerido”. Debe proponer pruebas como import_customers_missing_email_returns_row_error y especificar el código de error y la forma del mensaje que devuelves.

Haz una pasada más antes de implementar: pide al modelo que replantee las reglas como una lista de verificación y que señale dónde defaults, campos requeridos y normalización podrían entrar en conflicto. Ese paso de revisión atrapa mucho comportamiento que genera tickets.

Paso a paso: pruebas fuzz para importaciones CSV y JSON

Write the import contract
Use Koder.ai planning mode to turn your import rules into a clear, testable contract.

Las pruebas fuzz evitan que “archivos raros” se conviertan en tickets de soporte. Empieza desde un pequeño conjunto de archivos válidos y genera miles de variaciones ligeramente rotas, y asegúrate de que tu importador reaccione de forma segura y clara.

Construye un set semilla y luego mutalo

Comienza con un pequeño corpus semilla de ejemplos válidos que representen uso real: el archivo válido más pequeño, un archivo típico y un archivo grande. Para JSON, incluye un objeto, muchos objetos y estructuras anidadas si las soportas.

Luego añade un mutador automatizado que toque una cosa a la vez. Mantén las mutaciones reproducibles registrando la semilla aleatoria para poder reproducir fallos.

Dimensiones de fuzz que capturan la mayoría de problemas reales:

  • Problemas de codificación: UTF-8 con BOM, secuencias de bytes inválidas, normalización mixta
  • Problemas de estructura: encabezados faltantes, columnas/fields extra, delimitador equivocado, comas finales
  • Comillas y saltos de línea: comillas sin cerrar, nuevas líneas embebidas, CRLF vs LF, escapado inconsistente
  • Bordes de tipo: enteros gigantes, NaN/Infinity (JSON), cadenas vacías vs null, padding con espacios
  • Tamaño y límites: campos muy largos, muchas filas, claves repetidas, arrays profundamente anidados

No te quedes en la sintaxis. Añade fuzz semántico también: intercambia campos similares (email vs username), fechas extremas, IDs duplicados, cantidades negativas o valores que violen enums.

Define qué significa “pasar” y luego fíjalo

Las pruebas fuzz solo ayudan si los criterios de pase son estrictos. Tu importador nunca debería colapsar o bloquearse, y los errores deben ser consistentes y accionables.

Un conjunto práctico de reglas de pase:

  • No hay crashes, timeouts ni picos de memoria más allá de tu límite
  • Errores claros con punteros a fila/campo (CSV) o rutas JSON
  • Códigos de error estables entre ejecuciones para la misma falla
  • No hay escrituras parciales a menos que explícitamente soportes éxito parcial
  • Importaciones exitosas producen resultados idénticos sin importar formateos inofensivos (como espacios en blanco)

Ejecuta estas pruebas en CI en cada cambio. Cuando encuentres un fallo, guarda el archivo exacto como fixture y añade una prueba de regresión para que no vuelva.

Si usas Claude Code para este trabajo, haz que genere fixtures semilla que coincidan con tu contrato, un plan de mutaciones y los errores esperados. Tú eliges las reglas, pero obtendrás una superficie de prueba amplia rápido, especialmente para comillas en CSV y esquinas de JSON.

Trampas comunes que causan tickets repetidos

La mayoría de los tickets de importación vienen de reglas poco claras y feedback poco útil.

Una trampa común es el parsing “best effort” que no está documentado. Si tu importador recorta espacios en silencio, acepta comas y punto y coma, o adivina formatos de fecha, los usuarios construyen flujos alrededor de esas conjeturas. Luego un pequeño cambio, o un generador de archivos distinto, rompe todo. Elige el comportamiento, documéntalo y pruébalo.

Otro repetidor frecuente es el mensaje genérico. “Invalid CSV” o “Bad request” obliga a los usuarios a adivinar. Suben el mismo archivo cinco veces y soporte acaba pidiendo el archivo de todas formas. Los errores deben apuntar a una fila, un campo, una razón clara y un código estable.

Fallar todo el archivo por una fila mala también es frecuente. A veces eso es correcto (por ejemplo, importes financieros donde datos parciales son peligrosos). Muchas importaciones de negocio pueden continuar y reportar un resumen, siempre que ofrezcas una opción explícita como modo estricto vs importación parcial.

Los problemas de codificación de texto generan tickets persistentes. UTF-8 es el por defecto correcto, pero los CSV reales a menudo incluyen BOM, comillas rizadas o espacios no separables copiados de hojas de cálculo. Manéjalos de forma consistente y reporta lo detectado para que los usuarios puedan arreglar la configuración de exportación.

Por último, cambiar códigos de error entre versiones rompe clientes y automatizaciones. Mejora el wording si quieres, pero mantén códigos y significados estables. Versionéalos solo cuando sea estrictamente necesario.

Trampas a proteger desde el inicio:

  • Parsing “best effort” no documentado que cambia con el tiempo
  • Errores sin punteros a fila/campo y sin código de error estable
  • Importaciones todo-o-nada sin opción estricta vs parcial
  • UTF-8, BOM y caracteres invisibles no manejados consistentemente
  • Cambios en códigos de error que rompen el manejo del cliente

Ejemplo: un cliente exporta un CSV desde Excel, que añade un BOM y formatea fechas como 03/04/2026. Tu importador adivina MM/DD, pero el cliente esperaba DD/MM. Si tu reporte de errores incluye el formato detectado, el campo exacto y una sugerencia de corrección, el usuario puede arreglarlo sin ida y vuelta.

Lista rápida antes de lanzar un importador

Ship a better import API
Create a consistent import API response with counts, status, and correlation IDs using Koder.ai.

La mayoría de los problemas de importación son pequeños desajustes entre lo que los usuarios creen que significa el archivo y lo que tu sistema acepta. Trátalo como una puerta de release.

  • Encabezados y nombres de campo: confirma que las columnas requeridas están presentes, que los nombres coinciden con tus reglas y que los duplicados se rechazan. Decide qué hacer con columnas extra (ignorar, advertir, fallar) y mantenlo consistente.
  • Tipos de datos y formatos: fija cómo parseas enteros vs decimales, booleanos (true/false, 0/1, yes/no), fechas y timestamps (reglas de zona horaria). Prefiere un formato aceptado por campo.
  • Null y valores faltantes: define qué significa cadena vacía por campo. Separa campo faltante, null explícito y texto vacío.
  • Límites de tamaño y seguridad: establece límites para tamaño de archivo, filas máximas y longitud máxima de campo. Falla pronto con un mensaje claro.
  • Errores deterministas: la misma entrada mala debe producir el mismo código de error y forma de mensaje siempre.

Una prueba práctica: usa un archivo intencionalmente desordenado. Ejemplo: un CSV donde el encabezado aparece dos veces (dos columnas “email”), un campo boolean usa “Y” y una fecha es “03/04/05”. Tu importador no debería adivinar. Debe aplicar una regla documentada de mapeo o rechazar con un error específico.

Dos comprobaciones que los equipos suelen saltarse:

Primero, verifica que el importador reporta errores con suficiente detalle de ubicación para corregir el archivo de origen. “Invalid date” no es accionable. “Fila 42, columna start_date: se esperaba YYYY-MM-DD, se recibió 03/04/05” sí lo es.

Segundo, ejecuta el mismo archivo inválido dos veces y compara resultados. Si el orden de errores cambia, los códigos cambian o los números de fila fluctúan, los usuarios pierden confianza. El comportamiento determinista es aburrido, y esa es la idea.

Un ejemplo realista y pasos siguientes

Una importación común son pedidos de clientes que vienen de una exportación de hoja de cálculo. Alguien exporta un CSV de un sistema antiguo, lo edita en Excel y luego lo sube. La mayoría de los tickets ocurren cuando el importador “arregla” datos en silencio o cuando el mensaje de error no dice qué cambiar.

Imagina un archivo llamado orders.csv con columnas: order_id,customer_email,order_date,currency,total_amount.

Aquí hay tres filas malas realistas (como las vería el usuario):

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 fila 2 tiene un email inválido y una fecha ambigua. La fila 3 carece de order_id, tiene un código de moneda no soportado (US en lugar de USD) y un importe negativo.

Si tu API devuelve errores, mantén la forma consistente y específica. Aquí hay un ejemplo de respuesta que soporta éxito parcial:

{
  "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]
  }
}

El éxito parcial importa porque los usuarios no deberían tener que volver a subir todo el archivo. Un flujo de reintento simple es: arreglar solo las filas fallidas, exportar un CSV pequeño con las filas 2 y 3 y volver a subir. Tu importador debería tratar esto como idempotente cuando order_id está presente, así que el “reintento” actualiza los mismos registros en lugar de crear duplicados.

Para soporte, correlation_id es la vía más rápida al diagnóstico. Un agente de soporte puede pedir ese único valor, encontrar la ejecución en logs y confirmar si el parser vio columnas extras, un delimitador equivocado o codificación inesperada.

Pasos siguientes que hacen esto repetible:

  • Usa Claude Code para generar reglas de validación, filas malas de ejemplo y los códigos/mensajes de error que quieres estandarizar.
  • Convierte esos ejemplos en pruebas automatizadas (incluyendo pruebas fuzz) para que nuevos casos límite sean pruebas fallidas, no tickets nuevos.
  • Si construyes con Koder.ai, usa el modo planning para redactar el contrato de importación, generar el validador y las pruebas, y luego itera hasta que la salida de errores se mantenga consistente entre CSV y JSON.

Preguntas frecuentes

Why do CSV and JSON imports fail even when my importer code looks correct?

Most failures come from messy real-world data, not “bad code.” CSV issues are usually about shape (headers, delimiter, quoting, encoding), while JSON issues are usually about meaning (types, null vs empty, unexpected nesting). Treat both as untrusted input and validate against an explicit contract.

Should my importer reject the whole file or allow partial success?

Define three outcomes up front:

  • Accepted: everything imports.
  • Rejected: nothing imports because the file can’t be trusted (wrong headers, unreadable JSON, bad encoding).
  • Partially accepted: valid records import; invalid ones are skipped with clear reasons.

Pick a default (many products choose partial) and make it consistent across UI and API.

What should an “import contract” include?

Write down an import contract before writing validation:

  • Accepted formats (CSV delimiter, header required, UTF-8/BOM handling; JSON array vs object vs JSON Lines)
  • Field rules (required/optional/defaults)
  • Normalization (trim, case rules, date formats)
  • Duplicate definition and handling
  • Where validation happens (client, server, or both)

This prevents “it worked yesterday” surprises when behavior changes.

How do I handle weird dates and type drift (`"00123"` → 123, yes/no booleans)?

Default to one unambiguous format per field (for example, dates as YYYY-MM-DD). If you accept variants, make it explicit and predictable (for example, accept true/false/1/0, but not every spreadsheet guess). Avoid guessing ambiguous dates like 01/02/03; either require ISO format or reject with a clear error.

What’s the best way to deal with duplicates during import?

Decide:

  • What counts as a duplicate (email, external_id, or a composite)
  • Detection scope (within the file, against existing records, or both)
  • The action (keep first, keep last, merge, or reject)

If users can retry imports, combine this with idempotency so the same upload doesn’t create duplicates.

How should I structure validation rules so they stay readable and testable?

Use layers instead of one giant validate():

  • Normalize input into a canonical shape
  • Field-level rules (type, range, enum)
  • Cross-field rules (start < end, totals add up)
  • File-level rules (required headers, duplicate keys, supported version)

Small named rules are easier to test and safer to change.

What should my error reporting format look like?

Return a stable error shape with:

  • code (stable identifier)
  • message (human-friendly)
  • path/field (column name or JSON pointer)
  • row/line (for CSV)
  • severity (error vs warning)

Make it actionable by including what was expected and what was received when possible.

What should my import API return on success and on failure?

Always return a consistent summary, even when there are errors:

  • counts: created, updated, skipped, failed, plus totalRows/totalRecords
  • status (success, rejected, completed_with_errors)
  • timestamps (startedAt, finishedAt)
  • a correlationId for support/debugging

For large files, include a small errorsSample and a way to fetch the full report later.

How do I make imports idempotent so retries don’t create duplicates?

Support retries explicitly:

  • Accept an idempotencyKey (or use a file hash)
  • Return the existing importId if the same request is repeated
  • Define upsert matching rules (for example, email is the unique key)

Without this, normal user retries can double-create records.

How do I fuzz test CSV/JSON imports without making the test suite unmanageable?

Start with a few known-good seed files, then generate many small mutations (one change at a time):

  • encoding (UTF-8 BOM, invalid bytes)
  • structure (missing headers, extra columns, wrong delimiter)
  • quoting/newlines (unclosed quotes, embedded newlines)
  • type edges (huge numbers, empty vs null, NaN/Infinity in JSON)
  • size limits (very long fields, deep nesting)

A fuzz test “passes” when the importer never crashes/hangs and returns deterministic, actionable errors.

Related posts