大規模プロジェクトでのコード品質保証





2012年の秋にAirbnbに来たときは、控えめに言っても、混乱とよろめきがありました。 少し前に、会社は途方もないペースで成長と発展を始めました。 これは主にトラフィックとトランザクションの量で表されました。 このすべてに対処するために、開発者のスタッフも非常に迅速に増加しました。 私がグループに参加する前の年には、16人がいましたが、私は約40人で、現在は130人を超えています。そして、これらすべてのプロセスによって引き起こされる主な問題の1つは、急速に成長し複雑なプロジェクトでコードの品質を維持することでした。



振り返ってみると、これは当社の歴史のターニングポイントだったように思えます。 爆発的な開発により、Airbnbの技術的および文化的な課題が数多く発生しました。 以前は多くの困難なタスクを解決する必要がありましたが、一般的には次のことに関連していました。



•さらなるスケーラビリティを期待せずに作成されたRuby on Railsアプリケーション全体のスケーリング、

•前の問題を解決するための開発チームの増加。



これで、多くの成長の問題に対処できたことは明らかです。 そしてここで、より便利で、保守しやすく、より技術的なコードを書くことに移った方法についてお話したいと思います。 そして、結果として、スケーリングにより適しています。



今私は支払い処理するモジュールを開発しているグループで働いています。 したがって、私たちにとって、コードの品質、その安定性、およびメンテナンスの容易さが重要です。 トランザクションの量を考えると、小さなエラーでさえも受け入れられません。 過去1年間、私たちはルールとテクニックを開発する素晴らしい仕事をしてきました。それにより、コードを迅速かつ同時に高品質で書くことができます。 まず、コーディング時の特定のスタイルと条件の確立と遵守、ピアによるコードの定期的なピアレビューとテストについて話します。



コードの一貫性



コードの一貫性は、構文スタイルと、作成時の規則とテクニックの遵守に依存します。 これについて何度も読んで聞いたことがありますが、繰り返します。コードベースからファイルを開き、スタイルによって同僚が書いたものを判断できる場合、これは非常に悪い兆候です。 内部コーディング標準の開発とすべてのチームメンバーによる遵守は、私たちの作業に非常に有利な影響を与えました。



一般的な意味では、コンピューターコードは、マシンと人間の2つのクラスのエンティティによって消費されます。 それでも、問題なくコンパイルできるのであれば、マシンはコードの見た目を気にしません。 しかし、開発者は気にしません。 チームがさまざまなスタイルとアプローチを使用してコードベースの問題を記述および解決すると、すべての参加者に過度の認知的負荷がかかり、自分で作成しなかったコードを長く理解することになります。 グループのすべてのメンバーが多かれ少なかれ似た方法でコードを記述すると、デバッグが大幅に簡素化され、他の人のコードを維持することがはるかに簡単になります。 コードを読みやすくするため、またはコードを単純にするために、いくつかの決定を支持して選択する必要があるという意味ではありませんが、チームのすべてのメンバーによるコーディングプロセスを統一する方法は十分にあります。



Airbnbでは、2年間コードの一貫性を改善しています。 まず、スタイルガイドを開発しました(このガイドにはいくつかのバージョンがありました)。 最も詳細なJavaScriptおよびRubyガイドは、 RSpec 、API設計、サービス設計などにもあります。 これらのガイドは特定の要件と条件に基づいているという事実にもかかわらず、それらの多くは現在、他のチームと単一の開発者の両方によって使用されています。 もちろん、これらのガイドの潜在的な有用性は、作業しているアプリケーションの種類によって異なりますが、JavaScriptまたはRubyで開発している場合は、とにかくガイドに慣れることをお勧めします。 少なくともインスピレーションのため。



一部のルールは、当社が独占的にarbitrarily意的に受け入れています。 たとえば、インデント(タブとスペース)、または開始ブレースを配置する行。 これらおよび他のポイントは好みの問題です。 関係者全員がルールを尊重することが重要です。 一方、場合によっては、オプションが可能です。



