2016年8月5日金曜日

スケーラブルな開発と Conway’s law

Twitter で自分が関わっているサービスの開発者の数は、この 4 年間で約 5 倍に増えた。エンジニアの数とサービスの開発・運用について考えてみたい。

2, 3 人の小さいチームが作ったサービスが徐々に複雑になり、アーキテクチャの大きな変更がないまま、複数のチームに属する数十〜数百人のエンジニアが関わるようになる、ということはよくある。このときの問題点として、次の 3 つが考えられる。

コードレビュー

数十人が関わるサービスは大抵コードが複雑で、全体像をしているのは一部のエンジニアだけという状況になる。そして、その一部のエンジニアが「コアチーム」となり、コード変更の決定権を持つ。
エンジニア全体の増加に比べて、コアチームのメンバーは増えない(システムの複雑性が上がっていくので、全体を理解しているエンジニアの数が比例しない)ので、コアチームのレビューが開発のボトルネックになる。

デプロイ・インシデントレスポンス

エンジニアが増えると、一つのデプロイに含まれる変更の数が増える。当然、デプロイが失敗する確率(機能的なバグやパフォーマンス低下など)が上がる。また、失敗した時にどの変更をロールバックすべきかの判断が難しい。デプロイできるのはコアチームだけになり、また、デプロイの頻度が下がる。

リソースプランニング

そのサービスが使用するリソースの予測が難しくなる。
一つのサービスの中にいくつもの機能が含まれるので、ダウンストリームのサービスへのリクエスト数、CPU / ネットワーク使用量、ストレージのキャパシティの見積もりが非線形になる。



これらに共通する原因は、機能・モジュールごとのアイソレーションが無いことだ。
アイソレーションのわかりやすい実装例として、micro service があるが、もしこの複雑なサービスを複数の単純なサービスに分解できるのであれば、これらの問題は解決する。
それぞれの機能を一つのサービスとして実装し、それぞれのサービスは個別のチームが運用すればよい。
難しいのは、micro service に分解するのが現実的ではないケースだ。例えば、機能間でやりとりされるデータ量が大きい場合、別のサービスに分けると、ネットワークがボトルネックになったりする。あるサービスを micro service に分解できるかどうかは、機能間のデータ量、同期処理・非同期処理の切り分け、リアルタイム性などに依存する。

Conway’s law という法則があって、意訳すると「システムの構造は組織の構造を反映する」というものだ。


組織の構造をシステムに反映させ(またはシステムの構造にあった組織を作り)、機能・モジュール間のアイソレーションを提供する。これは、エンジニアの数に対してスケーラブルな開発環境を保つために、コアチームが解決すべき重要な問題だと思う。

2016年8月4日木曜日

コードレビューで心掛けていること

Twitter の Ads チームは、これまでに働いてきた環境に比べて、コードレビューをずっと重視している。
4 年間を振り返って、自分が学んだことをまとめておきたい。

コードレビューの目的は、ソフトウェアの品質を高めることだ。品質にはいろいろな観点があるが、正しく動く、メンテナンスしやすい、設計が正しい、の 3 点でカバーできると思う。

正しく動く

この観点でのレビューには、ロジカルなバグ、テストカバレッジ、エッジケースを考慮してるかなどの確認が含まれる。
テストカバレッジのチェックやマイクロベンチマークは、理想的には自動化して、変更のマージ前にチェックできると良い・・・が現実的にはいろいろ難しいのでレビューでカバーすることになる。
具体的には、null チェックしてるか、メソッド中の条件分岐がユニットテストでカバーされてるか、無駄にオブジェクトを生成してないか、適切に例外を処理しているか、などを見る。

メンテナンスしやすい

大きなサービスだと、運用を担当するエンジニアが機能開発をするエンジニアと別(またはサブセット)ということがよくあるので、オペレーションの観点からのレビューが重要になる。
この機能がバグってたときに、サービスとしてどのように振る舞うべきかを考え、サービス全体を落とすべきなのかその機能だけを無効にするのか、その機能が動いていないことをどのように誰に通知するのか、などを考える。メンテナンスという観点では、コードの読みやすさも大切なので、長すぎるコードやおかしな変数名・クラス名は直してもらう。
これが良いことかどうかは別の機会にまとめたい(追記:まとめた)が、運用を担当するエンジニアは、リリースする機能やリリースのタイミングについて強い決定権を持つべきで、同時に、ある機能をリリースする / しないことによるリスク・ベネフィットのバランスを持たなくてはいけない。

設計が正しい

多分これが一番重要で難しいが、「この変更にバグがなくメンテナンス性も高いと仮定して、そもそもこれは正しい設計なのか」という観点が重要だ。
このレビューが難しい理由は、正しい設計を思いつくには、現在・将来のサービス設計、この機能のビジネス的な意味、どういう方向に発展していくのか、といった幅広い理解が必要だからだ。レビューワが変更のバックグラウンドを知っているケースもあるし、優れたレビューワだと「この変更を読んだところ、あなたがやりたいことは XXX だと思うのだが、だったらこのようにコードを書いた方が良いのではないか?」と、コードを読む→変更の理由を理解する→正しい設計を提案する、とリバースアセンブルしたりする。

