捨てたコードは宝の山だった - 失敗から学んで4営業日で PoC を完了した体験記

捨てたコードは宝の山だった - 失敗から学んで4営業日で PoC を完了した体験記

49日間かけて失敗したプロジェクトから得た設計書・テスト・コードという「資産」を利用し、またもや自分ではコードを書かないスタイルの AI 駆動開発を行い、4営業日(合計約22時間)で、PoC を完了させた体験記です。(読み終わるまでの時間:8分程度)
2026.04.06

こんにちは。小売流通ソリューション部 SRE チームの池田です。

前回の記事で、Claude Code に社内ツールの開発を丸ごと任せた49日間の記録を書きました。結末は「本番採用見送り」でした。採用したプラットフォームの構造的な遅延が解決できず、2,755件のテストと191ファイルのコードを抱えたまま、プロジェクトを止めました。

あれから数日後、そのコードを引っ張り出して、AWS ネイティブ構成で PoC を作り直しました。4営業日で、申請から承認、IAM 権限の自動付与、利用完了後の自動剥奪まで、全フローが動くところまで辿り着きました。

この記事は、その4日間の体験記です。「49日間は無駄だったのか」という問いに対する、今の私なりの答えでもあります。

何を作っているのか

社内の AWS アカウントへのアクセス権限を管理するシステムです。チームメンバーが「このアカウントに ReadOnly でアクセスしたい」と申請し、承認されると IAM ポリシーが自動で付与される。期日が来たら、あるいは利用完了を押したら、権限が自動で剥奪される。そういう仕組みです。
詳細に興味がありましたら、前回の記事で触れていますのでご覧ください。

既存の仕組みは kintone アプリで安定稼動しています。前回はそれを GAS SPA で実装してみる取り組みでしたが、パフォーマンスの壁にぶつかって本番採用は見送りました。今回は、AWS のサービスだけで実装する試みです。

Day 1 (4時間程度)

orphan ブランチという選択

作業を始めるにあたって、git の履歴をまっさらにする「orphan ブランチ」で始めることにしました。

前回の取り組みのコードとテストはそのまま残っています。DynamoDB のテーブル設計も使えます。ただ、git の履歴には GAS 固有の設計判断や、途中で捨てた Fargate 構成の残骸が混ざっていました。新しい構成で開発するのに最初からノイズが多い状態は避けたい、でもコードはそのまま活用したい。ということでどうするのが良いか、AI に相談した時に orphan ブランチという使い方を提案されました。

ここから再出発

作業ディレクトリも専用に作成して、前回の取り組みで生み出されたフロントエンド、バックエンドの5パッケージ、共有の型定義など GAS SPA に関わらない部分のコード(191ファイルと320のテスト)を複製しました。

本番環境同様に Terraform 管理での実装を想定していましたが、この検証のためだけに本番同等の環境を用意するのはちょっと大袈裟に思いました。今回はあくまでも PoC と割り切って、全てのリソースをシェルスクリプトでデプロイする方針として、新たなデプロイスクリプトを作成してもらいました。

Day 2 (6時間弱)

検証用環境の準備

今回の取り組みも私と Claude だけで行うので、既存アカウント等から隔離された検証専用の AWS アカウントを使わせてもらうことにしました。
IAM User や IAM Role と AWS アクセスキーの作成作業、それらの認証情報を 1Password へ登録といったことは、手作業で実施したので少々時間を要しました。この辺の作業は新規アカウント作成時にしかやらないですし、過去の記憶はアテにならないので AI に作業手順書を用意してもらいました。
その後、初日に AI が用意してくれたデプロイスクリプトを見ると、.env ファイルに認証情報を記載する前提になっていました。一般的な手法ではありますが、1Password CLI を使う方針だったので、 op read コマンドで実行時に取得する方式に修正してもらいました。

Day 3 (7時間程度)

予定ではこの日にデプロイを終えて検証に入るはずでしたが、現実はそうなりませんでした。

Google OIDC の断念

ここでまた、私の考慮漏れが発覚しました。CloudFront への Web アクセス時の認証方法です。設計では Google の OIDC 認証を使うつもりでしたが、Google Cloud Console へアクセスできる権限がありませんでした。

PoC の目的は「クロスアカウント IAM の自動付与・剥奪が動くかどうか」を確かめることですから、素直にメールとパスワードによるシンプルな実装に変更しました。

Lambda が起動しない

次の問題は Lambda の実行で遭遇しました。コードを Lambda にデプロイしても Dynamic require of "buffer" is not supported というエラーで起動しません。エラーメッセージを Claude に伝えると「ESM(ECMAScript Modules)でバンドルしているが、Lambda 環境では動かない。CJS(CommonJS)に切り替えれば解決するから修正する」という回答が返ってきました。正直、何が起きているのかよくわからないまま修正してもらいましたが、ちゃんと理解しておこうと思います。

バグがたくさん

