連載公開分(クリックで開く)
- # 【プロローグ】素人エンジニアが自分でアプリ開発できると甘く見てた 全16弾無邪気な実装 vs 架空エンジニア2人の容赦なきツッコミ ※便利かなと思ってどんどん追加しちゃうんだよね
- #1「在庫が見たいだけ」だったのに、引き算ひとつから在庫引当と多目的最適化まで始まった ※ 総在庫・予約在庫・有効在庫を分けた瞬間、ただの在庫表示が別物になった
- #2「予約機能もこのアプリ1個にまとめれば便利じゃん」で、“時間”が一気に牙をむいた ※時間重複・境界条件・時間粒度・スマホ操作まで、空いてる時間を選ぶだけでは終わらなかった
- #3 「機能増えたら毎回探すの面倒じゃん」で、お気に入りから共通UI設計まで広がった ※機能を増やした結果、「何を作るか」より「どう辿り着くか」が問題になった
- #4「普通の組織図でいいじゃん」で一人を複数部署に入れたら、“多対多”が始まった ※兼務・複数所属・部署別役職・過去履歴が一気に絡み始めた
- #5「どこの現場で使うかも持てばいいじゃん」で、物件マスタが在庫・予約・人を全部つないだ ※機能ごとにバラバラだった情報が、共通マスタを境に一つのシステムへ変わり始めた
- #6「会社ごとにちょっと変えたい」で、設定駆動とバリデーションが殴り合いを始めた ※柔軟にしたら、「自由に設定させすぎない仕組み」の方が難しくなった
- #7「ログインできたからって何でも触れたらまずいじゃん」で、認証より面倒な認可の沼に入った ※管理者か一般かでは足りず、「誰が・誰に・何をできるか」まで判定することになった
- #8「二人とも空いてる画面を見てたら、どっちが勝つの?」でRace Conditionが牙をむいた ※在庫も予約も、画面に表示された「空き」を信用しただけでは守れなかった
- #9「自分の在庫も持てた方が楽じゃん」で、“場所・所有・利用範囲”が全部別問題だった ※マイ在庫・現場在庫・共有在庫を分けたら、在庫数よりスコープ管理が難しくなった
- #10「申請もこの中でできた方が楽じゃん」で、CRUDの裏に状態遷移が増殖した ※申請・承認・差し戻しを入れた瞬間、「保存できる」だけでは何も足りなくなった
- #11「資格もQRで見られたら早いじゃん」で、“全部見せる”より“必要な答えだけ返す”方が難しかった ※資格・個人QR・権限をつないだら、識別より情報開示の方が本題になった
- #12「一輪車を“ネコ”で探せないの不便じゃん」で、Canonical IDと人間の曖昧さを両立させることになった ※人間には優しく、内部は厳密に−−別名検索から正式IDへ収束させるまで
- #13「機能増えすぎて説明する方が面倒」で、“どれが今の正解?”問題がコードの外まで広がった ※ヘルプ・仕様・会話・引き継ぎがズレ始め、Single Source of Truthが必要になった
- #14「Codexなら全部見てくれるじゃん」で、AIの作業速度が人間のレビュー限界を超えた ※ AIがリポジトリ全体を調べられるほど変更範囲が怖くなる――Gitで差分・ステージ・既存変更・コミット境界を守る開発へ
- #15「テストこんなに通るじゃん」で安心しかけたら、“全部緑なのに間違ってる”が普通に出てきた ※ テストがPASSでも正しいとは限らない――機能間の不具合・境界値・回帰・期待結果そのものまで疑う横断テストへ
- #最終回【AIの尻拭い】JSONのまま押し通したら、AIたちが「最小トランザクションジャーナル」まで考え始めた ※「こうしたい」の一言から、AIとCodexが複数JSON更新の整合性リスクを発見し、Crash Recovery・回帰テスト・Git安全境界まで組み上げる開発へ
- #エピローグ「作れた」で終わりじゃなかった。むしろ、そこからが本番だった ※ 単体テストはPASS、次は“全部つないで壊れないか”第2回横断テストへ
この頃からはCodexが入って、個別のテストも、かなり増えていました。
予約を直したら、予約のテスト。在庫を直したら、在庫のテスト。権限を直したら、権限のテスト。一個ずつ、その都度確認する。
A「じゃあ、かなり安全になってきた?」
私「俺もそう思ってた。」
そこで一度、それまでより、かなり広い範囲をまとめて確認しました。
横断テスト。
私「で、やってみたらさ。」
A「どうだった?」
私「思ってたより通った。」
A「お。」
私「正直、もっとボロボロだと思ってた。」
A「作った本人が一番信用してない(笑)」
私「だって、完全な初心者状態から積み上げてきたものだからね(笑)」
一個通る、こっちも通る、また通る。
私「結果見ながら、意外とちゃんと作れてるじゃんとは思った。」
A「調子乗った?」
私「ちょっと(笑)」
実際、横断テストでは、かなり広いところを確認しました。
未ログインで、取れてはいけないデータが取れないか、本人、他人。管理者、それぞれ、見える範囲が正しいか。
在庫の総数、予約数、有効数、この三つが、操作後も合っているか。
予約を作る、取り消す、変更する、数量を変える、ロケーションを変える、競合したら、409で止まるか、画面とAPIで、認証や権限の扱いがズレていないか。
A「で、全部通った?」
私「いや。」
A「ですよね(笑)」
私「ちゃんと穴は出た。」
ただ、見つかった問題は、単純に、「予約ボタンが動かない」とか、「在庫画面が開かない」だけではありませんでした。むしろ厄介だったのは、一個ずつなら正しいのに、組み合わせるとおかしくなるもの。
A「例えば?」
私「在庫単体なら正常。」
A「うん。」
私「予約単体でも正常。」
A「うん。」
私「でも予約された在庫を、別の在庫操作と組み合わせたら?」
A「整合が崩れる。」
私「そういうやつ。」
B「Interaction Bug、相互作用によるバグですね。個々の機能は正しくても、複数機能の組み合わせで不整合が出ます。」
例えば、在庫10個、予約3個、使えるのは7個、ここまでは正しい。でも、別の在庫操作をしたときに、予約済み3個を無視して、8個出せてしまったら?
私「一個の処理だけ見れば、8個減らしただけ。」
A「でも予約との関係を見るとダメ。」
私「そう。」
B「だから、単体テストだけでは見えない。」
A「共通化したら楽になるって言ってたよね?」
私「長い目で見ればね。」
A「その言葉、だいぶ怪しくなってきた(笑)」
私「でも共通化したら、一個直したときに影響先も増えるのは確か。」
B「そこでRegression Testing、回帰テストが重要になります。変更箇所だけでなく、以前動いていた関連機能が壊れていないか確認します。」
そして、横断テストを広げるほど、境目も気になるようになりました。
例えば、数量10までOK。では、9、10、11、0、未入力。予約時間なら、終了時刻と、次の予約の開始時刻が、ちょうど同じ、1分だけ重なる、日をまたぐ。
私「普通に使いそうな数字だけ入れてたら、こういうところ抜けるじゃん。」
B「Boundary Value Testing、境界値テストですね。」
A「正常な真ん中より、端っこが壊れやすい。」
私「後からそれが分かってきた。」
でも、俺が一番怖いと思ったのは、別のパターンでした。
A「まだある?」
私「全部緑なのに間違ってる。」
A「それタイトルのやつ。」
例えば、本当は、在庫が足りないから、その操作は成立してはいけない。でも、テストを書くときに、その仕様を勘違いして、「成立するのが正解」としてしまう。そして、実装も、その間違ったルールどおりに作る。
A「コードは期待値どおり。」
私「うん。」
A「テストも?」
私「緑。」
A「でも業務としては?」
私「間違い。」
A「嫌すぎる(笑)」
つまり、テストは、テストに書いた正解と一致しているかを見ている。その正解自体が間違っていれば、テストは、間違った実装を、堂々と合格にできます。
B「ここではExpected Result、期待結果そのものの妥当性を確認する必要があります。」
私「俺の言葉なら、コードとテストが仲良く間違ってたら、ずっと緑じゃん。」
A「笑えないけど分かりやすい(笑)」
これは、テストが赤くなったときも同じでした。
A「赤くなった。」
私「うん。」
A「じゃあテスト直す。」
私「何で?」
A「……。」
私「本体が間違ってるかもしれない。」
A「うん。」
私「テストが間違ってるかもしれない。」
A「うん。」
私「そもそも仕様が変わってるかもしれない。」
B「原因切り分けですね。」
テストが落ちたから、とりあえず期待値を書き換えて、緑にする。
私「これが一番怖い。」
A「実装に合わせてテストを直す。」
私「本当はバグだったものまで、『これが正しい動きです』って固定しちゃうかもしれない。」
B「期待値を実装へ無条件に合わせると、本来のバグを正しい挙動として固定してしまう危険があります。」
A「じゃあテストたくさん書けば安全?」
私「それも違う。」
A「分かってきたね(笑)」
全部を、同じ方法で、大量に自動テストすればいいわけでもありません。APIで見るのが得意なもの、単体で見るもの、複数機能を合わせるもの、UIで見るもの、実際のブラウザで見るもの、スマホで触ってみるもの。
B「ユニットテスト、APIテスト、統合テスト、UIテスト、E2Eテストには、それぞれ得意な範囲があります。」
A「じゃあ全部いっぱい書こう。」
私「それはそれで、テストの保守で死ぬんでしょ(笑)」
B「そうですね。」
テスト自体にも、実行時間、保守、データ準備、環境依存、そういうコストがあります。そして、横断確認する対象も、テストコードだけではなくなりました。Git、差分、API、JSON、データ、文字コード、サーバー状態、実際の画面。
私「PowerShellも、この頃にはほぼ確認側。」
A「第14弾の検査員。」
私「そう。」
A「Git見る。」
私「見る。」
A「テスト。」
私「見る。」
A「JSON。」
私「見る。」
A「文字化け。」
私「見る。」
A「API。」
私「見る。」
A「データ状態。」
私「見る。」
A「ブラウザ。」
私「必要なら実際に見る。」
A「確認多すぎ(笑)」
私「一個だけ見て『全部OK』って言う方が怖い。」
そして、もう一つ変えました。確認していないものを、確認済みにしない。
A「自動テストは通った。」
私「テスト済み。」
A「でも実ブラウザは見てない。」
私「実ブラウザ未確認。」
A「スマホでは?」
私「見てないなら未確認。」
A「じゃあ完成?」
私「そこまで確認が必要なテーマなら、完成とは言わない。」
全部を、無理やりPASSか、FAILの二択にもしたくありませんでした。条件付きで通っている、まだ確認していない、そこもそのまま残す。PASS、条件付きPASS、FAIL、未検証。
B「これは、完了状態そのものを曖昧にしない考え方ですね。」
そして、重要度の高い問題は、P0、P1、P2といった形で、優先度も分けて見るようになりました。
A「じゃあ第1回の横断テスト、結局どうだった?」
私「思ったより通った。」
A「でも?」
私「穴も見つかった。」
A「直した。」
私「直した。」
A「じゃあ完成。」
私「じゃない。」
A「そこ大事?」
私「かなり。」その後も、UIを直す、共通化する、機能を追加する、既存機能を直す、業務ルールを調整する、普通に開発は続きました。
A「横断テスト後にも、普通に開発期間がある。」
私「うん。」
A「つまりテストって、最後に一回やって終わりじゃなくなった。」
私「そう。」
B「変更のたびに、どこが壊れる可能性があるかを見る、品質管理の一部になったわけですね。」
私「最初は『動いた!』で喜んでたのにね。」
A「今は?」
私「『何確認した?』ってなる。」
A「嫌な成長したな(笑)」
そして、ここまで来ると、開発方法そのものも、最初とはかなり変わっていました。私、AI、Codex、PowerShell、Git、正本仕様、テスト。
A「役割もかなり分かれてきた。」
私「そう。」
A「じゃあ今の形、完成形?」
私「今の俺には、かなりやりやすい。」
A「質問には答えてない(笑)」
私「完成形とは言ってない。」
B「その辺は最終回ですね。」
A「じゃあ最後は、今やってる開発そのものにツッコむか。」
私「どうせ言いたいことあるんだろ。」
A「いっぱいある(笑)」
次回、最終回。
【AIの尻拭い】JSONのまま押し通したら、AIたちが「最小トランザクションジャーナル」まで考え始めた
「こうしたい」の一言から、AIが複数JSONの整合性リスクを発見――「こうしたい」の一言から、AIとCodexが複数JSON更新の整合性リスクを発見し、Crash Recovery・回帰テスト・Git安全境界まで組み上げる開発へ
連載公開分(クリックで開く)
- # 【プロローグ】素人エンジニアが自分でアプリ開発できると甘く見てた 全16弾無邪気な実装 vs 架空エンジニア2人の容赦なきツッコミ ※便利かなと思ってどんどん追加しちゃうんだよね
- #1「在庫が見たいだけ」だったのに、引き算ひとつから在庫引当と多目的最適化まで始まった ※ 総在庫・予約在庫・有効在庫を分けた瞬間、ただの在庫表示が別物になった
- #2「予約機能もこのアプリ1個にまとめれば便利じゃん」で、“時間”が一気に牙をむいた ※時間重複・境界条件・時間粒度・スマホ操作まで、空いてる時間を選ぶだけでは終わらなかった
- #3 「機能増えたら毎回探すの面倒じゃん」で、お気に入りから共通UI設計まで広がった ※機能を増やした結果、「何を作るか」より「どう辿り着くか」が問題になった
- #4「普通の組織図でいいじゃん」で一人を複数部署に入れたら、“多対多”が始まった ※兼務・複数所属・部署別役職・過去履歴が一気に絡み始めた
- #5「どこの現場で使うかも持てばいいじゃん」で、物件マスタが在庫・予約・人を全部つないだ ※機能ごとにバラバラだった情報が、共通マスタを境に一つのシステムへ変わり始めた
- #6「会社ごとにちょっと変えたい」で、設定駆動とバリデーションが殴り合いを始めた ※柔軟にしたら、「自由に設定させすぎない仕組み」の方が難しくなった
- #7「ログインできたからって何でも触れたらまずいじゃん」で、認証より面倒な認可の沼に入った ※管理者か一般かでは足りず、「誰が・誰に・何をできるか」まで判定することになった
- #8「二人とも空いてる画面を見てたら、どっちが勝つの?」でRace Conditionが牙をむいた ※在庫も予約も、画面に表示された「空き」を信用しただけでは守れなかった
- #9「自分の在庫も持てた方が楽じゃん」で、“場所・所有・利用範囲”が全部別問題だった ※マイ在庫・現場在庫・共有在庫を分けたら、在庫数よりスコープ管理が難しくなった
- #10「申請もこの中でできた方が楽じゃん」で、CRUDの裏に状態遷移が増殖した ※申請・承認・差し戻しを入れた瞬間、「保存できる」だけでは何も足りなくなった
- #11「資格もQRで見られたら早いじゃん」で、“全部見せる”より“必要な答えだけ返す”方が難しかった ※資格・個人QR・権限をつないだら、識別より情報開示の方が本題になった
- #12「一輪車を“ネコ”で探せないの不便じゃん」で、Canonical IDと人間の曖昧さを両立させることになった ※人間には優しく、内部は厳密に−−別名検索から正式IDへ収束させるまで
- #13「機能増えすぎて説明する方が面倒」で、“どれが今の正解?”問題がコードの外まで広がった ※ヘルプ・仕様・会話・引き継ぎがズレ始め、Single Source of Truthが必要になった
- #14「Codexなら全部見てくれるじゃん」で、AIの作業速度が人間のレビュー限界を超えた ※ AIがリポジトリ全体を調べられるほど変更範囲が怖くなる――Gitで差分・ステージ・既存変更・コミット境界を守る開発へ
- #15「テストこんなに通るじゃん」で安心しかけたら、“全部緑なのに間違ってる”が普通に出てきた ※ テストがPASSでも正しいとは限らない――機能間の不具合・境界値・回帰・期待結果そのものまで疑う横断テストへ
- #最終回【AIの尻拭い】JSONのまま押し通したら、AIたちが「最小トランザクションジャーナル」まで考え始めた ※「こうしたい」の一言から、AIとCodexが複数JSON更新の整合性リスクを発見し、Crash Recovery・回帰テスト・Git安全境界まで組み上げる開発へ
- #エピローグ「作れた」で終わりじゃなかった。むしろ、そこからが本番だった ※ 単体テストはPASS、次は“全部つないで壊れないか”第2回横断テストへ