文字列の長さを取得します。 行を短く説明的にすることを好みます。 短い行は読みやすいだけでなく、演算子をより単純な形式で指定することもできます(特に説明的な変数名で使用する場合)。 一連の短い単純な演算子で構成されるメソッドは、読み取りや変更が簡単です。 結果としての差分でさえ、コミット間で何が変更されたかをより正確に示します。 そして、小さく簡潔なメソッドを含むテスト可能なコードを書いているので、これは非常に「クリーン」なモジュール式で理解しやすいコードベースの作成を促進します。



例を考えてみましょう:



usernames = Users.where(:status => 1, :deleted => false).map{ |u| u.first_name.downcase }.reject{ |n| n == 'john' }.map{ |n| n.titleize }
      
      







ケースを変更する必要があるとしましょう。 Diffは次のように表示されます。



 - usernames = Users.where(:status => 1, :deleted => false).map{ |u| u.first_name.downcase }.reject{ |n| n == 'john' }.map{ |n| n.titleize } + usernames = Users.where(:status => 1, :deleted => false).map{ |u| u.first_name.upcase }.reject{ |n| n == 'JOHN' }.map{ |n| n.titleize }
      
      







あなたの頭の中の線を蒸すことなく、正確に何が変わったかをヒントなしで言うのは難しいです。 そして、コードが元々このように書かれていた場合:



 users = Users.where(:status => 1, :deleted => false) usernames = users. map{ |user| user.first_name.downcase }. reject{ |name| name == 'john' }. map{ |name| name.titleize }
      
      







diffのその違いはより明確になります。



 users = Users.where(:status => 1, :deleted => false) usernames = users. - map{ |user| user.first_name.downcase }. - reject{ |name| name == 'john' }. + map{ |user| user.first_name.upcase }. + reject{ |name| name == 'JOHN' }. map{ |name| name.titleize }
      
      







おもしろいですが、最初にこのアプローチを促進した私たちは、最初に同僚の抵抗に遭遇しました。 私はしつこくならなければならず、その結果、短い線を引くことは私たち全員の間で習慣になりました。 このテーマに関するより極端な例は、 Joint Strike Fighter C ++およびJPL C Codingガイドで見ることができます 。 明らかに、これらの標準はほとんどのコンシューマーWebアプリケーションにとって冗長であるため、特定のルールを遵守することで、プロジェクトのレベルと達成したい目標を常に関連付けます。 バランスを保つことが重要です。



前述したように、APIとサービスを開発するためのガイドも作成しました。 そのようなことを事前に明確に規定することは常に可能ではないという事実にもかかわらず、チームの一般的な知識を単一のドキュメントに統合することは非常に有益です。



同僚によるコード検証



同僚による定期的なコード監査は、コードの一貫性を向上させる第2の礎となっています。 このプロセスの最も重要な利点は、次のリリースに入る前にあらゆる種類のバグを迅速かつ効率的に検出できることです。 しかし、相互監査には他にも多くの小さな肯定的な側面があります。 過去1年間、コードの変更ごとに監査を実施してきました。



私たちは、コードまたは機能のこの部分に何らかの形で関与している全員を監査に関与させようとします。 その結果、最も重要なプル要求に従って、少なくとも1つまたは2つの独立した議論が生じます。 時々、解析プロセスが進みすぎて、一部の人は以前は知られていないこと(たとえば、クレジットカード処理、内部データベース編成、暗号化など)を調査する必要があります。 コードの変更がいくつかの重要な問題に関係する場合、適切な推奨事項とドキュメントを作成します。 確認するとき、私たちは自分の地位や立場に異議を申し立てることはできません。 チームのメンバーの貢献に感謝し、最良の決定が戦闘に展開されます。 ところで、これは初心者にとっては良い学校です。



推奨事項が存在するからといって、推奨事項に従う(または少なくとも読む)ことを意味するわけではなく、あらゆる場合に適していることを忘れないでください。 しかし、復習は相互支援という点で非常に効果的であるだけでなく、プログラミングの技術を誰かに学習または教えるための素晴らしい方法でもあります。これは通常の教科書では難しいことです。 そして、人々が仕事で多くを学ぶとき、それは彼らの関与を促進し、仕事の質を改善します。



