受託PM10年の自分が『人月の死』を肌で感じでしまった話
何が起きたか
本番で稼働しているネイティブアプリを、まるごとブラウザで動くWeb版に作り直す仕事があった。仕様を読んだ実装担当のエンジニアが見積もって、フロントエンドの実装だけで約40人日。APIの組み込みまで入れると、さらに乗る。そのフロントエンド部分が、自分はコードを1行も書かないまま、着手から約10時間で全画面・全フローが動いて、その日のうちに非公開ホストへのデプロイまで終わった。実装はすべてAIコーディングエージェントで、自分はプロジェクトマネージャとして「ここが違う」と指摘して、判断をしただけだ。
「AIで作ったら1日で終わった」という記事は、もう珍しくない。ただ、その多くは趣味のプロジェクトか、見積もりという商売の外側にいる人の話だと思う。自分は受託開発の世界に約10年いて、その間ずっとPMをやってきた。人月で見積もって、人月で請求する側の人間だ。
「人月は死んだ」という言葉は、昔から何度も聞いた。言っているのはだいたい外の人で、正直、またその話かと思って聞き流してきた。それが今回、自分の案件の中で、本当に起きた。人月で商売をしてきた側の人間として、これを書かないのは不誠実な気がした。だからこれは効率化の自慢ではなくて、同じ商売をしている人に向けた警鐘のつもりで書く。
前提を先に置く
題材の具体は伏せる。粒度としては、本番稼働中の、画面がそこそこあるtoC向けアプリと思ってもらえればいい。撮影の画面もある。移行元はネイティブ(SwiftUI)、移行先は純ブラウザのWeb(Nuxt 3 / Vue / TypeScript)だ。
| 項目 | 内容 |
|---|---|
| 移行元 → 移行先 | 本番稼働中のネイティブアプリ → 純ブラウザWeb |
| 見積もり | 仕様を読んだ実装担当エンジニアが積んだ値で、フロントエンド実装のみ約40人日(API組み込みは別途) |
| 今回作った範囲 | フロントエンド全体+本番APIを模した自作モック。実環境に繋げばそのまま動く形にしてある |
| 体制 | 実装はAIコーディングエージェント。人間(自分)は指摘と判断のみ。コードは書かない |
| 再現規模 | 主要6画面+各種の状態(エラー、撮影の待機・録画・プレビュー等) |
| 実測 | 11:33着手 → 同日21:38頃に全画面・全フロー+デプロイ到達(約10時間)。時刻はAIセッションログの実測値 |
| 生成規模 | 62ファイル/約8,290行(.vue 35本 6,322行、.ts 24本 1,713行 ほか) |
正直な但し書きを2つ
- これは既存の実ソースと仕様がある移植案件で、ゼロから要件を考える新規開発ではない
- 動作確認はすべて自作モックの上での話で、実データでの本番稼働は別フェーズだ
この但し書きを見て、なんだ条件が良かっただけか、と思う人もいると思う。ただ、受託の日常を思い出してほしい。要件が固まっていて、仕様書があって、それを形にする。受託の工数の大半は、この「決まったものを作る」工程に張り付いている。今回AIが10時間でやったのは、まさにその工程だ。つまりこれは、条件のいい特殊な案件の話ではない。受託の売上のいちばん太い部分が、そのまま当てはまる話だ。
その10時間は、雑な10時間ではない
40人日が10時間、と言うと、まず疑われるのは品質だ。だから先に、この10時間がどう回っていたかを書く。
一人のAIに順番にやらせたわけではない。この案件で走らせたAIセッションは2日間で計14本、初日のピークでは4本が同時に動いていた。実装役のAIとは別に、レビュー役のAIを立てた。機能の境界でファイルの持ち場を分けて、触るファイルが重ならない機能を別々のAIに割り当てる。共通ファイルは書き換え禁止で、足すことしか許さない。AI同士の連絡は書き置きのメモで、「あなたの持ち場には手を出していない」を明示させる。やっていることは人間のチームのマネジメントと同じで、相手がAIになっただけだった。
品質のチェックは、レビュー役のAIだけには頼らなかった。AIはコードや設計の矛盾を見つけるのは得意だが、画面が1pxはみ出しているといった細かい見た目の崩れを拾うのは得意ではない。だからそこは、判定が機械的に決まるチェックスクリプトに任せた。実装より先に用意したのは、75行の検証スクリプトだ。iPhoneの実寸(390×844)のエミュレーション上で主要7ルートを自動で巡回して、各ルートで3点を見る。HTTP 200が返ること。documentElement.scrollWidth === window.innerWidth、つまり横に1pxでもはみ出したら即✗。そして動画・録画・処理表示といった主要な要素がちゃんと存在すること。1件でも落ちたら終了コード1で止まる。エラー系の状態はURLパラメータで一発で再現できて、撮影フローは擬似カメラで自動で走る。AIたちがすごい速度で変更を出してきても、自分は赤くなったところだけを見ればいい。
そのうえで、チーム全体にルールをひとつだけ敷いた。正本は実ソース。人間の記憶でも、仕様書でも、AIの自信でもなく、食い違いが出たら必ず元アプリの実ソースを開いて決める。このルールが、このあと自分自身を裁くことになる。
19:13、AIが自分に反論してきた
セッションログから、ほぼそのまま引く。
19:13、完成した撮影画面を見て、自分はこう指示した。「動画撮影中は✗ではなくて戻るボタンでしょ。あと位置は左でしょ、確か。実物の仕様見て頼む」。使ったことのあるアプリの記憶があったから、迷いはなかった。
AIは、その場で直さなかった。「ご指摘が私の読んだソースと食い違うので、推測で直さず精密に再確認します」。
19:19、6分後に返ってきた回答はこうだ。「実物は撮影中=×(押すと録画を破棄)で、戻るボタンは非表示でした。ご記憶とは差異があります」。根拠として、撮影画面のヘッダ定義の該当行(L274-327)と、復元済みの仕様の項目番号。結論、「現状の実装が正です」。
19:20、自分の返信は一行だ。「準拠のままでOK」。
自分の勘違いだった。指示から決着まで7分。悔しさより先に、納得が来た。AIが賢いというより、自分で敷いたルールが、敷いた本人に対してもちゃんと動いた。正しいかどうかを決めたのが、立場ではなくて実ソースだったから、負けても嫌な感じがしなかった。
AIは、渡した正本をそのまま増幅する
逆向きの事件もあった。ある時点まで、AIが作る画面が元アプリと微妙にずれていた。色も余白も、どこか自前で寄せたような手触りだった。「実物準拠って言ったよね。引き継ぎに入ってなかった?」と聞くと、AIは言い訳をしなかった。「その指摘はその通りです。『実物UIに準拠』は口頭で出た方針なのに、引き継ぎ書の確定方針に明記されていませんでした。だから私も自前で近似してしまいました。これは私の記録漏れです。すみません」。
根っこは自分にあった。文章にしなかった方針は、無いのと同じだった。AIは、渡された正本をそのまま増幅する。正本が実ソースなら正確さが増幅されるし、正本が人間の曖昧な記憶なら、その曖昧さが自信満々に増幅される。
AI同士の間でも、同じことが起きた。レビュー役のAIが実装役の設計文書を読み込んで、つじつまの合わない点を重要度順に並べて突きつけた。実装役は、それを鵜呑みにしなかった。本人の言葉を借りると「鵜呑みにせず実体検証してから回答する」。一次ソースを検証し直して、指摘された以上の取り違えを自分で見つけてきた。ある仕様の値をめぐって両者の主張が食い違い、突き詰めたら両方間違っていて、実ソースには第三の値が書いてあった、という場面まであった。決着は一行だった。「ワーカーの修正は全てOK、かつ私の想定を超える良い仕事だった」。
メンツの張り合いも、忖度もない。判定するのがソースだと決まっていると、誰が正しいかを争う必要が消えて、何が正しいかだけが残る。間違いは速く、安く片づく。人間のチームだと、この「潔く負ける」が一番難しいのを、自分はよく知っている。
いちばん怖かったのは、エンジニアの反応だった
21:38、全フローが動いてデプロイまで通った。そのとき来た感情は、やった、とまずい、が同時だった。AIの進歩そのものは心から嬉しい。ただ同じ瞬間に、あの40人日の見積もりはこの後どうなるんだ、というのが浮かんだ。
後日、40人日を積んだ本人に、10時間で終わったことを伝えた。
感心はしてくれた。ただ、それ以上ではなかった。彼はこれを「AIがすごい」という話として聞いていて、「自分の40人日が要らなくなるかもしれない」という話としては聞いていなかった。無理もないと思う。言われたものを言われた通りに作る契約で働いていれば、明日の仕事は何も変わらないからだ。ただ、自分はこの反応がいちばん怖かった。この変化でいちばん影響を受ける本人に、この変化がいちばん届いていない。警鐘を書こうと決めたのは、この瞬間だ。
人月は、何で死んだのか
人月という商売は、二つの前提の上に立っている。ひとつ、価値は掛けた時間に比例すること。ふたつ、人は単価の枠の中で、だいたい取り替えがきくこと。単価表という書類は、この二つを信じているから成立している。
40人日が10時間になった。これで壊れたのは一つ目の前提だ。ただ、それだけなら「AIで速くなりました」というよくある話で終わる。本当に壊れたのは、二つ目のほうだと思っている。
正直に書くが、今回この速度で終わったのは自分がやったからで、同じ手順書を隣のPMに渡して同じ10時間になるとは思っていない。実は、ここが問題の核心だ。人月という単位が使えるのは、同じ単価の枠に入っている人なら、誰がやっても成果がだいたい同じ幅に収まるからだ。差が2倍くらいなら、単価を上下させれば吸収できる。今回起きたのは40倍で、これは単価では吸収できない。見積書に「1人月」と書いたとき、その1人月の中身が、担当者によって40倍違うかもしれない。そうなったらこの単位は、時間は測れても、価値はもう何も測っていない。
じゃあ、自分が10年書いてきた見積もりは嘘だったのか。それは違うと言い切れる。AIがない前提なら、あの数字はどれも妥当だった。40人日と積んだエンジニアも、何も間違えていない。数字が間違っていたのではなくて、数字の下にあった仕事の作られ方が、ある日入れ替わった。それだけの話で、それが全部の話だ。
入れ替わったあとの世界では、人を分ける軸が変わる。これまで工数と単価を分けていたのは、シニアかジュニアかだった。これからは、AIをどれだけ使えるかだ。同じ仕様書を渡して、あるエンジニアは40人日と積み、別の誰かは3人日と積む。どちらも正直な数字だ。本来なら、この13倍の差は、そのまま値段の差になるはずだ。ところが、業界の単価表にはシニアとジュニアの区分はあっても、AIをどれだけ使えるかを表す欄はどこにもない。いま成果を一番大きく左右している軸が、値段を決める書類のどこにも存在しない。人月という単位が死んでいるとは、こういうことだ。
それでも次の案件で、自分はエンジニアが積んだ数字を使うと思う。実装の責任を取るのは実装する人間で、そこは変わらないからだ。ただ、その数字の意味は変わってしまった。見積もりはもう作業量の申告ではなくて、その人がAIとどこまで組めるかの申告になっている。
単位が死んだあと、何が売れるのか
いちばん居心地の悪い事実を書いておく。今回、40人日を10時間にしたのは、AIの操作がうまかったからではない。正本をどこに置くかを決める。主張には根拠を出させる。持ち場を切って並行で走らせる。チェックを人の注意力ではなく仕組みにやらせる。全部、受託のPMとして10年やってきた仕事そのものだ。人月の商売の中で身につけた能力が、人月を終わらせる側で一番よく効いた。
死んだのは受託ではない。死んだのは、人月という単位だ。では、単位が死んだあとに何が売れるのか。今回の10時間が、その答えを半分見せてくれている。
この案件が速く終わった最大の理由は、正本——何が正しいかの決定版——が、実ソースという形で最初から存在していたことだ。正本さえあれば、AIはそれを安く、正確に、疲れずに実行する。裏を返すと、仕事はこれから二つに割れる。正本を実行する仕事と、正本を作る仕事だ。実行する側の値段は、今回見た通りに崩れていく。
残るのは、正本を作る側だ。要件定義のような曖昧なものを、関係者の間で決着させて、一つの正本に固定する。そして、その決定の結果に責任を持つ。AIは正解の候補ならいくらでも出してくれるが、関係者の合意を取り付けることはできないし、決定が間違っていたときに責任を負うこともできない。客が金を払うのは、正しい答えにではなく、誰かが責任を持って決めた答えにだ。これからの受託で値段が付くのは、そこだけになっていく。逆に言えば、正本を作れず、責任も引き受けず、実行だけをしてきた層から順に、AIに置き換えられる。
警鐘として
最後に、これを警鐘として書いた理由をはっきりさせておく。人月の死は、業界のニュースや単価表の改定みたいな、分かりやすい告知では来ない。今回がそうだったように、ある案件が見積もりの何十分の一かの時間で終わって、関係者の誰も騒がず、翌日も普通に仕事が続く。そういう形で、個々の現場に、ばらばらに、静かに来る。だから、待っていても気づけない。
受託でPMをやっている人は、一度でいいから自分の案件で試してみてほしい。人間チームの見積もりと、AIに任せた場合の実測を、並べてみる。それだけで、自分が客に売っているものの正体が、嫌でも見える。自分はそれをやってしまって、もう人日の見積もりを、以前と同じ気持ちでは書けなくなった。