POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

Nikhil Garg

本記事は、原著者の許諾のもとに翻訳・掲載しております。

高品質のコードベースは、反復作業やコラボレーション、メンテナンスを簡単にすることで、長期的な開発のスピードを上げてくれます。Quoraではベースコードの品質は重要だと考えます。

高品質のコードを維持することは利点がありますが、その反面かなりのオーバーヘッドが発生し、実際の開発のサイクルに時間が掛かってしまいます。このオーバーヘッドと利点の折り合いを付けるのは難しい問題です。この場合、2つの選択肢しかないように思えます。低品質でコードスピードが速いか、もしくは高品質でスピードが遅いか。スタートアップは素早い開発サイクルに最適化しているので、多くの人は低品質で進めたほうがいいと思っています。

このジレンマは解消できます。ツールやプロセスを工夫することで、コードベースの品質を維持したままスピードを速めることができるのです。この投稿では、コードの品質に関しての私たちの考えや、2つの世界を共存させるための具体的な方法をご紹介します。

コードの品質維持で目指すもの

高品質のコードの主な利点は、長期的に見て開発のスピードが上がることです。ここに焦点を当てましょう。長期的な利点と、目の前のコードをよりきれいに書かなければいけないという短期的な苦労の折り合いをつけるのです。

これを念頭に置き、私たちはコードの品質に関して役立つであろう4つの原則を考えました。

コードの品質に関するQuoraの原則

  1. コードは読んで分かりやすくあるべきである。 コードは書くよりも、読むほうが大幅に時間が掛かります。ですから、多少書く時間が増えたとしても、読む時間は最小限に抑えるべきです。
  2. コードのパーツによって、品質の基準も変化すべきである。 コードはそれぞれの行で、寿命、スコープ、壊れるリスク、壊れた場合のコスト、さらにはそれを直す場合のコストなどに関して、それぞれの特徴を持っています。つまり、長期的な反復作業のスピードには、全く違う影響が出てきます。ですから、同じ品質基準を全てに適用することが最適とは限りません。
  3. コードの品質のオーバーヘッドは縮小できる。 高品質のコードを維持すると発生するオーバーヘッドは、自動化やより良いツール、プロセス、開発者の教育によって、減少させることができます。
  4. ベースコードの整合性は重要である。 コードベース全体で整合性を保つ、という行為には価値があります。例え局所的にみると最適とは言えない「準最適」なコードになってしまったとしても、です。整合性のないコードベースは、読んで理解したり(1を参照)書いたりすることが難しく、自動化されたツールを使用してもなかなか改善できません。

これらの原則を応用している開発プロセスを、具体的に紹介します。

コミット前のコードレビュー

コードベースで変更されたコードは、正確さ、プライバシー、パフォーマンス、アーキテクチャ、再利用性、スタイルという6つの次元からピアレビューされます。コードを読むことは、コードレビューでは重要なパートです。そして、コードレビューによってコードの読みやすさを大幅に改善することができます。

しかし残念なことに、コードレビューは開発のスピードを遅くさせてしまうという特徴もあります。例えば、コードをプッシュ前にレビューして修正するコミット前のコードレビューは、業界内では普通のことです。たとえ、レビューに2日間掛かり、2~3回ほど繰り返さなければならないとしても、のちに1週間まるまる時間を使わなければならなくなるよりはマシです。

Quoraでは通常、コミット後のコードレビューを行っています。つまり、コードを 先に実行させてから 、あとで誰かがコードをレビューします。分かりやすく説明するために、昨日48人でコードを合計187回プッシュしました。コミット後のレビューでは、開発者が自由にコードをプッシュできて、他の作業に移ることもできるので、優れていると言えます。コードレビューを行なう人にもメリットがあります。時間を調整しやすく、レビューをする人の都合のいい時間に行うことができます。誰かを待たせて、急いで行う必要はありません。コードレビューの期限は1週間以内であることが多いですが、うまくやれば、実際には1~2日で作業は終わります。ですから、1週間の間で都合のいい時を見つけて作業すればいいのです。しかし、もしレビューして悪い結果が出てしまった時のことを考えるとその期限では短いです。別の人に読んでもらったり、コードベースの別の場所で質の悪いコードが広がってしまうことを考えると足りないかもしれません。さらに実際は、多くの開発者が他のルーティーンワークを行わなければいけないという事実もあります。

