インフラ・プロセス・速さ

ZUCKERBERG: We’ve changed our internal motto from "Move fast and break things" to "Move fast with stable infrastructure."

Mark Zuckerberg on Facebook's Future, From Virtual Reality to Anonymity | WIRED

Zuck 氏が "Move fast with stable infrastructure." と言ったのが 10 年前。

Feature Flags の必要性

アプリの社内ベータリリースを派手に壊した若者が retrospective を書いたというので眺めていると「単体テストのカバレッジが足りませんでした。テスト真面目に書きます。」と書いてあり、固まってしまった。いや、テスト書くのはいいんだけどさ・・・。

「フラグがあるといいんじゃない?」とコメントしたら若者の上司が出てきて「うちのチームでも会社全体でも、フラグの外側でバンバンコード書いてんじゃん?アンラッキーな事故だよ」という返事。ふたたび固まってしまった。

あんたそれ稼ぎ頭のアプリ相手に言ってごらん・・・。


インターネット動画部門のアプリチームで仕事をしていた頃、すべての変更はじっさいフラグの裏で行われていた。リファクタリングさえも、明らかに安全なものを除けばフラグの裏だった。毎週のように出ていくリリースで何かが壊れるとアラートがあがりチャットが開かれ、ばばばっと問題のフラグが特定され、壊れる挙動はサーバからのフラグデータ更新で無効化される。これは動画部門に限った話ではなく何億人もユーザがいる他のアプリたちでも同じだと思われる。同じインフラが使われているし。

そのむかし大企業で働き始めた頃「このプロジェクトは revert first だから、何かが壊れたら怪しい変更はバンバン revert するんだよ」と言われて感慨を受けた。フラグプッシュで問題を直すのはその先を行っている。バイナリを配り直さない。モバイルのようにバイナリの再配布が難しい環境ではフラグに圧倒的優位がある。

もちろんこれは、特にサーバ側の世界では特段珍しい話ではなく、アプリが使っているフラグの仕組みもそっちの世界から来たものである。Devops の国 Amazon の Builder's Library には、フラグやリリースをロールバックする時に壊れないようちゃんとデザイン・テストしようななんて話まであるわけ。だからさ、いまどきフラグくらい面倒がらず使ってくれよ。

Feature Flags の面倒臭さ

・・・と思ったが、ここでふと思い返す: そういう洗練された世界のフラグ地獄に音を上げ、この荒くれ者チームに戻ってきたんじゃなかったっけ?

Feature flag first が徹底し、全てが Controlled Online Experiment な世界に住む人のコミット一覧を見ると、アプリのコードを書くよりフラグの設定をいじるコミットの方が多い。フラグの追加・削除だけでなく、rollout のようなデプロイ作業もバージョン管理されており(えらいね!)、いつもなにかしらフラグを触っているのが見える。ちょっと粒度のあるタスクを始める最初の作業がフラグの追加。そんな世界。

大きな機能追加だと、むしろフラグを足したあとしばらくはその裏でコードを書く時間が続くので相対的に面倒は少ない。一方で既存コードをリファクタリングするのは大変。IDE がサポートする完全に副作用フリーなリファクタリングならともかく、それなりにエッジケースのある非純粋な「リファクタリング」をしようとおもったら、やはりフラグ。

フラグで隠すリファクタリングには独自の難しさがある。いつも Branch By Abstraction みたいに綺麗な切り口があるわけではない。フラグ歴の長いエンジニアはごく自然にその離れ業をやってのけるが、比較的 junior なエンジニアは苦労しており、結果としてリファクタリングは諦められることが少なくない。結果として複雑さは段々と積み重なり、コードベースを扱いにくいものにしていく。

これは (特にアプリでの) feature flag first が持つ本質的な複雑さで、避け難い。積み上がったゴミを一掃しようとたまにすごいシニアなエンジニアがでてきて新しいフレームワークとかを導入するが、移行は数年がかり。前の大変更が終わらないうちに次の大変更がやってきたりする。それぞれ別のフラグの裏で。ちょっとまって!たすけて!

Moving "Fast" and Not Breaking Things

Feature flags や experimentation, 段階的 rollout といった洗練されたインフラを使うのは、サービスを壊さない、止めないためである。何億人もが使うサービスを止めると色々問題があるし、金銭的な被害も無視できない。

究極的には "velocity" のためとも言える。サービスは止められない、サービスが止まると、すべての進捗は問題が解決するまで止まってしまうから。洗練されたインフラの流儀に従えば blast radius が小さくなり、サービス全体を止めなくて済む。

つまりフラグは個々のエンジニアの velocity を下げるかもしれないが、インフラのおかげで安定性を犠牲にすることなく開発人員を増やせる。そのスケーラビリティは個々人の低速化を補ってあまりある。

開発人員を増やすのが常に良いアプローチとはいえない。けれどサービスがスケーラブルな問題を扱っているなら・・・これは巨大なインターネットサービスはだいたいそうだが・・・人員追加によるスケールアウトは普通。こうしてインターネット大企業は大きくなってきた。冒頭の Zuck 発言も同じ趣旨と言えよう。

個人の視座

インターネットサービスにおいて Feature Flags は正しい。Online Experiments は正しい。Staged, Continous Rollout は正しい。それはサービスとしての速さを支えているから。

しかし開発者個人としてはどうか。フラグや実験はめんどくさい、時間もかかる。認知負荷も高い。必要なのはわかっているからやめろとはいわないが、楽しくはない。しかもフラグの「楽しく無さ」は、ミーティングのような一時的に我慢すれば済む通り雨的かったるさではなく、仕事の楽しさのコアであるコード書き体験全体に影を落とす税金的かったるさである。一番の楽しみがうっすらと損なわれる(あなたがリファクタリングパズル愛好家でない限りは - 巨大インターネットサービスのアプリ開発者として偉くのに必要な素養の一つには違いない)。

巨大インターネットサービスを覆うそんなフラグ文化を、マイナー電話機付属アプリを作っているだけの荒くれチームに布教してしまっていいのだろうか。その戦いを自分は信じているのか。社外には continous delivery なんてせずリリース前にブランチを切ってきちんと安定させるんだから、社内ベータくらいたまに壊れてもいいじゃない?数千人くらいユーザいそうな気がするけれど・・・。

そういえばフラグを押し返した件の上司はもともとはアプリの開発者。アプリに飽きて社内ライブラリチームを立ち上げたのだった。今回壊れたのもそのライブラリ。まあ気持ちはわかるよね。ライブラリ内部の書き換えに関するフラグなんて、いちいち公開・フリップしたくないよな。めんどくさい。

代償の払い方

インターネットサービスでもないんだし、たまに社内ベータが壊れるくらいいいじゃない。でかい変更を隠すフラグは必要だけど、何から何までフラグに隠せなんて言いたくない。

基本的には賛成したい姿勢だが、躊躇がある。なぜか。

と、バグが起きた時にけっこうな確率で自分が "triage" に巻き込まれるからだろう。他人のおこしたバグの分析を押し付けられ数時間・一日潰れる。自分の仕事は進まない。うんざり。しかも急ぎの仕事でめちゃストレス。これは安定しないサービスを保守する開発者が毎日のように pager によばれ疲弊/Burnout するのに似ている。自分は burnout すると困る妻子持ちなのでなんとかしないといけない。

ここでの教科書的な答えは 1) 辞める か 2) 近代化にむけて立ち上がる、なわけだが、1) は失敗しており、失敗した理由の一つは 2) の現実が気に入らなかったからなので、教科書的でない答えを模索する必要がある。

それについては、また今度。