主な役割がコーディングスタイルではなく、賢明なアプローチによって行われる状況の例を次に示します。



 rus_users = User.active.select{ |u| 'RUS' == u.country } puts “There are #{rus_users.length} RUS users today”
      
      







そして



 rus_users = User.active.where(:country => 'RUS') puts “There are #{rus_users.count} RUS users today”
      
      







最初のオプションはよりリソースを消費し、大量のデータを使用すると、パフォーマンスが壊滅的に低下する可能性があります。 User.active



オブジェクトにselect



を追加select



ことは、RailsがMySQLにアクセスし、すべてのアクティブなユーザーを呼び出してインスタンス化し、配列に入れて反復し、国がRUSに対応するユーザーを選択することを意味します。 そして、これはすべてそれらを数えることです。



2番目の例では、 User.active



オブジェクトから始めますが、whereフィルターを適用します。 最初の行は、データベースクエリを開始しません。 2行目で、カウンターを要求するとき、RailsはSELECT COUNT(*)



要求のみを行い、呼び出し線やモデルのインスタンス化を気にしません。



別の例を次に示します。



悪い:



 amount = Payout. where(:successful => true). where('DATE(timestamp) = ?', Date.today). inject(0) { |sum, p| sum += p.amount }
      
      







良い:



 amount = Payout. where(:successful => true). where('DATE(timestamp) = ?', Date.today). sum(:amount)
      
      







最初の例では、検索を1日に制限していますが、この場合でも、1つのフィールドを合計するために、多数のオブジェクトを呼び出してインスタンス化する必要があります。 2番目の例では、MySQLは検索中に合計を処理し、必要な値を単純に返します。 一般に、これによりMySQLに追加の負荷が生じることはありません。不必要なネットワークトラフィックを生成せず、Rubyでの膨大な計算を回避します。



上記の例は、サイトの応答性を改善し、クラッシュを防ぐ方法を示しています。 コードの特定の部分を議論する際、従業員はデータベース構造や財務レポートの生成など、さまざまなトピックを定期的に提起するため、ピアレビューも優れています。 つまり、活発で生産的な対話がコードの周りで絶えず発生し、その間に私たちは自分自身を学び、他の人に教えます。



テスト中



この問題については詳しく説明しません。これは、ブログ投稿の1つに非常によく書かれています。 テストは、維持に便利な高品質のコードを確保するという点で非常に重要なプロセスであると言えます(そして、これがあなたにとって新しいものになることはまずありません)。 しかし、ポイントはテストでコードを最大限にカバーすることではありません。主なことは、テストカルチャーを作成して、各チームメンバーの条件反射になります。 テストを書くことが習慣である場合、テスト可能なコードを作成することも習慣になります。



おわりに



言われたことすべてを要約します。



プロの開発チームのすべてのメンバーは、それがどんなに大きくても、事前に承認された同じスタイルでコードを記述する必要があります。 これは、成功したアプローチと手法を広く使用するのに役立ち、他の人によるコードの保守と保守を容易にします。 より良いスタートは、コーディングスタイルガイドを作成することです。 一見単純で明白なものでさえ、コードの品質、安定性、リファクタリングの容易さに大きく影響します。



お互いのコードを積極的かつ建設的にレビューする必要があります。 作成者に加えて、少なくとも1人がすべての差分を表示する必要があります。 友好的な提案の提出とコードの検証は、チーム構築と専門家の成長の鍵です。 これは、初心者がより速く、より完全に学び、有用なスキルと習慣を身につけるのに役立ちます。 最終的に、深刻な転倒からアプリケーションを救うことができます。



妥協したり、道を切ったりするのではなく、強力なテスト文化を開発することが重要です。 テストすればするほど、習慣になります。 その結果、このプロセスを愛することさえできます。



All Articles