私たちがコミット後にレビューを行えるのは、1人1人の開発者を信頼しているからです。私たちは優秀な人材しか採用しませんし、採用後は彼らのコードの品質に関する教育やツールに力を入れています。このようなやり方を行うことで、コードレビュー前に自分のコードを丁寧にテストして、正確性を高めようという意識を持つことができます。加えて、私たちは、堅牢で高度に設定可能な Phabricator というレビューツールを使っています。 コミット後のレビューのワークフローでさらにうまくいくように、Phabricatorでいくつかの変更を実装しました。例えば、本番用にコードをプッシュし、レビューをリクエストするためにコマンドラインのツールを作りました。このツールは、コミットの修正をしなくてもPhabricatorが動作するように設定されています。

そして、私たちは変更の種類ごとに、異なったコードレビュー基準を実行しています。、エラーや失敗によってコストがかかり、致命的な問題が起こる可能性がある時には、普段行っているコミット後のレビューの代わりに、コミット前のレビューを行うことにしています。以下のような場合です。

  1. ユーザのプライバシーや匿名性を扱うコードに触れる時。
  2. 多くのコードが依存している、コアな抽象化に触れる時。
  3. 間違えることでダウンタイムを引き起こす可能性のあるような基盤の一部に触れる時。

また、これは開発者の判断にもよります。多くの意見を得るためにコミット前のレビューを行いたい場合は、レビューを通常のコミット後ではなくコミット前に行うこともあります。あまりこのようなことはありませんが。

コードレビューのルーティング

コードレビューを成功させるためには、それぞれの変更を、その変更に詳しい人がレビューするべきです。コードレビューする人が、そのコードを維持する責任を負ってくれることが望ましいです。その方が長期的には折り合いがつきます。

私たちはファイルの冒頭にmetaタグを付けるだけでモジュールやディレクトリレベルの所有権が明確になる、シンプルなシステムを導入しています。以下が例です。

1   __reviewer__ = 'vanessa, kornel'

コミットでファイルが変更されたら、レビュアーはファイル(または家系図)からパースされ、コミット時に自動的にレビューアとして追加されます。また、コードレビューを正しいユーザに割り当てるための追加ルールもあります。例えば、A/Bテストを行うためのコミットがなされて、そのレビュアーが誰もいない場合、データサイエンティストが自動的にレビュアーとして追加されます。事実、私たちはツールをセットアップして、そのようなカスタムされたルーティングのルールを簡単に追加できるようにしています。例で示したように、希望すれば、新人の行なったあらゆるコミットをメンターにルーティングするルールを追加することも簡単です。コードレビューの適任者にコミットをルーティングする、スマートなシステムを持つことで、作成者が適切なレビュアーを探すという手間が少なくなり、全てのコミットを最適なレビューアが担当するようになります。

テスト

テストは開発のワークフローの中でも重要な部分です。私たちはたくさんのユニット、関数、UIテストを幅広く書きます。包括的なテストを行うことで、開発者が現在存在する機能を壊してしまうかもしれないという心配をせずにスピードを上げることができます。私たちはテストを書いて発生するオーバーヘッドを減らすため、( nosetests の上に構築した)テスト用フレームワークを簡単に素早く使えるようにすることに多くの時間を費やしてきました。

また、私たちはテストを自動化するいくつかのツールを開発してきました。以前書いた continuous deployment system(継続的なデプロイシステム) という投稿で述べたように、中心のサーバは、新しいコードをデプロイする前に全てのテストを実行します。テストを実行しているサーバは高度に並列化されているため、一連の全てのテストをしても5分で終わります。作業が早ければテストを書いて実行することを頻繁に行う気になるでしょう。私たちは”test-local”というツールで、作業中のコードのコピーの変更をテストするため、テストに関連するテストファイルを識別して実行します。これを成功させるにはテストはモジュール式である必要があります(モジュール式は、テストが停止した場合に、素早くデバッグするために役立ちます)。いいテストを維持するため、私たちはテストを書くためのベストプラクティスを書いたドキュメントをシェアしています。これらのガイドラインはコードデビューの際に厳格に実行されています。

コードビューと同様、テストにも変更の種類によって別の基準を設けていますし、壊れた場合のコストが高いテストには高い水準を設けています。

これらのシステムを合わせれば、少ないオーバーヘッドで長期的に開発サイクルを節約できるテストを書くことができます。

コードの品質のガイドライン

