Claude Code によるデータのインポート/エクスポートの正確性:実践ステップ
Claude Code によるデータのインポート/エクスポートの正確性: 検証ルール、安定したエラー形式、CSV/JSON のファズテストを定義してエッジケースのサポートチケットを減らす実践手順。

CSV と JSON のインポートで何が問題になるか
インポートが失敗するのは大抵「コードが間違っている」からではありません。実際のデータが雑で一貫性がなく、あなたの想定を知らない人が作ったファイルだからです。
CSV の問題は形やフォーマットに関することが多く、JSON の問題は意味や型に関することが多いです。どちらも小さな見た目の違いが原因で混乱する結果を招きます。
サポートチケットで繰り返し見かける問題例:
- 必須フィールドがない(email、id、country)や、誰かがファイルを「整形」して列名を変えてしまった\n- 奇妙な日付(01/02/03、2024-13-01、Excelのシリアル日付、混在するタイムゾーン)\n- 別のツールが余計な列や予期しないネストしたJSONオブジェクトを追加している\n- 型のずれ(
"00123"が 123 になる、true/false が"yes"/"no"になる)\n- 重複やほぼ重複(同じ id が二回登場、同じ人が大文字小文字で別扱いになる)
正確性は単に「インポートできたか」だけではありません。どの結果を許容するかを決める必要があります。ユーザーは静かな誤り(サイレントな間違い)を、大きな失敗よりも気にします。
多くのチームは三つの結果に合意できます:
- Accepted: 全ての行やレコードが有効でインポートされる\n- Rejected: ファイルが信頼できないため何もインポートしない(ヘッダー間違い、エンコーディング不良、読めないJSON)\n- Partially accepted: 有効なレコードだけインポートし、無効なものは理由付きでスキップする
エッジケースは、何がどう間違っているか分からないと手戻りになります。よくあるシナリオ: 顧客が5,000行のCSVをアップロードして「Invalid format」とだけ表示され、ランダムな編集で3回再試行する。これが複数のチケットになり、あなたの側で誰かがローカルでそのファイルを再現しようとすることになります。
サイクルを短くする目標を立ててください: 再試行を減らし、修正を早くし、結果を予測可能にする。ルールを書く前に「partial」が何を意味するか(許容するかどうか)、行レベルの問題をどう報告するか、ユーザーが次に何をすべきか(ファイルを編集する、フィールドをマッピングする、修正済みのバージョンをエクスポートする)を決めてください。もし Koder.ai (koder.ai) のようなプラットフォームでバリデータやテストを素早く生成している場合でも、インポート契約がその振る舞いを製品の進化に対して一貫させる役割を果たします。
ルールを書く前にインポート契約を決める
検証ルールを書く前に、あなたのプロダクトにとって「有効な入力」が何かを決めてください。ほとんどのインポートバグは、ユーザーがアップロードするものとシステムが暗黙で想定しているもののギャップです。
まずはフォーマットについて明確にします。"CSV" はカンマかセミコロンか、ヘッダー行の有無、UTF-8 か "Excel が出力したもの" かを意味し得ます。JSON なら単一オブジェクトを受け入れるか、レコード配列を受け入れるか、JSON Lines を受け入れるかを決めてください。ネストしたJSONを受け入れるなら、どのパスを読むか、どのパスを無視するかを定義します。
次にフィールド契約を固定します。各フィールドについて、必須か任意か、既定値があるかを決めてください。デフォルトは実装の詳細ではなく契約の一部です。country が欠けている場合、空にするのか、特定の国をデフォルトにするのか、行を拒否するのかを決めます。
パースの挙動は「寛容な」インポートが長期的な問題を作る場所です。トリムやケース正規化、"yes"/"true"/"1" のようなバリアントを受け入れるかを事前に決めてください。寛容性は予測可能で文書化されているなら問題ありません。
重複も契約で決めるべき点です。何が重複に当たるか(同じメール、同じ external_id、複数フィールドの組合せ)、どこで検出するか(ファイル内、既存データと照合、または両方)、起こったときの挙動(先を保持、後を保持、マージ、拒否)を定義してください。
仕様に貼り付けられるチェックリスト例:\n\n- 受け入れる形式とエンコーディング(CSVの区切り文字、JSON vs JSON Lines、ネスト対応)\n- フィールドルール(必須/任意/デフォルト、許容値)\n- 正規化ルール(トリム、ケース、日付/数値の形式)\n- 重複定義と処理(検出範囲、選んだ挙動)\n- 検証の配置(クライアント、サーバー、または双方)\n
例: "customers" をインポートする場合。email がユニークキーなら、" [email protected] " は "[email protected]" と等しいか、メールが欠けているときに external_id があれば許可するか、ファイル内の重複を拒否するかなどを決めます。契約が決まれば、UI と API の一貫性が保ちやすくなります。
可読でテストしやすい検証ルール
雑なインポートはしばしば巨大な validate() 関数から始まります。より良いアプローチは、明確な名前と小さな関数によるレイヤー化です。変更がレビューしやすくなり、テストも書きやすくなります。
まずフィールドレベルのルールから始めます: 単一の値だけで合否が決まるチェック(型、範囲、長さ、許容値、正規表現)。ここは地味で予測可能に保ちます。例: email が基本的なメールパターンに一致する、age が 0 から 120 の整数である、status が active|paused|deleted のいずれかである。
必要な場合にのみクロスフィールドルールを追加します。これらは複数のフィールドに依存するチェックで、バグが隠れやすい場所です。典型例: startDate は endDate より前でなければならない、total は subtotal + tax - discount に等しいべき、など。これらは「レコード無効」だけでなく、具体的なフィールドを指摘できるように書いてください。
レコードレベルのルールとファイルレベルのルールを分離してください。レコードレベルは1行(CSV)や1オブジェクト(JSON)をチェックします。ファイルレベルはアップロード全体をチェックします: 必須ヘッダーの存在、ユニークキーが行をまたいで重複していないか、列数が期待通りか、ファイルがサポートするバージョンを宣言しているか、など。
正規化は「魔法」にせず明示的に。検証前に何を正規化するかを決めて文書化します。一般的な例はトリム、Unicode 正規化(見た目は同じでも比較できるように)、電話番号の一貫したフォーマット化などです。
読みやすさを保つ構造例:\n\n- Normalize: 生データを正準形に変換する。\n- Validate fields: フィールドごとの小さな再利用可能なチェック。\n- Validate relationships: 明確なターゲットを持つクロスフィールドチェック。\n- Validate file rules: ヘッダー、重複、バージョン対応など。\n- Test each layer: 各ルールのユニットテストと数件のエンドツーエンドフィクスチャ。
ルールにバージョンを付けてください。ファイルやAPIリクエストに schemaVersion(またはインポートプロファイル)を入れることで、何が「有効」かが変わっても古いエクスポートを再インポートできます。これは「昨日は動いたのに」が起きるのを防ぎます。
人が対応できるエラー報告フォーマットの設計
良いインポーターは助けになる失敗をします。曖昧なエラーは無意味な再試行と不要なサポート業務を生みます。明確なエラー形式は、ユーザーが迅速にファイルを修正できるようにし、クライアントを壊さずに検証を改善する助けになります。
安定したエラーオブジェクト形を最初に決め、CSV と JSON で一貫させてください。Claude Code を使ってスキーマと現実的な例を提案させ、インポート契約の一部としてロックダウンできます。
安定したエラーオブジェクト
各エラーを変わらない小さなレコードとして扱ってください。メッセージは進化してよいですが、code と location は安定させます。\n\n- code: REQUIRED_MISSING や INVALID_DATE のような短い安定識別子\n- message: UI 向けの人間フレンドリーな文\n- path: 問題の場所(JSON ポインタ /customer/email や列名 email)\n- row または line: CSV の場合は1始まりの行番号(元の行をオプションで含めても良い)\n- severity: 少なくとも error と warning
エラーは実行可能であるべきです。期待していたものと実際に見えたものを含め、可能なら合格する例を示してください(例: 期待 YYYY-MM-DD、実際 03/12/24)。
UI とデバッグ向けのグルーピング
フラットなリストを返す場合でも、行やフィールドでグルーピングできる十分なデータを含めてください。多くのUIは「行12に3件の問題がある」と表示して各列をハイライトできます。サポートチームはパターンを見るのが好きなので(例えばすべての行で country が欠けている)、グルーピング情報は有益です。
小さなサンプルレスポンス例(実際のJSONは下のコードブロック参照)を用意し、ローカリゼーションの計画を立てる際にも code を言語非依存に保ち、message を置き換え可能にしてください。
{
"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"
}
]
}
後から messageKey や翻訳済みメッセージを追加しても、古いクライアントは同じ code でフィルタや集計ができるようにしてください。
成功/失敗時にインポートAPIが返すべきもの
「何が起きたか」と「ユーザーが次に何をすべきか」の二つの問いに答える形にしてください。
毎回明確なインポートサマリーを返す
エラーがあっても一貫したサマリーを返すことで、UI とサポートツールがすべてのインポートを同じように扱えます。含めるべき項目:\n\n- created, updated, skipped, failed のカウント\n- totalRows(または JSON 用の totalRecords)\n- mode(例: createOnly, upsert, updateOnly)\n- startedAt と finishedAt タイムスタンプ\n- サポートが問い合わせに使える correlationId
この correlationId は非常に有用です。ユーザーが「インポートされなかった」と報告したとき、正確な実行をログからすぐに見つけられます。
実行可能なエラーとフルレポート取得手段を含める
10,000件の行エラーをレスポンスにそのまま突っ込まないでください。パターンを示す小さなサンプル(例えば20件)を返し、完全なレポートを取得する別の手段を提供してください。
各エラーは具体的で安定しているべきです:\n\n- 位置: 行番号(CSV)またはJSONポインタ(JSON)\n- フィールド名\n- エラーコード(機械可読)\n- メッセージ(人間可読)\n- 拒否された値(機微データには注意)
成功(一部行失敗あり)の例(下のコードブロック参照)も用意するとUI設計が楽になります。
{
"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 フィールドのように、最小限の成功ペイロードでも製品が次に何をすべきかを示せると良いです: レビュー画面を出す、再試行を提案する、またはインポート済みコレクションを開く等。
冪等性(idempotency)を定義する
人は再試行します。ネットワークは失敗します。同じファイルが二度インポートされても予測可能な結果にしたいなら、idempotencyKey を受け入れる(またはファイルハッシュを計算する)べきです。同じリクエストなら既存の importId を返します。upsert モードならマッチングルール(例: email がユニークキー)を定義し、create-only モードなら重複に対しては created を増やさず skipped と返すようにしてください。
失敗時は正しいステータスを使い、形は安定させる
リクエスト全体が無効(認証不良、コンテンツタイプ違い、読めないファイル)なら速やかに失敗させ status: "rejected" と短いエラーリストを返してください。ファイル自体は読み取れるが行レベルの問題があるなら、failed > 0 を伴う completed 系のジョブとして扱い、ユーザーが修正して再アップロードできるようにしてください。
Claude Code を使ってルールと例を生成する方法
モデルに契約を構造化された形式で書かせる習慣が有用です。単なる説明文はトリムやデフォルト、空セルの扱いなどの詳細を飛ばしがちです。
人がレビューしやすく、開発者がコードに落とせる表形式を出力するようにモデルに指示してください。各フィールドのルール、合格/不合格の例、あいまいな点への注記を求めます。
プロンプト例(要旨):\n\nYou are helping design an importer for CSV and JSON.\nOutput a Markdown table with columns:\nField | Type | Required? | Normalization | Validation rules | Default | Pass examples | Fail examples\nRules must be testable (no vague wording).\nThen output:\n1) A list of edge cases to test (CSV + JSON).\n2) Proposed test names with expected result (pass/fail + error code).\nFinally, list any contradictions you notice (required vs default, min/max vs examples).
最初のドラフトの後で「ルールごとに正例と負例を一つずつ出す」ように頼むと、空文字、空白のみ、カラム欠落、null と文字列 "null"、非常に大きな整数、指数表記、重複ID、余計なJSONフィールドなどのコーナーケースを強制的にカバーさせられます。
具体例: CSV から "customers" をインポートするシナリオ。email は必須、phone は任意、signup_date は欠けているときに今日をデフォルトにする。モデルはもし signup_date を必須とも定義していれば矛盾を指摘すべきです。import_customers_missing_email_returns_row_error のようなテスト名と、返すべきエラーコードとメッセージ形を提案させると実装が速くなります。
実装前にもう一度、モデルにルールをチェックリストとして言い換えさせ、デフォルトと必須フィールド、正規化が衝突する箇所を指摘させてください。このレビューステップは多くのチケットの原因を事前に摘み取ります。
ステップバイステップ: CSV と JSON のファズテスト
ファズテストは「変なファイル」がサポートチケットになるのを防ぎます。まず小さな正常ファイルセットを用意し、そこから少しずつ壊れた変異を何千件も生成して、インポーターが安全かつ明確に反応するかを確かめます。
シードセットを作り、それを変異させる
まず現実的な使用を表す有効例の小さなシードを用意します: 最小の有効ファイル、典型的なファイル、大きなファイル。JSON では単一オブジェクト、多数のオブジェクト、ネスト構造(許容するなら)を含めます。
次に1つずつ変更する自動ミューテータを用意します。ランダムシードをログに残して再現可能にしてください。
ファズで検出しやすい次元例:\n\n- エンコーディング問題: UTF-8 BOM、無効なバイト列、混在する正規化\n- 構造問題: ヘッダー欠落、余分な列/フィールド、誤った区切り、末尾のカンマ\n- 引用と改行: 閉じられない引用、埋め込み改行、CRLF と LF、エスケープの不整合\n- 型の端: 巨大整数、JSON の NaN/Infinity、空文字と null、空白パディング\n- サイズと制限: 非常に長いフィールド、多数行、繰り返しキー、深いネスト
構文だけで止まらず、意味的なファズも加えてください: 類似フィールドの入れ替え(email と username)、極端な日付、重複ID、負数の数量、列挙値に違反する値など。
「合格」基準を定義し、それを固定する
ファズテストが役立つのは合格基準が厳密に定義されている場合だけです。インポーターはクラッシュやハングを起こさず、エラーは一貫して実行可能であるべきです。
実用的な合格ルール例:\n\n- クラッシュ、タイムアウト、メモリスパイクが発生しない\n- CSV は行/列ポインタ、JSON はパス付きで明確なエラーを返す\n- 同じ失敗に対するエラーコードが実行ごとに安定している\n- 明示的に部分成功をサポートしない限り部分書込みをしない\n- 無害なフォーマット差(空白など)で結果が変わらない
これらを CI で毎回実行してください。失敗したら正確なファイルをフィクスチャとして保存し、回帰テストを追加して再発を防ぎます。
Claude Code を使う場合、契約に合致したシードフィクスチャ、ミューテーションプラン、期待されるエラー出力を生成させるとテスト幅を迅速に広げられます。ルールはあなたが決めますが、CSV の引用や JSON のコーナーケースに対するテスト面は速く手に入ります。
繰り返し発生するサポートチケットを生む落とし穴
ほとんどのインポートチケットは不明瞭なルールと役に立たないフィードバックから来ます。
よくある罠は文書化されていない「ベストエフォート」パーシングです。自動でトリムしたり、カンマとセミコロン両方を受け入れたり、日付形式を推測したりすると、ユーザーはその挙動に依存したワークフローを作ります。挙動を決めて文書化し、テストしてください。
別の繰り返しの原因は汎用的なエラーメッセージです。Invalid CSV や Bad request ではユーザーは推測するしかなく、同じファイルを何度もアップロードします。エラーは行、フィールド、明確な理由、安定したコードを指すべきです。
1行の不良でファイル全体を落とすのも不満の元です。財務系のように全件が正しくなければならないケースは別ですが、多くの業務インポートは明示的な選択肢(strict mode vs partial import)を提供すれば部分的に進められます。
テキストエンコーディングも頑固なチケットの原因です。デフォルトは UTF-8 が正しいですが、実際のCSVはBOMやカールクォート、改行の種類、スプレッドシート由来のノンブレイキングスペースを含むことがあります。これらを一貫して扱い、検出した内容を報告してユーザーにエクスポート設定を直させる情報を与えてください。
最後に、リリース間でエラーコードを変えるとクライアントや自動化が壊れます。文言は改善しても良いですが、コードと意味は安定させ、どうしても変える必要があるときだけバージョン管理してください。
守るべき罠の一覧:\n\n- 時とともに変わる非文書化の「ベストエフォート」パース\n- 行/フィールドのポインタや安定したエラーコードがないエラー\n- 厳格モードと部分受け入れの選択肢がない全件失敗\n- UTF-8、BOM、目に見えない文字の不整合な扱い\n- エラーコードの変更でクライアントが壊れる
例: 顧客がExcelからCSVをエクスポートし、Excel が BOM を付け、日付を 03/04/2026 のようにフォーマットする。インポーターが MM/DD と推測してしまうと顧客は DD/MM を期待しているかもしれません。エラーレポートに検出したフォーマット、対象フィールド、修正案を含めればユーザーは往復を減らして自分で直せます。
インポーターを出荷する前の簡易チェックリスト
多くのインポート問題はユーザーの期待とシステムの受け入れ方の小さな食い違いです。これをリリースゲートとして扱ってください。
- ヘッダーとフィールド名: 必須列が存在するか、名前がルールに一致するか、重複があれば拒否することを確認。余分な列の扱い(無視、警告、失敗)を決め一貫させる。\n- データ型と形式: 整数と小数、ブール(true/false, 0/1, yes/no)、日付とタイムスタンプ(タイムゾーンルール)をどう解釈するかを固定。フィールドごとに一つの受け入れ形式を推奨。\n- NULL と欠損値: フィールドごとに空文字が何を意味するかを定義。欠落フィールド、明示的 null、空文字を区別する。\n- サイズと安全上の制限: ファイルサイズ、最大行数、最大フィールド長を設定し、早めに失敗させる。\n- 決定論的なエラー: 同じ悪い入力は常に同じエラーコードとメッセージ形を返す。
実践的なテスト: 意図的に乱れたファイルを1つ用意する。例: ヘッダーが二回出現する(email が二列)、ブールフィールドが Y を使っている、日付が 03/04/05 になっている。インポーターは推測せず、文書化されたマッピングを適用するか、特定のエラーで拒否するべきです。
チームがよく見落とす2つのチェック:\n\n1) エラーがファイル修正に十分な位置情報を報告しているかを確認する。Invalid date では不十分。Row 42, column start_date: expected YYYY-MM-DD, got 03/04/05 のように具体的にする。\n2) 同じ無効ファイルを二度実行して結果を比較する。エラーの順序やコード、行番号が変わると信頼が失われます。決定論的な挙動は地味ですが重要です。
現実的な例と次のステップ
よくある実例は、古いシステムからエクスポートしたスプレッドシートから来る注文データです。誰かが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行目は無効なメールと曖昧な日付形式を含んでいます。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をエクスポートし再アップロードする。order_id があれば再試行は同じレコードを更新するように設計しておき、重複作成を避けます。
サポート上、correlation_id は最速の診断ルートです。サポート担当はその値を訊くだけでログの該当実行を見つけ、余分な列や誤った区切り、エンコーディングの問題を確認できます。
次のステップ(繰り返し可能にするため):\n\n- Claude Code を使って検証ルール、悪い行の例、標準化したエラーコード/メッセージを生成する。\n- これらの例を自動テスト(ファズテスト含む)に変えて、新しいエッジケースがチケットでなくテストで発見されるようにする。\n- Koder.ai で構築しているなら、planning モードでインポート契約を下書きし、バリデータとテストを生成して、CSV と JSON でエラー出力が一貫するまで反復する。
よくある質問
Why do CSV and JSON imports fail even when my importer code looks correct?
ほとんどの失敗は「コードが悪い」からではなく、現実のデータが雑であることから来ます。CSVは主に形式や形(ヘッダー、区切り文字、引用符、エンコーディング)の問題、JSONは型やnullと空文字、予期しないネストなど「意味」の問題が多いです。両方とも信頼できない入力として扱い、明確な契約に対して検証してください。
Should my importer reject the whole file or allow partial success?
Accepted(完全受け入れ)、Rejected(全件拒否)、Partially accepted(一部受け入れ)の三つの結果を定義してください。
- Accepted: すべてのレコードが有効でインポートされる。
- Rejected: ファイル全体が信用できないため何もインポートされない(ヘッダーが間違っている、JSONが読めない、エンコーディングが悪いなど)。
- Partially accepted: 有効なレコードだけをインポートし、無効なものは理由を付けてスキップする。
デフォルトを選び(多くのプロダクトは部分受け入れを採用)、UIとAPIで一貫させてください。
What should an “import contract” include?
検証ルールを書く前にインポート契約(import contract)を文書化してください。含めるべき内容の例:
- 受け入れる形式(CSVの区切り、ヘッダー要否、UTF-8/BOMの扱い、JSON配列か単体オブジェクトかJSON Linesか)
- フィールドルール(必須/任意/デフォルト)
- 正規化ルール(トリム、ケース変換、日付形式)
- 重複の定義と処理方法
- 検証を行う場所(クライアント、サーバー、または両方)
これで「昨日は動いたのに今日は動かない」が減ります。
How do I handle weird dates and type drift ("00123" → 123, yes/no booleans)?
フィールドごとに受け入れる一つの明確な形式を決めてください(例: 日付は YYYY-MM-DD)。もし複数を許容するなら、どのバリエーションを許すかを明確にして予測可能にします(例: true/false/1/0 は許可するが、スプレッドシートのあらゆる推測を許すわけではない)。曖昧な日付(01/02/03など)は受け入れず、ISO形式を必須にするか、明確なエラーを返す方が安全です。
What’s the best way to deal with duplicates during import?
決めるべきは:
- 何を重複と見なすか(メール、external_id、または複合キー)
- 検出する範囲(ファイル内、既存データと照合、または両方)
- 取るアクション(先に出現したものを保持、後を保持、マージ、拒否)
ユーザーが再実行できるなら、idempotency(冪等性)と合わせて設計し、同じアップロードで重複が二重作成されないようにしてください。
How should I structure validation rules so they stay readable and testable?
巨大な validate() 関数に頼らず、レイヤードなルールに分けると保守性とテストが格段に良くなります。
- Normalize: 生データを正規形に変換
- Field-level rules: 型、範囲、長さ、許容値、正規表現などの小さなチェック
- Cross-field rules: 複数フィールドに依存する検証(
startDate < endDateなど) - File-level rules: ヘッダーの存在、ユニークキーの重複、列数の一致など
各レイヤーをユニットテストし、エンドツーエンドのフィクスチャもいくつか用意してください。スキーマバージョン(schemaVersion)を入れてルールをバージョン管理するのも重要です。
What should my error reporting format look like?
安定したエラーオブジェクト形を返してください。例に含めたいフィールド:
code(例:REQUIRED_MISSING,INVALID_DATE)message(UI向けの人間可読文)path(JSONポインタ/customer/emailや列名email)row/line(CSV用の1始まりの行番号)severity(errorまたはwarning)
何が期待され、何が見えたか(expected/actual)を含めるとユーザーが修正しやすくなります。code は言語非依存の安定した値にして、message をローカライズ可能にしてください。
What should my import API return on success and on failure?
常に一貫したサマリーを返してください(エラーがあっても同様の形にする)。含めるべき項目例:
created,updated,skipped,failedのカウントtotalRows(JSONならtotalRecords)mode(例:createOnly,upsert,updateOnly)startedAt,finishedAtタイムスタンプ- サポート用の
correlationId
大ファイルではエラーを全て返すのではなくサンプル(例: 20件)を返し、完全なレポートを別途取得できるようにしてください。エラーは行番号やJSONパス、フィールド名、コード、メッセージ、必要に応じて拒否された値を含めます(機微なデータには注意)。
How do I make imports idempotent so retries don’t create duplicates?
再試行は発生します。以下の対応を検討してください:
idempotencyKey(またはファイルハッシュ)を受け入れる- 同じリクエストの繰り返しなら既存の
importIdを返す - upsert モードならマッチングルール(例: emailがユニークキー)を明確にする
これがないとユーザーの再アップロードで重複作成が発生します。
How do I fuzz test CSV/JSON imports without making the test suite unmanageable?
まずは少数の良いシードファイルを用意し、それを一点ずつ変異させて多数のケースを作ると良いです。変異候補:
- エンコーディング(UTF-8のBOM、無効バイト)
- 構造(ヘッダー欠落、余分な列、誤った区切り)
- 引用と改行(閉じられない引用、埋め込み改行)
- 型の端(巨大整数、空文字とnull、JSONのNaN/Infinity)
- サイズ制限(非常に長いフィールド、多数の行、深いネスト)
合格基準を厳密に定め、クラッシュやハングがなく、一貫したエラーを返すことをCIで検証してください。失敗したファイルはフィクスチャとして保存し、回帰テストに組み込みます。