デプロイスクリプトは手動実行することにしましたが、実行するたびにエラーが起きるの繰り返しでした。
エラーが出るたびに Claude が原因を読み解いて修正してくれましたが、こんなにエラーが出るスクリプトは一連のプロジェクトの中で初めてでした。

これらのバグは全部、Day 1 で自分と Claude Code が設計したスクリプトに最初から埋まっていたものです。思えば、このデプロイスクリプトは Claude Code プラグインの Tsumiki で作ったものではありませんでした。つまり TDD によるテストや検証を行なっていないものでした。この見落としは完全に自分のミスでした。反省...

どうにか全フェーズのデプロイが完了し、合計で 373のテストがパスした状態でコミットしました。

Day 4 (5時間弱)

本命の検証

4日目。クロスアカウント IAM の付与・剥奪処理が、期待通りに動くかワクワクしながら開始しました。

今回は 2つの AWS アカウントを使いました。PoC 用のアカウントでシステムを動かし、ブラウザで CloudFront 上の申請用ページにアクセスして申請を行います。申請が承認されたら、Lambda が呼び出され、スイッチロール先となる別の検証用アカウントに作成した IAM ロールに対してポリシーのアタッチ / デタッチを実行します。

自動実行のテストで最後のバグ潰し

まず、playwright(chrome-devtools-mcp だったかも)を使ってブラウザによるテストを自動実行してもらいましたが、まだいくつかバグが残っていました。

DynamoDB のクエリに ExpressionAttributeValues が付いていないコードが残っていて ValidationException。これは以前の取り組みで書いたコードの残骸だったようです。

Lambda のロールに dynamodb:Scan 権限が抜けていたとのことで、これは事前の調査が不足していたんだと思います。

フロントエンドが PUT で送っているのにバックエンドは POST を待っていたというもの。「どうしてこうなった」。

フロントエンドのビルド時に環境変数が未設定で、Cognito の認証が通らない。設定して再ビルドしてくれましたが、これもなぜ発生したんだろう...

今回のバグはどれも原因の特定が簡単だったようです。ちゃんと TDD のプロセスで作成されたコードは、問題の切り分けがしやすい構造になっていたらしく、また、テストがあるからどこが壊れているかがすぐわかったようです。
でもテストの段階で発見して修正しておいて欲しかった。テスト結果のレポートを読むのをサボったのが原因ですね...

自動テストを終えて実際に操作してみた

フロントエンドから「SwitchRole(ReadOnly)」で申請を作成しました。対象アカウント、期間、理由を入力して送信。管理者ビューに切り替えて「承認」をクリック。

Lambda のログを確認すると

[INFO] Attached policy arn:aws:iam::aws:policy/ReadOnlyAccess to ikeda.test

CLI でスイッチロールして aws s3 ls を実行しました。S3 バケットの一覧が表示されました。さっきまでアクセスできなかったアカウントのリソースにアクセスできました。権限が付与された証拠です。

フロントエンドに戻って「利用完了」をクリック。Lambda が IAM ポリシーを剥奪。

もう一度 aws s3 ls を実行すると

An error occurred (AccessDenied): User is not authorized to perform s3:ListAllMyBuckets

期待通り、権限が剥奪されていることが確認できました。
ついに、申請、承認、権限付与、利用完了、権限剥奪の全フローが動きました。

49日間は無駄だったのか

4営業日、合計およそ22時間で PoC として最低限のことが完了できた今、はっきり言えます。前回までの取り組みは全く無駄ではありませんでした。

フロントエンドのコードは、ほぼそのまま流用できました。DynamoDB のテーブル設計も、技術要素の調査も、すでに終わっていました。今回「どう作るか」を考える必要がほとんどなかったのは、前回の49日間があったからです。判断に迷う場面が少なかったぶん、手を止めずに済んだと思います。

前回の記事のタイトルは「作って、捨てて、また作る」でした。結果的に、今回の取り組みもそのサイクルの延長でした。

まだ終わっていない

正直に書いておくと、4営業日で「完成」したわけではありません。

動いたのは DynamoDB、API、認証、フロントエンド、クロスアカウント IAM。検証できたアクセス種別は「SwitchRole ReadOnly」の1つだけです。

EventBridge バッチと SES メール通知機能も実装する想定で、コードは書いてありますが未検証です。また、本来8種類あるアクセス種別のうち7つは、これから実装と検証を行う必要があります。本番環境への移行においては、Terraform 体系に統合する作業がまるごと残っています。

PoC は「できる」を示すものであって、「できた」ではありませんから、ここから先が本番です。

おわりに

前回の取り組みで作ったものを捨て、4日間で作り直しました。捨てたのはアーキテクチャであって、そこで得たものは何も捨てずにここまで来ることができました。テスト、設計、失敗の記憶、どれも全部、今回の4日間に持ち込むことができました。

採用を見送ったプロジェクトであっても、その取り組みの経験とコード、コミットログは残ります。作って、捨てて、また作る。そのサイクルの中で、確実に前に進んでいることは証明済みなので、本番環境への移行も楽しみながら進めていきたいと考えています。

この記事をシェアする

関連記事