私たちは、コードの品質の基準に関してのガイドラインをシェアするのが大好きです。なぜなら以下のようなことがあるからです。

  • 新しい開発者向けの教育のいいツールとして使える。
  • チーム全体で知恵やベストプラクティスを共有できる。
  • コードベースの整合性を維持するための共通の基準を設定できる。
  • 開発中やコードレビューの際にオーバーヘッドを縮小できる。例えば、コードレビューのたびに1行を80文字にするか100文字にするかなどと話し合う必要はありません。一度話し合った後に具体化させていけばいいのです。

様々な言語の構文のスタイルガイドに加えて、私たちはコードを理解する時間を減らすため、優れたテストを書き、モジュールを構築するような、より抽象的なガイドラインも設けています。これらのガイドラインは、コードを書くスピードとコードの品質の折り合いの付け方を理解するたびにどんどん進化していきます。また、私たちはガイドラインを変更した後に、以前作業したコードを全て修正する場合に備えるため、巨大なリファクタリングツール( codemod のようなオープンソースのツールや、社内で開発したツール)を手元に準備しています。

古いコードのクリーンアップ

フットワークの軽いチームは様々なことを試してみるものです。当然ながら、うまくいくことも、いかないこともあるでしょう。結果的に、こうしたチームのコードベースには時間が経つにつれて粗悪なコードが大量に発生します。粗悪なコードはこの先使用されることなく残り、コードベースを複雑にします。これをクリーンアップすることで、コードベースは健全な状態に保たれ、開発のスピードが速くなります。

Quoraでは定期的に”クリーンアップ週間”を設け、粗悪なコードを除去しています。この期間は特定のチームや、場合によっては会社全体でひたすら古いコードのクリーンアップを行います。この定期的なクリーンアップのおかげで”通常業務”と”クリーンアップ業務”の切り替えに伴うコストが削減されるだけでなく、クリーンアップ作業自体をイベントとして楽しむことができます。

コードベースの一部が他に比べてクリーンアップしやすい、ということはよくあります。同様に、コードベースのある一部をクリーンアップすることで、極端に開発スピードが遅くなる場合もあります。クリーンアップに費やす時間を無駄にしないため、私たちはクリーンアップのためのコストとクリーンアップが開発スピードに与える影響に基づき、モジュールに優先順位を付けています。

Lint

ワンオフのインスタンスがコードの構文に関する品質ガイドライン(docstringの形式や行の長さなど)に従わないことで生じるコストは軽視されがちですが、これは時間が経つにつれて増えていくものです。しかし、特にルールの数が増えていくと、ガイドラインを念頭に置いて様々なルールを適用するのは面倒です。その結果、この業界の多くの人がガイドラインに従わなくなってしまうのも仕方のないことかもしれません。

こうした面倒を減らすため、私たちはqlintという内部向けlinterを開発しました。qlintは flake8Pylint をベースにしたスマートなlinterで、テキストの構造だけでなくASTも理解できます。また、独自のlintルールの追加も簡単になっています。たとえば、Pythonのプライベート変数の先頭にアンダースコアを付ける必要があるという規則に従い、このようなプライベート変数が不正に使用されている場合に検出するというルールをqlintに追加しました。

lintエラーを監視しなくても済むように、qlintにはシームレスな開発環境を提供する様々なシステムを統合しています。初心者向けには、おなじみのVim、Emacs、Sublimeなどのテキストエディタが完全に統合されていて、ルールに違反があった場合にはエディタ内にフィードバックが(赤いマークで)表示されるようになっています。また、プッシュ処理も統合されており、誰かがコードをプッシュしようとするたびに、(インタラクティブモードで)実行されます。実際、あるコミットが違反しているルールによっては、そのデプロイがブロックされる場合もあります。全てのlintエラーから関連するスタイルガイドのルールへのハイパーリンクが出力されるように、qlintにはQuoraのスタイルガイドも統合されています。 Phabricator でもqlintを使用できます。これにより、qlintで検出された全てのエラーにフラグが設定されるため、コードレビューが簡単になります。

私たちはこうした変更を行ったおかげで、ほとんどコストを掛けずにコードの整合性と品質を向上させることができました。

まとめ

この投稿で述べたように、Quoraではコードの品質を重要視しています。実用性を追求し、長期的な開発の生産性を高めて維持するための優れたシステム、ツール、プロセスを構築しています。チームは絶妙なバランスで常に成長し、進化し続けています。今後もより多くのツールやシステムが生み出されていくでしょう。

こうしたシステムを構築したい方、もしくは実用的かつ入念な方法でコードの品質を高める開発チームに加わりたい方は、ぜひ 私たちの仲間になってください