LLMに95%書かせたOSSをレビューしてみた
バイブコーディング全盛の今、95%をLLMが実装した国政政党OSSをリバースエンジニアリング。テスト・監視・デプロイ・セキュリティ・インフラ設計の5観点から、リバースエンジニアリングだけではわからなかった設計判断の疑問も含めて建設的に考察します。
コラム
kkm
Backend Engineer / AWS / Django
お断り: そこいらのフリーランスエンジニアが興味本位でソースコードを読んでみた結果をアウトプットしただけの、おっさんの落書きです。法律の専門知識も、本記事で取り上げたシステムのドメイン知識もありません。人様の成果物を勝手にレビューする形になるため、お断りさせていただきました。何かを批判・糾弾する意図はありませんので、あまり真に受けすぎないでください。なお、本記事の内容は2026年3月16日時点の実装に基づいており、現在は改善されている可能性があります。事実誤認や間違い等がありましたら、フッターのお問い合わせからご連絡いただければすぐに対応いたします。
スタンド使いの実力が試される時代に
ジョジョの奇妙な冒険をご存知でしょうか。作中に「スタンド」という概念があります。本体(人間)の精神エネルギーが具現化した存在で、スタンドが代わりに戦ってくれる。
AIコーディングエージェントは、まさにエンジニアにとってのスタンドです。しかし、スタンドには鉄則があります。スタンドの強さは本体の精神力に依存する。どんなに強力なスタンド能力を持っていても、本体が弱ければその力を引き出せません。
最近、「バイブコーディング」という言葉をよく目にするようになりました。AIに要件を伝えて、ほぼ全部書かせる。人間はビジョンだけ持っていればいい。速い、安い、すごい。最強じゃないか、と。
私はいわゆる職人タイプのエンジニアです。大した学歴もなく、ひたすらシステムを開発することだけをやってきました。体で覚えるタイプ。
LLMはコードを書く能力が驚異的でも、何を書かせるか、何が足りないかを判断するのは人間の仕事です。その判断力がなければ、スタンドに振り回されるだけのスタンド使いになってしまう。そしてその判断力は、頭の回転の速さではなく、何度も本番環境で痛い目を見てきた経験から来るものだったりします。痛い目を見るたびに「次はこうしよう」と積み上げてきた設計パターンやテンプレート。それがスタンド使いとしての精神力 ─ スタンドの出力を左右するものです。
そんな中、ある国政政党が「95%以上をLLMが実装した」と公言するOSSをリリースしました。短期間で。15,000行。しかもオープンソース。
2ヶ月かかっていないと聞いて、純粋にめんたまが飛び出ました。
ただ、経験則として一つ確信していることがあります。時間をかけるべき設計判断やアーキテクチャの選定を雑にLLMに任せると、挽回不可能な技術的負債が土台に埋まるということ。直すには最初から作り直すしかない、というレベルの負債が。
そこで、体で覚えてきたタイプのエンジニアとして、実際にそのOSSをリバースエンジニアリングして調べてみました。
レビュー対象: ある国政政党のOSS
当該プロジェクトは、政治資金の収支を可視化するWebダッシュボードです。クラウド会計ソフトからCSVを取り込み、Sankey図で資金の流れを視覚化できます。リリース直後に大きな話題となり、短期間で数十万PVを記録しました。
てっきりAPI連携かと思っていたのですが、実際にはCSVファイルの手動インポートでした。つまり、データの更新には毎回「会計ソフトからCSVをエクスポート → adminにアップロード」という手作業が必要です。公開ダッシュボードの最終更新日が2025年12月28日前後で止まっていることを見ると、運用負荷がタイムリーな更新のボトルネックになっている可能性を感じます。
失礼ながら、技術スタックをパッと見た印象は「個人開発っぽい構成だな」でした。ただ、読み込んでいくと印象が変わった部分もあるので、それは後述します。
| 項目 | 採用技術 | 評価 |
|---|---|---|
| フレームワーク | Next.js 15 (App Router) + React 19 | 最新で妥当 |
| データベース | Supabase (PostgreSQL) + Prisma | 型安全で良い選択 |
| ホスティング | Vercel + Supabase Cloud | 小〜中規模なら妥当 |
| 設計 | DDD 4層 (presentation/application/domain/infrastructure) | 教科書的に正しい |
| コード品質 | Biome + dependency-cruiser + knip + Renovate | ツール整備は充実 |
| モノレポ | pnpm workspace (webapp / admin / shared) | 構成は明快 |
DDD 4層にdependency-cruiserで層間の依存方向を自動検証、knipで未使用コード検出、Renovateで依存関係自動更新。骨格だけ見れば、むしろ過剰なほど整っています。
問題は、骨格の中身です。
ドメインロジック: 政治資金規正法をどこまで正しく実装できているか
前のセクションで「骨格の中身が問題だ」と書きました。DDD 4層構造の形式的な評価は後回しにして、まずはビジネスロジック自体を読み込みます。政治資金の計算処理は正しいのか、法律のルールは正確にコード化されているのか。
全体のデータフロー
このプロジェクトはwebapp(公開ダッシュボード)とadmin(管理画面)の2つのNext.jsアプリで構成されています。webapp = Read Model、admin = Write ModelというCQRS的な分離がされていて、これ自体は妥当な設計判断だと思いました。
graph TB
subgraph admin["admin(Write Model)"]
CSV["会計ソフトCSV"] --> PARSE["CSVパース<br>Shift_JIS/UTF-8自動検出"]
PARSE --> CONVERT["MfRecordConverter<br>勘定科目→収支区分変換"]
CONVERT --> VALIDATE["TransactionValidator<br>重複検出・整合性チェック"]
VALIDATE --> DB[("Supabase DB")]
DB --> REPORT["ReportData組立<br>閾値判定・セクション分類"]
REPORT --> XML["XML生成<br>政治資金収支報告書"]
DB --> SUGGEST["取引先推薦<br>Strategy: 頻度+名前マッチ"]
end
subgraph webapp["webapp(Read Model)"]
DB --> SANKEY["SankeyDataBuilder<br>カテゴリ集約・残高調整"]
DB --> BALANCE["BalanceSheet<br>固定負債・純資産計算"]
DB --> MONTHLY["MonthlyAggregation<br>12ヶ月ギャップ埋め"]
SANKEY --> UI["Sankey図・表・グラフ"]
BALANCE --> UI
MONTHLY --> UI
end
政治資金規正法の閾値ルール: 正しく実装されている
admin側のreportコンテキストには17個のドメインモデルファイルがあり、政治資金規正法のルールがコード化されています。最も重要な閾値ルールの実装を確認しました。
| 区分 | 閾値 | 比較演算 | ちょうど閾値の場合 | 判定 |
|---|---|---|---|---|
| 経常経費 | 10万円 | amount >= 100,000 | 個別記載対象 | 正しい |
| 政治活動費 | 5万円 | amount >= 50,000 | 個別記載対象 | 正しい |
| 寄附 | 5万円 | groupTotal > 50,000 | 「その他」に合算 | 正しい |
支出は「以上」(>=)、寄附は「超」(>)と使い分けられており、政治資金規正法の規定に正確に対応しています。寄附のグルーピング処理(寄付者IDごとに年間合計を算出し、5万円超のグループのみ明細記載)も正しく実装されています。
取引先の推薦にはStrategyパターンが使われており、使用頻度スコアリング(上位10件、1位:80点〜10位:53点)とパートナー名マッチング(スコア80点ベース)を組み合わせ、同一取引先は加算されます。バリデーションも蓄積パターン(複数エラーをまとめて返す)が採用されています。
このツールの社会的文脈
ビジネスロジックの詳細に入る前に、このツールがなぜ作られたのかを振り返っておきます。
2023年末に表面化した自民党派閥の政治資金パーティー裏金問題。85人の国会議員が5億7,949万円の政治資金パーティーの対価に係る収入(第12条第1項第1号ヘ)を政治資金収支報告書に不記載。裏金の使途として挙げられたのは「人件費」「懇親費用」「選挙費用」などで、特に人件費は政治資金規正法第12条第1項第2号で支出の記載対象から除外されており(情報公開クリアリングハウスの解説)、使途不明金の温床として国会で追及されました。
このツールはまさにその問題への回答として開発されています。公式発表では「政治とカネの問題を終わらせるための第一歩」と銘打ち、党首自身も「いろんな党の方が一緒に使っていったほうが、より政治とカネの問題の解決につながる」と語っています。自党専用ツールではなく、全政党・全政治団体が使える汎用的な透明化ツールとして位置づけられています。
この文脈を踏まえた上で、ビジネスロジックを見ていきます。
ビジネスロジック上の問題点
閾値ルールは正しい。では、それ以外のビジネスロジックはどうか。コードを1ファイルずつ読み込んでいくと、法定書類の数値に直接影響するバグがいくつか見つかりました。しかも、それは裏金問題の核心だった項目と重なっています。
- !【重大】公開ダッシュボードから人件費の流れが丸ごと見えない
このシステムの公開ダッシュボードは公開2日で20万PVを超えました。国民が政治資金の流れを確認する場所です。しかし、Sankey図(資金フロー図)に人件費のノードが表示されていません。「政治資金の流れを可視化する」はずのダッシュボードから、記載義務がないことが抜け穴として問題視された費目の流れが見えない状態です。さらに、法定報告書(XML)のエクスポートでも支出総額から人件費が抜け落ちます。たとえば実際の支出が1,000万円でうち人件費が300万円なら、報告書には700万円と記載される。開発スコープドキュメントには「2026年3月末に当該政党が政治資金報告書を提出する際に、本アプリケーションを使用できるようにする」と記載されていますが、それまでに修正されるか、手動チェックで拾えるかはウォッチしてみようと思います - !【重大】本部・支部間の交付金で、少額の支出が報告書から消える
政党の本部から支部へ(またはその逆に)渡したお金は、政治資金規正法第18条第4項により金額に関係なく全件を報告書に記載する義務があります。しかしこのシステムでは、一定額未満の支出が報告書から自動的に除外される仕組みになっており、少額の交付金支出が報告書に載りません。交付金は通常まとまった金額なので頻度は高くないかもしれませんが、透明性を謳うシステムで法定記載事項が漏れる可能性を許容するという設計判断には疑問を感じました - !党費・パーティー収入が法定報告書の収入総額に含まれない
公開ダッシュボード側では党費は収入として表示されますが、法定報告書のエクスポートでは収入総額の計算に含まれません。政治資金規正法第12条第1項第1号で記載が求められている項目です。設計ドキュメントには「当該政党では該当なし」と記載されており、自党に該当しないから後回しにした判断ですが、全政党への展開を謳うシステムで、裏金問題の核心だった項目を「スコープ外」にする設計判断には、個人的に疑問を感じました。他の政治団体が使えば、ダッシュボードの数字と法定報告書の数字が食い違うことになります - !パーティー券の購入者明細が公開ダッシュボードに表示されない
Sankey図では「パーティー収入」の総額は独立ノードとして正しく表示されています。ただし、誰がいくらパーティー券を買ったのかは見えません。admin側にはDonor情報(氏名・住所・職業)の保存機能があるのに、公開ダッシュボードにはその表示UIがない。機能設計として、国民が一番知りたい部分じゃないかとも思ってしまいました。 - !CSVパーサーがRFC 4180非準拠
mf-csv-loader.tsのパーサーで、ダブルクォート内のエスケープ("")が正しく処理されません。フィールド内にクォートを含むCSVデータでパースが壊れる可能性があります - !金額パースで不正値がサイレントに0になる
MfRecordConverter.parseAmountは空文字や不正な値を0に変換しますが、エラーとして検出しません。0円の取引がそのままDBに保存される可能性があります
問題①: 党費(個人負担金)が収入総額から完全に欠落
政治資金規正法第12条第1項第1号イでは、収支報告書に「個人が負担する党費又は会費」の金額および納入者数の記載を求めています。しかし、このシステムでは党費がそもそもデータ構造に存在しません。
graph TB
subgraph actual["実際のコード(report-data.ts)"]
INCOME["IncomeData"] --> BIZ["事業収入 ✓"]
INCOME --> LOAN["借入金 ✓"]
INCOME --> GRANT_I["交付金 ✓"]
INCOME --> OTHER["その他収入 ✓"]
KIFU["寄附合計"] --> SUM
BIZ --> SUM["honnenSyunyuGk<br>本年収入額"]
LOAN --> SUM
GRANT_I --> SUM
OTHER --> SUM
DUES["党費(kojinFutanKgk)"] -.->|"null(スコープ外)"| SKIP["計算に参加せず"]
SKIP -.->|"XMLでは0円出力"| XML["収支報告書XML"]
SUM --> TOTAL["syunyuSgk = 前年繰越 + 本年収入額"]
TOTAL --> XML
end
subgraph law["政治資金規正法が求める計算"]
LAW_DUES["党費 ✓"] --> LAW_SUM["本年収入額"]
LAW_KIFU["寄附 ✓"] --> LAW_SUM
LAW_BIZ["事業収入 ✓"] --> LAW_SUM
LAW_LOAN["借入金 ✓"] --> LAW_SUM
LAW_GRANT["交付金 ✓"] --> LAW_SUM
LAW_OTHER["その他 ✓"] --> LAW_SUM
end
style DUES fill:#fee2e2,stroke:#ef4444,color:#991b1b
style SKIP fill:#fee2e2,stroke:#ef4444,color:#991b1b
style LAW_DUES fill:#dcfce7,stroke:#22c55e,color:#166534
account-category.ts にはマッピング定義が存在し("個人の負担する党費又は会費" → key: "membership-fees")、データの存在自体は認識されています。しかし IncomeData インターフェースに党費用のプロパティが定義されておらず、getSummary の計算に組み込まれていません。
バグ詳細②: 交付金支出で閾値以下の取引がシート16から脱落
交付金に係る支出は、政治資金規正法第18条第4項により、本部・支部への交付金支出について名称・所在地・金額・年月日の記載が求められます。しかし、閾値フィルタリングの後に交付金フラグを参照する設計のため、閾値以下の交付金支出が構造的に漏れます。
graph TB
TX["トランザクション<br>isGrantExpenditure = true<br>金額 = 8万円(光熱水費)"] --> THRESH{"閾値判定<br>経常経費: 10万円以上?"}
THRESH -->|"YES(10万以上)"| ROWS["rows配列に追加<br>koufukin = 1"]
THRESH -->|"NO(10万未満)"| UNDER["underThresholdAmount<br>に合算(明細なし)"]
ROWS --> GRANT_FILTER["grant-expenditure.ts<br>rows.filter<br>koufukin === 1"]
GRANT_FILTER --> SHEET16["シート16に出力 ✓"]
UNDER -.->|"rows に含まれない"| LOST["シート16に出力されない ✗"]
style UNDER fill:#fee2e2,stroke:#ef4444,color:#991b1b
style LOST fill:#fee2e2,stroke:#ef4444,color:#991b1b
style SHEET16 fill:#dcfce7,stroke:#22c55e,color:#166534
| 経費区分 | 金額 | 閾値 | rows | シート16 |
|---|---|---|---|---|
| 光熱水費 | 12万円 | 10万円 | 含まれる | 出力される ✓ |
| 光熱水費 | 8万円 | 10万円 | 含まれない | 脱落する ✗ |
| 組織活動費 | 6万円 | 5万円 | 含まれる | 出力される ✓ |
| 組織活動費 | 3万円 | 5万円 | 含まれない | 脱落する ✗ |
ユーザーがUI上で isGrantExpenditure フラグを立てても、そのトランザクションが閾値以下であれば aggregateExpenseSection の段階で rows から除外されるため、フラグの情報が失われます。
バグ詳細③: 経常経費の合計から人件費が欠落
同じ ExpenseData を入力として、2つの集計関数が矛盾した結果を返します。
graph LR
EXP["ExpenseData"] --> GS["getSummary<br>(収支総括表用)"]
EXP --> ESD["ExpenseSummaryData<br>(経常経費小計)"]
subgraph getSummary_calc["getSummary の計算(バグ)"]
GS_U["光熱水費 ✓"]
GS_S["備品消耗品費 ✓"]
GS_O["事務所費 ✓"]
GS_P["人件費 ✗ 漏れ"]
end
subgraph esd_calc["ExpenseSummaryData の計算(正しい)"]
ES_P["人件費 ✓"]
ES_U["光熱水費 ✓"]
ES_S["備品消耗品費 ✓"]
ES_O["事務所費 ✓"]
end
GS --> getSummary_calc
ESD --> esd_calc
getSummary_calc --> XML_OUT["XML出力<br>支出総額が過少"]
esd_calc --> TABLE_OUT["管理画面表示<br>正しい金額"]
style GS_P fill:#fee2e2,stroke:#ef4444,color:#991b1b
style XML_OUT fill:#fee2e2,stroke:#ef4444,color:#991b1b
style TABLE_OUT fill:#dcfce7,stroke:#22c55e,color:#166534
管理画面上では ExpenseSummaryData が使われるため正しい金額が表示されますが、XMLエクスポート時に使われる getSummary では人件費が欠落し、法定提出書類の支出総額が過少になります。テストも人件費を含むケースを検証していないため、このバグは検出されていません。
特に上の2つは、政治資金収支報告書の法定数値に直接影響します。閾値ルール(部品)は正確なのに、集計ロジック(組み立て)にバグがある。そしてスコープ判断でも、システムの存在理由に関わる領域が後回しにされている。もちろんOSSだからこそ、こうやって外部から問題を見つけられるわけで、システムにバグがあること自体は珍しくありません。問題は、この超重要領域にテストも設計上のガードもなかったということです。人件費の集計漏れは、テストに人件費を含むケースが1つでもあれば防げた。党費のスコープ判断は、法定様式の全項目を設計段階で洗い出していれば気づけた。LLMは個別のビジネスルールを正確にコード化できるが、「何をテストすべきか」「何をスコープに含めるべきか」の判断は人間の仕事です。実際にしばらくバイブコーディングをしていて感じるのですが、テストを「テスト書いて」くらいの抽象度で指示すると、ビジネス要件や上流の仕様に対するテストはほとんど書かれず、形式的なロジック検証ばかりになりがちです。人件費の集計漏れは、まさにそのパターンに見えました。
そして気になるのは、これらの欠落がどの領域に集中しているかです。パーティー収入、人件費、党費 ─ いずれも裏金問題で社会的に問われた項目そのものです。「政治とカネの問題を終わらせる」と掲げたツールで、まさにその問題の核心部分が未実装またはバグになっている。開発スコープドキュメントには「当該政党はパーティー未開催のため対応不要」「該当する取引が存在しないため対応しない」とありますが、全政党への展開を謳う以上、他党にはパーティー収入も党費も人件費もあります。自党の事情でスコープを切った結果、ツールの存在意義そのものに関わる領域が空白になっているのではないか、と感じました。
なお、Sankey図の「支出 > 収入」時の補完や、固定負債がマイナスになるケースについても調査しましたが、前者は adjustWithBalance で前年度繰越を加えるため通常は収入 >= 支出が保証される設計であり、後者はテストで負の値を明示的に期待値としている意図的な設計でした。ちゃんと読めば設計意図が見える部分と、本当にバグである部分の区別は重要です。
webapp側のSankey図: 可視化ロジックの中身
webapp側で最も複雑なロジックはSankey図の構築です。データフローを追うと、以下の処理が行われています。
graph LR
A["カテゴリ別集計"] --> B["renameOtherCategories<br>その他→その他の収入/支出"]
B --> C["consolidateSmallItems<br>動的閾値で小項目統合"]
C --> D["adjustWithBalance<br>前年度繰越・残高追加"]
D --> E["SankeyDataBuilder.build<br>ノード・リンク生成"]
E --> F["Sankey図表示"]
consolidateSmallItems は動的閾値を計算して小さなカテゴリを「その他」に統合します。閾値は「上位8番目の金額」と「全体の1%」の大きい方を採用。チャートの可読性を考慮した実用的な設計です。ただし、閾値計算の母数にサブカテゴリなし項目(借入金、交付金など)を含めるため、これらの金額が大きいと1%閾値が過大になり、サブカテゴリが過度に統合される可能性があります。
DDD構造の問題(補足)
ビジネスロジック自体とは別に、DDD的な構造上の問題もあります。
- •
MfRecordConverter(会計仕訳の変換ロジック)がinfrastructure層に配置。中身はドメイン知識そのもの - •
encoding-converter.tsがdata-importとsharedで重複。BigInt ID変換も複数ユースケースに散在 - •XML直列化器がdomain層に配置(法律仕様準拠のためとも解釈可能だが、DDDの原則からは外れる)
- •webapp側のエラーハンドリングが汎用的な再スローのみで、スタックトレースが失われる
- •
FinancialSummarySection.tsx(プレゼンテーション層)にSankeyデータ構造から財務データを逆算するドメインロジックが漏出
LLMは「DDDで実装して」と指示すれば構造は完璧に作れます。政治資金規正法の閾値ルールのような明文化されたビジネスルールも正確にコード化できている。一方で、「どの層に何を置くか」という設計判断にはばらつきがあり、最終出力の集計ロジックにバグが入り込む。部品の品質と全体の整合性は別問題であり、後者こそ人間のレビューが不可欠な領域です。
テスト: LLMが得意なテストと、人間が設計すべきテスト
リポジトリをクローンして確認したところ、85ファイル・約18,300行のテストコードが存在していました。
単体テストの量はAI以前ではなかなかできないレベルで充実しています。admin側のauthコンテキストとdata-importコンテキストは100%カバー、CIパイプラインも5ジョブが動き、dependency-cruiserで層間依存の自動検証まで入っている。E2Eテストもadmin側7ファイル、webapp側3ファイル。テストコードの中身を1行1行読み込んだわけではありませんが、量と構成は立派です。
ただ、僭越ながら気になったのはテストの守備範囲です。充実しているのは「この関数にこの入力を渡したらこの出力が返る」という形式的なロジック検証で、LLMが最も得意とする領域です。一方で、ビジネス要件から逆算して個別に設計しなければならないテスト ─ 統合テスト、コントラクトテスト、パフォーマンステスト ─ は一切ありません。
経験上、本当にバグを防いでくれるのは後者のほうです。前のセクションで書いた人件費の集計漏れも、「人件費を含む支出データでgetSummaryを呼んだら支出総額に含まれるか」というテストが1つあれば防げた。でもそれは「テスト書いて」という指示からは出てこない。「人件費が後から追加されたのだから、集計ロジックの全箇所を確認すべき」という判断は、ビジネス要件を理解した人間にしかできません。
また、ユースケースのテストでリポジトリをモック化しているため、「ユースケース → リポジトリ → DB」の結合は本番でしか検証されていません。スキーマ変更後にクエリが壊れるリスクが、テストで守られていない状態です。
E2Eテストも同じ傾向です。admin側7ファイル・webapp側3ファイルと数はありますが、中身を見ると「ページが表示される」「ボタンが押せる」「リダイレクトされる」といったUI動作確認が中心で、ビジネスフローの最終出力を検証するテストがありません。具体的には、CSVインポートから法定報告書(XML)をエクスポートするまでの一連のフローや、公開ダッシュボードに表示される数値が入力データと整合しているかの検証が不在です。Sankey図は「SVGが表示される」ことだけ確認されていて、表示された数字が正しいかは誰もチェックしていない。
非機能要件: 本番運用に必要なもの
ビジネスロジックの問題を先に書きましたが、非機能要件にも気になる点がいくつかありました。僭越ながら、政治資金データという公共性の高い情報を扱うサービスとしては、もう少し整備が欲しいなという印象です。
- !監視体制: コードベースからSentry、Datadog等のエラー追跡ツールの導入を確認できませんでした。Vercelの標準ログはありますが、「金額がおかしい」「データが表示されない」といった異常を能動的に検知・通知する仕組みがあるかどうかは読み取れません。リポジトリ外で運用されている可能性はあります
- !デプロイの安全策: mainマージでVercelが即本番デプロイ。ステージング環境はありません。Vercelのロールバック機能は使えますが、DBマイグレーションが途中で失敗した場合のリカバリ手順はコードベースから確認できませんでした
- !セキュリティ: webapp側のCSPヘッダーやX-Frame-Options等の基本的なセキュリティヘッダーは設定されています。ただ、アプリケーションレベルのRate Limit、WAF、Bot対策はコードベースから確認できませんでした。VercelやSupabaseのプラットフォームレベルのDDoS対策はありますが、APIエンドポイントごとの制限などはプラットフォーム任せでは対応しきれない部分です
- !IaC(Infrastructure as Code): Terraform、CDK、Dockerfile、docker-compose、
vercel.jsonのいずれも存在しません。設定の再現性をどう担保しているのかが気になります
では、どう改善できるか。現在の技術スタック(Vercel + Supabase)を維持するパターンと、AWSに移行するパターンの2つで考えてみました。
パターンA: 現スタック(Vercel + Supabase)のまま改善
技術スタックを変えずにできることは意外と多いです。
- •監視: 特定のサービスへの依存を増やしたくない場合でも、Vercelのデプロイフック + GitHub Actionsで異常検知時にIssueを自動発行し担当者をアサインするだけで、追加依存なしで最低限の検知体制は組めます。もう少し踏み込むならSentry(無料枠あり)で未処理例外の自動キャプチャも
- •デプロイ: Vercelのプレビューデプロイをステージング代わりに活用。DBマイグレーション前にSupabaseの自動バックアップポイントを確認する運用ルールを整備
- •セキュリティ: Next.js middlewareでRate Limitを実装(
@upstash/ratelimit等)。Vercel FirewallでBot対策。vercel.jsonでセキュリティヘッダーを統一設定 - •IaC: 最低限
vercel.jsonでビルド設定・リダイレクト・ヘッダーをコード管理
パターンB: AWS構成に移行
政治資金データの管理権を自前に持ちたい場合や、ベンダーロックインを避けたい場合はこちら。国政政党には政党交付金という財源があり、AWS構成でも月$35〜70程度で組めます。
例えば、以下のようなデプロイワークフローを用意すれば、マイグレーション失敗時のロールバックまで含めて自動化できます。
graph TB
S1["1. Build & Test
Dockerイメージ作成
CI全テスト実行"] --> S2["2. Push
ECRにイメージ登録
S3に静的ファイル同期"]
S2 --> S3["3. Migrate
RDS Snapshot自動作成
マイグレーション実行"]
S3 -->|"成功"| S4["4. Deploy
ECS / App Runner
CloudFrontキャッシュ無効化"]
S3 -->|"失敗"| ROLL["Snapshotから自動復旧
Slack通知"]
S4 --> HEALTH["5. Health Check
異常検知でロールバック"]
ポイントは、マイグレーション前にDBスナップショットを自動取得していること。失敗してもスナップショットから復旧できるので、「mainマージで即本番」でも安全策が担保されます。
- •監視: CloudWatch Logs + Sentry + OpenTelemetryで構造化ログ・エラートラッキング・分散トレーシングの3層構成
- •セキュリティ: AWS WAF + CloudFrontで2層防御。エンドポイントごとのRate Limit。Secrets Manager + IAM認証でDB接続文字列を廃止
- •IaC: Terraformでインフラ全体をコード管理。環境の再現性とレビュー可能性を担保
どちらのパターンでも、障害に気づけないシステムは、障害が起きていないのではなく障害を見逃しているだけだと思っています。短期間という制約の中で後回しにされたのは理解できますが、本番運用を続けるなら早期に整備したいところです。
リバースエンジニアリングではわからなかった設計判断
ここまではコードから読み取れる事実をもとに書いてきましたが、ここからは「わからなかった」ことについて書きます。リバースエンジニアリングの限界であり、同時に最も気になった部分です。
技術スタックの選定は妥当だったのか
当該プロジェクトの技術スタックは Next.js 15 + Supabase + Prisma です。加えて、管理画面を別のNext.jsアプリとして自作しており、Radix UIコンポーネント群、TanStack Table、さらにAnthropic Claude SDK(AI連携)まで組み込まれています。
このシステムの特性は「データ中心のCRUDアプリ + 可視化レイヤー」です。管理画面(admin側186ファイル・webapp側の約6倍)が実務の中心であり、これだけの管理画面を作り込むのは当然の判断です。
技術スタックの選定理由について、いくつかの仮説が考えられます。
- ?OSSとしてコントリビューターの間口を広げるため? TypeScriptに統一すれば、フロントもバックも同じ言語で書ける。Next.js + Supabaseは個人開発者に人気のスタックであり、コントリビュータを広く募りたいOSSとしては合理的な選択です
- ?LLMが最も得意な言語・フレームワーク? LLMはReact/Next.jsの学習データが圧倒的に多いため、LLMに95%書かせるならNext.jsが最も効率的という判断だったのかもしれません
- ?短期間で動くものを出すための最速構成? Vercel + Supabaseはセットアップが速く初期コストも低い。「100日以内に出す」という政治的コミットメントの中では、手堅い選択です
どの仮説が正しいにせよ、興味深いのはこれらの選定理由がすべて「作りやすさ」に寄っていることです。「運用しやすさ」「セキュリティを担保しやすい構成か」という観点からの選定がされていたかどうかは、外からは判断できません。
Vercel + Supabase自体はスタートアップや個人開発で定番の組み合わせですが、政治資金データの最終的な管理権がSupabase社(米国本社)にある点、Vercel固有機能へのベンダーロックインは気になります。また、リポジトリにはvercel.jsonを含むIaCが一切存在しません。LLMがインフラ設計を苦手としている影響なのか、マネージドサービスだから不要という判断なのか。設定の再現性をどう担保しているのかは疑問が残ります。
全体を通しての所感
失礼を承知で率直に書くと、全体を通して感じたのは技術力が足りないという話ではなさそうだ、ということです。閾値ルールの正確な実装やDDD構造を見る限り、コードを書く力はある。ただ、当該政党は100日プランの公約としてこのツールのリリースを掲げていました。「100日以内に出す」という政治的コミットメントの中で、「LLMが書きやすい構成」「コントリビューターが参加しやすいスタック」といった判断軸が優先され、「本番運用で何を守るか」という軸が後回しになったのかもしれない、と感じました。
最初に「個人開発っぽい構成だな」と思った印象は、技術スタックの問題ではなく、この優先順位の違いから来ていたのかもしれません。
土台に埋まる技術的負債
ここまで複数の観点から見てきましたが、共通するのは「LLMに書かせた部分」と「人間が判断すべきだった部分」の境界が曖昧になっているということです。
フェアに評価すべき点は評価します。admin側のドメインモデリングは政治資金規正法のルールをきちんとコード化しており、dependency-cruiserによる層間依存の自動検証、CIパイプラインでのカバレッジ計測、88ファイルの単体テスト。「書いた部分」の品質は決して低くない。
しかし、「書かなかった部分」に問題があります。統合テスト、監視体制、デプロイの安全策、セキュリティの多層防御。加えて、ドメインロジックのセクションで詳述した通り、パーティー収入・人件費・党費という裏金問題の核心領域が未実装またはバグになっています。「自党に該当しないからスコープ外」という判断は自社利用なら合理的ですが、「全政党が使える透明化ツール」として公開している以上、その空白はツールの社会的意義に関わるのではないかと感じます。
これらは「どの行を書くか」ではなく「何を守るか」の判断であり、LLMではなくサービスの文脈を理解した人間が判断すべき設計判断です。
GitClearの2025年調査は、AI支援によるコード品質の変化を2億1,100万行の変更から分析しています。結果、リファクタリングに費やされるコードの割合が2021年の25%から2024年には10%未満に低下。コードクローン(5行以上の重複ブロック)は8倍に増加。新規コードが2週間以内に修正される割合は3.1%から5.7%に上昇しています。
これは「速く書ける」の裏側で、「考える時間」が削られていることを示唆しています。短期間で15,000行というスピードは見事ですが、その短期間の中に「テスト戦略のうち統合テスト・パフォーマンステストまで設計する時間」「監視要件を定義する時間」「セキュリティモデルを検討する時間」は含まれていたのでしょうか。
ただ、一つフェアに書いておくと、ゼロからの短期間という条件なら、こうなるのも無理はないのかもしれません。テスト戦略の全層整備や監視設計といった「非機能要件の引き出し」は、過去のプロジェクトで痛い目を見た経験から蓄積されるものです。経験豊富なエンジニアがチームにいれば、過去に整備した設計テンプレートやIaC基盤を持ち込んで、短期間でも本番品質に近づけられる可能性はあります。つまり問題は「期間が短すぎた」ことではなく、その短期間に本番運用の経験値を持ち込めたかどうかではないかと思います。
もしそうした経験なしにこれらの判断が省略されて土台が作られたのだとしたら、後から改善しようとしても表面的なパッチでは済まなくなります。統合テストの後付け、監視体系の後付け、セキュリティの後付け。どれも「最初から考慮されている」場合と比べて、何倍もコストがかかります。場合によっては、作り直したほうが早い。
結論: 本体の経験値がスタンドの出力を決める
バイブコーディングを否定したいわけではありません。私自身、それなりにいいお値段のAI課金をしている人間です。LLMなしの開発にはもう戻れません。
そして、当該プロジェクトが全てダメだと言いたいわけでもありません。admin側のドメインモデリングは政治資金規正法のルールを正確にコード化しており、DDD 4層構造とdependency-cruiserによる層間依存の強制は教科書的に正しい。CQRS的なRead/Write分離も妥当な設計判断です。CIパイプラインもCodecovカバレッジ計測まで含めて動いています。LLMに書かせた部分の品質は、思った以上に高かった。
ただ、LLMが書けるのはコードです。「何を書かないか」「何が足りないか」は判断できません。統合テストの必要性、監視要件の定義、デプロイの安全策、セキュリティモデルの設計。そして「政治とカネの問題を終わらせる」と掲げるなら、パーティー収入・人件費・党費は最優先で実装すべき領域だったはずです。自党のスコープだけで機能を切る判断は、LLMには指摘できません。これらは「何行のコードを書くか」ではなく「何を守るか」の問題であり、サービスの文脈を理解した人間にしか決められません。
LLMが書けるのはコードだけです。設計判断は書けません。
テスト戦略の全体設計、監視要件、デプロイの安全策、セキュリティモデル。これらは「何行のコードを書くか」ではなく「何を守るか」の問題であり、サービスの文脈を理解した人間にしか決められません。
AIコーディングの登場で開発そのものが凄まじいアップデートを受けているのは間違いありません。ただ、そのアップデートの裏で、旧来大事にされてきた原則がないがしろにされていないでしょうか。
- •テストの独立性(ISTQB) ─ 設計・実装・テストは担当者を分け、別の視点で思い込みを排除する
- •V字モデル ─ 各開発フェーズに対応するテストフェーズを設け、上流の設計ミスを下流で検証する
- •関心の分離(SoC) ─ 複雑なシステムを独立した責務に分割し、変更の影響範囲を限定する
- •多層防御・フェイルセーフデフォルト(OWASP) ─ 一つの防御が破られても次の層で守る。障害時は安全側に倒す
どれもAI以前から当たり前とされてきた原則です。本記事の対象がこれらを無視していたと言いたいわけではありませんが、バイブコーディングを過信しているプロジェクトを見ると、「実装をAIにやらせるだけ」のはずが、いつの間にか設計もテストもスキップしてよくなったかのように扱われているケースがあります。結果として設計ミスやテスト考慮漏れが多発している印象です。AIで実装が速くなった分、こうした基礎知識の重要性はむしろ上がったと感じています。
私自身、実装を完全にAIに任せるシーンはかなりあります。ただ、その場合はAI以前よりも設計とテストに時間を割きます。自分がコードを書いていた頃は、実装の過程で「あ、この設計だとここが破綻するな」と気づいて軌道修正できていた。AIに実装を任せるとその巻き取りが効かない分、設計とテストできちんと巻き取る必要がある。トータルの工数は実装がほぼゼロになった分かなり短縮されていますが、設計とテストの比重はむしろ増えています。
スタンドの能力を最大限に引き出すには、本体の精神力が必要です。バイブコーディング時代のエンジニアに求められるのは、コードを書く力ではなく、何を作らせ、何が足りないかを見抜く力です。
AIにそれなりにいいお値段を払って書かせている人間からの、建設的な意見でした。