インフラとアプリ、同じ脳で回すと何が起きるか知った話

CDKでインフラを触っていると、deployに数分〜十数分かかることがある。最初は「遅いな」くらいにしか思っていなかった。
でも、ある日気づいた。
この待ち時間、アプリ開発の感覚とまったく違うぞ、と。
Reactを書いているときは、保存すればすぐ画面に反映される。エラーが出ても秒で直せる。「書く→確認→直す」のサイクルが数秒で回る。
一方、CDKは違う。書いて、synthして、deployして、CloudFormationが動いて、5分後に「IAM権限が足りません」と言われる。直して、またdeploy。また5分待つ。
この違いは単なる「速度の差」じゃないな、と思い始めた。
トライアンドエラーが1桁遅いという現実
アプリ開発とインフラ開発で、1回の試行にかかる時間を比べてみる。
アプリ開発の場合
コードを書く
→ 保存
→ Hot Reload
→ 画面確認
→ エラーなら修正
所要時間:数秒〜数十秒
インフラ開発(CDK)の場合
コードを書く
→ cdk synth(テンプレート生成)
→ cdk deploy(AWS APIコール開始)
→ CloudFormationがリソースを作成
→ VPC、ALB、RDS...それぞれ数分かかる
→ 途中で失敗したらROLLBACK
→ ロールバックも数分待つ
→ 原因調査
→ 修正して最初に戻る
所要時間:5〜30分
1桁どころか、場合によっては2桁違う。
アプリ開発の感覚で「とりあえず試してみよう」をインフラでやると、1日があっという間に終わる。午前中に3回しかdeployできなかった、みたいなことが普通に起きる。
エラーが「遅れて返ってくる」問題
アプリ開発では、エラーは即座に返ってくる。コードを書いた瞬間にエディタが赤線を引いてくれるし、実行したらすぐに例外が飛んでくる。
インフラは違う。
deployを実行して、5分待って、やっと「依存関係の順序が間違っています」と言われる。「え、それ最初に教えてよ」と思っても後の祭りだ。
Transit Gateway VPC Attachmentをデプロイしようとしたときの話をしよう。
コードを書いて、deployして、待つこと数分。エラーで止まった。なんでだろうと調べてみたら、Attachmentには「承認」というステップがあって、CDKはそれを待てないからタイムアウトする、と。
削除しようとしてもスタックが途中で止まって、DELETE_FAILEDの文字を見ながら手動でリソースを消す羽目になった。
この「フィードバックの遅延」は、思っている以上にストレスが溜まる。そして、思考のリズムが完全に変わる。
実行環境が手元にないという制約
Reactのコードはローカルで動く。Node.jsのスクリプトもローカルで動く。ローカルで確認してから本番に反映する、という流れが当たり前になっている。
でもインフラは?
VPCはローカルに作れない。ALBもRDSもEKSも、全部AWS上にしか存在できない。LocalStackのようなエミュレータはあるけど、完全な再現は無理だ。
ローカルで確認できないから、「とりあえず本番(または検証環境)でやってみる」しかない。試行回数を増やしたくても、物理的に増やせない。
この制約が、インフラエンジニアの思考パターンを形作っている。
2つの思考パターン
アプリエンジニアの思考パターンは、こうだと思う。
書いて → 動かして → 直す
フィードバックが速いから、まず手を動かす。動かしながら考える。間違っていたらすぐ直す。このサイクルを高速に回すことで、正解にたどり着く。
一方、インフラエンジニアの思考パターンは違う。
考えて → 想像して → 一発で当てに行く
フィードバックが遅いから、手を動かす前に考える。「このリソースを作ると、どういう状態になるか」を頭の中でシミュレーションする。できるだけ一発で正解を出す。試行回数を減らす方向に最適化する。
どちらが優れているという話ではない。環境の制約に適応した結果、こうなっているだけだ。
同じ人間が両方やると何が起きるか
問題は、この2つの思考パターンを同じ日に、しかも短い間隔で切り替えようとしたときに起きる。
午前中にCDKでインフラを触っていて、deployを待っている間に「ちょっとReactのバグ直しておこう」と思ってアプリのコードを開く。
数分でバグを直して、「あ、deploy終わったかな」とターミナルを見ると、まだ動いている。じゃあもう少しアプリを進めよう。
気づいたらアプリの作業に没頭していて、インフラのdeployが成功したのか失敗したのかわからなくなっている。確認したら失敗していて、また最初からやり直し。
これを繰り返していると、どっちも中途半端になる。インフラは進まないし、アプリも集中できない。脳が「スワップ」を起こしている状態だ。
なぜコンテキストスイッチが高くつくのか
単にタスクを切り替えるだけなら、そこまで問題ないはずだ。でも、インフラとアプリの切り替えは特別にコストが高い。
理由は、思考パターンそのものを切り替えないといけないから。
アプリモードの脳は「すぐ試す」方向に最適化されている。だから、インフラでも「とりあえずdeployしてみよう」という判断をしてしまう。結果、5分待った後に失敗して、また5分待つ。
インフラモードの脳は「一発で当てる」方向に最適化されている。だから、アプリでも設計を詰めてから書こうとしてしまう。結果、数秒で試せることに何分もかけてしまう。
切り替えた直後は、前のモードの思考が残っている。これがバグを生む。
インフラエンジニアの「待ち時間」との付き合い方
インフラエンジニアは、この待ち時間とどう付き合っているのか。
聞いてみると、「待ち時間を前提にタスクを並行化する」のが上手い人が多い。
deployを実行したら、終わるまで待たない。別のPRをレビューしたり、ドキュメントを書いたり、次のタスクの設計を考えたりする。「今deploy待ちだから、次に何やるか」を常に持っている。
待ち時間は「無駄な時間」ではなく「別タスクの時間」として組み込まれている。
この発想の転換ができるかどうかで、インフラ作業のストレス度合いがかなり変わる。
兼任するなら、意図的に分ける
両方やらないといけない状況は、個人開発では珍しくない。そういうときは、意図的にモードを分けるのが良いと思う。
日を分けるやり方。
月曜はインフラ、火曜はアプリ、水曜はインフラ、という具合に。その日は片方しかやらない、と決めてしまう。
時間帯で分けるやり方。
午前中にインフラを仕掛けておいて、午後はアプリに集中する。deploy待ちの時間は、ドキュメントや設計など「どちらでもない作業」を挟む。
どちらにしても、「今自分はどっちのモードか」を意識することが大事だ。無意識に切り替えると、前のモードの思考が残ってバグになる。
事前検証という投資
インフラの試行回数を増やせないなら、1回の試行の成功率を上げるしかない。
deployする前にできることは全部やる、というのが鉄則になる。
# 型チェック
npx tsc --noEmit
# Lint
npm run lint
# synthでテンプレート生成
cdk synth
# diffで差分確認
cdk diff
ここまでやってからdeploy。「とりあえずdeploy」は禁止。
この事前検証に時間をかけることで、deploy後に「5分待ってエラー」という最悪のパターンを減らせる。
属人化という見えないリスク
もう一つ、兼任には大きなリスクがある。属人化だ。
インフラもアプリも同じ人がやっていると、その人がいなくなったときに何も回らなくなる。特にインフラは「経験値がものを言う世界」なので、暗黙知が溜まりやすい。
「このスタックはこの順番でdeployしないと壊れる」「このリソースは手動で承認が必要」「このエラーはIAMポリシーをこう直せば通る」
こういった知識が一人の頭の中にしかないと、その人が休んだり辞めたりしたときに詰む。
しかも、兼任している人は忙しい。ドキュメントを書く時間なんてない。結果、属人化が加速する。
マネージャーの方へ
ここまで読んでいただいた方の中には、チームのマネジメントをしている方もいるかもしれない。
お願いがある。
インフラとアプリを同一人物に任せないでほしい。
「Aさんは両方できるから」「人が足りないから」という理由で一人に任せると、短期的にはコストが下がったように見える。でも長期的には、
- その人が疲弊する
- どちらかの品質が落ちる
- 属人化が進む
- その人がいなくなったとき、誰も引き継げない
というリスクを抱えることになる。
多少コストが上がっても、インフラとアプリは別の人に任せたほうがいい。あるいは、明確に役割を分けて、同時には担当させない。
「両方できる人」は貴重だ。だからこそ、その人を潰さないでほしい。思考モードの切り替えは、見えないところで大きなコストを払っている。
将来的なリスクまで含めて考えれば、分業したほうが安く上がる。間違いなく。
学びと展望
インフラとアプリ、両方の視点を持っている人は強い。それは間違いない。
ただ、思考パターンが違うということを理解せずに両方やろうとすると、どちらも中途半端になる。コンテキストスイッチのコストは、想像以上に高い。
個人開発でどうしても両方やるなら、意図的にモードを分ける。切り替えの回数を減らす。前のモードの思考が残らないように、明確な区切りを作る。
組織でやるなら、分業を検討してほしい。
インフラは「速く手を動かす仕事」ではなく「待ちを前提に設計する仕事」。この認識があるだけで、見え方が変わるはずだ。
Discussion