理想的には、コードを書く前にコミッタとレビューワが議論すると良いのだが、すべての機能開発でそれができるわけではない(コミッタにとっては自明な変更でもレビューワにとってはそうではないことはよくある )ので、コードレビューの時点で差し戻されることもある。上述のとおり、コードを書き直すことのインパクトをレビューワは理解するべきで、なんでもかんでも差し戻せば良いというものでもない。ただし、「このように書き直すには X 日かかるが、リリースが X 日遅れるとビジネス上どういうインパクトがあるのか?」と聞くと「じゃあ書き直すわ」となることは経験上よくある。概算で良いので定量化(X 日)することで議論しやすくなると思う。

この観点でレビューするときに、最終形が明確に見えていないときがままある。「最終的にどういうコードになるかはっきりとはわからないけど、この方向で書き直すと良さそうだ」と感じてコメントするときもあるし、そのようなコメントをもらうときもある。結果として良い設計になることもあるし、「XXX という理由でその方向では書き直せなかった」ということもあるが、「多くの場合に、結果的に正しい設計にたどり着く方針を、最終形が明確に見えてない時点で指摘できる」レビューワは本当にすごいと思う。



何度も書いているが、コードレビューする際には、この機能をリリースすることのベネフィットを理解することが最も大切だ。バグがないこと、サービスが安定すること「だけ」を目標にすると「新しい機能は一切リリースしない」のが合理的な解になってしまう。そうすると、重箱の隅をつつくようなコードレビューになり、コミッタとレビューワが対立することになる。レビューワはコミッタと同じ方向を向いて、「この変更のどこを修正すれば、この機能をリリースできるのか」と考えると生産的なレビューになると思う。

2016年3月29日火曜日

実現可能な最小の Block chain のサンプルコード(未完成)

Block chain についての Lightning Talk を聞く機会があったので、このとても解りやすい記事を読んで、サンプルコードを書き始めてみた。

価値あるモノ(例えば貨幣)をデジタル化した時に問題になるのが double spend (二重支払い)で、Block chain はこの問題を解決しようとしている。例えば、私が $100 のデジタル貨幣(適当なバイナリデータで表現されているとする)を持っていたとして、バイナリデータは簡単に複製可能なので、この $100 を同時に 2 人(以上)に送ることができてしまう。これを P2P の世界で、中央集権的なサーバなしでどのように解決するか、というのがテーマだ。

もし参加者全員が信頼できるならば、これはいわゆる leader election 問題なので、Paxos/Raft で解決できる。しかしここでは参加者が信頼できることを仮定しない。参加者の一部は悪意あるユーザーかもしれない。Paxos/Raft は、参加者が(データをドロップすることはあっても)プロトコルには従うことを仮定しているので、参加者が悪意ある場合には適用できない。

Block chain のアイデアは、「ある命題を主張するためにコストを支払わなければならないとしたら、コストが十分に高ければ、悪意あるユーザーが偽の主張をする経済的メリットはなくなり、システムは安定する」ということだ。たとえば、「Alice は Bob に $100 あげた(ホントは Chris にあげたのに)」という偽の主張をするインセンティブが Alice にはある。もしその主張が認められれば、Bob に $100 の貸しができるからだ。しかしもし、その虚偽の主張のために $100 のコストを払わなければならないのなら、そしてもし Alice が経済合理的な人間なら、そのように主張するはずがない。

このようなシステムが設計できることを示したのが Block chain 論文だ。参加者は契約(例えばお金のやりとり)をネットワークに流し「この契約を認証した人には $1 差し上げます」という。この $1 を欲しい参加者は、proof-of-work という作業を行い「この契約は正しい」とハンコを押すことができる。もちろん悪意あるユーザーが proof-of-work を行って虚偽の主張を認証することもできるが、proof-of-work は CPU を消費する作業なのでコストがかかる。もし参加者の多くが善意のユーザーだとしたら、偽の主張を認証するためには膨大なコストを払う必要があり、そのようなインセンティブはなくなる。



もしかしたらすでに現実的な解はあるのかもしれないが、Block chain にはシステムの安定性の問題があると思う(コンピュータシステムの安定性の話ではない)。ある主張の真偽は、どれだけ工夫しても確率的にしか定まらず、「この主張は絶対に正しい」という状態には到達しない。
突き詰めて考えれば、これは中央集権的なシステムにも存在する問題だ。Alice が Bob の銀行口座に $100 振り込んだ時、もしかしたらその銀行は Bob を騙していて、実際には $100 は振り込まれていないのかもしれない。私たちは単に銀行を非常に高く信頼してるだけの話だ。
とはいっても、銀行を「誰が運営しているかもわからないコンピュータのネットワーク」で置き換えられるのか、その置き換えを心情的に受け入れられるまでどれだけの期間がかかるのか疑問だ。


というわけで、Block chain は、詐欺の被害が小さい少額決済から順に普及していくのではないかなーと思っている。
なんにせよ、この記事はとても面白かった。サンプルコードはインタラクティブな感じにして完成させたい(希望)。