1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「トラック来てるんだけど」— Claude Codeで作った業務アプリが本番で壊れて、Claude Codeで15分で直した話

1
Posted at

「トラック来てるんだけど」— Claude Codeで作った業務アプリが本番で壊れて、Claude Codeで15分で直した話

はじめに

Claude Codeで業務アプリを作っている非エンジニアです。建設会社で現場代理人をやりながら、自社向けに樹木管理アプリ・日報アプリ・CRMを開発・運用しています。

この記事は、Claude Codeで作ったシステムが出荷当日の朝に壊れて、Claude Codeで15分で復旧した実話です。

「AIで作ったシステムって本番で大丈夫なの?」という疑問への、一つの答えになれば。

技術スタック

  • Next.js (App Router) + TypeScript
  • Supabase (PostgreSQL + Auth + Storage)
  • Vercel (本番ホスティング)
  • PWA (オフラインファースト設計)
  • 開発: Claude Code

朝7時半、電話が鳴る

「QR読めないんだけど。全部!! トラックもう来てるんだけど。」
「え??」 滝汗、、、

391本の樹木を取引先に出荷する日だった。トラックはもう敷地に入っている。

うちの樹木管理アプリでは、1本ずつ木にQRラベルを貼り、出荷時にスキャンして照合する仕組みになっている。それが全部「出荷対象ではありません」と返ってくる。

応急処置: 15分でデプロイ

パニックになりかけたが、やることは明確だった。

「QRが使えないなら、管理番号で検索してタップでピッキングできるようにすればいい。」

Claude Codeを開いて、こう伝えた:

ピッキング画面に手入力モードを追加してほしい。
管理番号を入力して検索 → 該当する樹木が表示される → タップで照合完了。
QRスキャンの代替手段として、既存のピッキング画面に組み込む形で。

Claude Codeが出してきたコードは、既存の PickingView.tsx に検索フォームとフィルタロジックを追加するもの。差分を確認して、ビルドして、git push。Vercelが自動デプロイ。

電話を切ってから15分後、現場で動作確認。積み込み開始。

原因調査: SQLで追跡

トラックが出発した後、原因を調べた。これもClaude Codeと一緒に。

QRスキャンで「出荷対象ではありません」と出る。
QRコードの中身はUUID。DBのtreesテーブルにそのUUIDが存在しない。
でもチェックリスト上にはその木が表示されている。
activity_logsとtreesテーブルを突き合わせて、原因を特定したい。

Claude Codeが生成したSQLを実行して、すぐ分かった。

-- 出荷対象の樹木の作成日を確認
SELECT id, management_number, species, created_at
FROM trees
WHERE shipment_id = 'xxx'
ORDER BY created_at;

-- → 全部 3/17〜18 に作成されている

数日前、単価を修正するために樹木データを削除→再登録していた。再登録で新しいUUIDが発番されたが、木に貼ってあるQRラベルには古いUUIDが印刷されたまま。

QRラベル → UUID_old → DBに存在しない(削除済み)
DB上の木 → UUID_new → QRラベルと紐づかない

「編集」で済むことを「削除→再作成」でやったのが原因だった。

再発防止: 「削除」を構造的に封じる

原因が分かったので、同じ事故が起きない設計に変えた。

1. 「無効」ステータスの新設

// Before: 物理削除
await supabase.from('trees').delete().eq('id', treeId);

// After: 論理削除(ステータス変更)
await supabase
  .from('trees')
  .update({ status: 'invalid' })
  .eq('id', treeId);

削除ボタンを「無効化」に差し替えた。UUIDは生き続ける。QRラベルとの紐づけも壊れない。復帰も可能。

2. QR照合のフォールバック

// UUIDで見つからない場合、management_numberでフォールバック
let tree = await findByUUID(scannedValue);
if (!tree) {
  tree = await findByManagementNumber(scannedValue);
}

QRの中身がUUIDでもmanagement_numberでも照合できるようにした。ラベルの再印刷なしで旧ラベルにも対応。

3. 手入力ピッキング(恒久化)

応急処置で作った手入力モードは、そのまま正式機能として残した。QRスキャンが使えない状況は今後も起き得る(ラベル汚損、カメラ故障、etc.)。代替手段は保険として常に必要。

1日で追加した機能(結果的に8つ)

応急処置から始まって、その日のうちに以下を追加・デプロイした:

# 機能 目的
1 手入力ピッキング QR代替
2 既存出荷に明細追加 現場判断の追加積み込み対応
3 一括取消(3重確認付き) 積まなかった木の除外
4 一括手動確認 未スキャン分の事後処理
5 クライアント紐づけ解除 出荷取消後のデータ整合
6 ポータル手入力受入 取引先側もQR不要に
7 「無効」ステータス 削除の代替
8 QR照合フォールバック UUID + 管理番号の両方で照合

全部Claude Codeとの対話で実装した。1人で。1日で。

非エンジニアがClaude Codeで本番運用して分かったこと

「AIで作ると壊れる」は半分正しい

壊れた。実際に壊れた。391本の出荷が止まりかけた。

でもそれは「AIで作ったから壊れた」んじゃない。「削除と編集の違いを理解せずに運用した」から壊れた。人間がやっても同じミスはする。

「AIで作ると直せる」も同じくらい正しい

もしこれが外注で作ったシステムだったら?

  1. 朝7時半に電話する
  2. 担当者に繋がる(繋がるか?)
  3. 状況を説明する
  4. 見積もりが出る
  5. 修正される

トラックは待ってくれない。

Claude Codeで作ったものは、Claude Codeで直せる。自分で作ったものは、自分で直せる。これが内製の最大のメリットだった。

大事なのは「壊れないシステム」じゃない

完璧なシステムは存在しない。大事なのは壊れた時に止まらないこと

  • QRが使えない → 手入力で回る
  • 削除してしまった → 無効化で代替する(そもそも削除できなくする)
  • 現場判断で追加が発生 → 既存出荷に明細追加で対応する

イレギュラーは必ず起きる。そこからの復旧速度が、システムの本当の価値。

まとめ

項目 内容
障害内容 QRスキャン全滅(UUID不一致)
原因 単価修正のための削除→再登録でUUIDが変わった
復旧時間 15分
復旧方法 Claude Codeで手入力ピッキング機能を追加→Vercelデプロイ
再発防止 物理削除→論理削除(無効ステータス)に変更
追加機能 計8機能を当日中にデプロイ
開発者 非エンジニア1人 + Claude Code

391本、全部トラックに積めました。


建設会社で働きながら、Claude Codeで業務アプリを作っています。
日報アプリ・CRM・樹木管理アプリの実画面 → GenbaLink ポートフォリオ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?