結城です。
OSSプロジェクトへのコントリビュートの「べからず集」記事について、経験の浅い方が「自分のしようとしていることもそれにあてはまるのではないか?」と心配になってコントリビュートをためらうことがないように、具体的な例と考え方を紹介するシリーズの2本目です。今回は、2つ目の「べからず」として挙げられている、コードの一貫性について説明します。
2. 一貫性を壊さない(コーディングスタイルを合わせる)
プロジェクトオーナーが困惑するケース、変更をマージしてもらえないケースでよくあるのが、コードであったり設計方針だったり運営方針だったりと行ったさまざまなレベルで、そのプロジェクトに「馴染まない」「一貫性を壊す」変更になってしまっているケースです。
元のコードに馴染むコードを書けるかどうかは技術力の有無とはあまり関係が無く、「そもそも馴染ませようという気があるか、ないか」という意識の差異が大きいです。別の角度から言うと、コードの書き方、いわゆるコーディングスタイルが統一されていることの意義をどう捉えているか、ということです。
コーディングスタイルを合わせよう
日本語の文章では、全く同じ内容の文章でも、どこに句読点を入れるか・漢字で書くかひらがなで書くか・「プリンター」と書くか「プリンタ」と書くか、といった「表記揺れ」があり得ます。プログラムにおいても、言語によってはそれと同様に、動作の内容自体は同じでも表記揺れが起こり得る部分があります。
たとえば、JavaScriptでは以下のような表記揺れがあります。
-
丸括弧や波括弧などの前後での改行の有無、スペースの有無。
-
if(experssion){
と書くのか? -
if ( experssion )≪改行≫{
と書くのか? -
if (≪改行≫experssion≪改行≫)≪改行≫{
と書くのか?
-
-
省略可能な要素を省略するかどうか。
-
行末のセミコロン(
;
)を必ず書くのか、書かないのか? -
if
での条件分岐で実行する内容が1つだけのとき、波括弧を使うのかどうか?
-
-
インデントの仕方。
-
仮引数の書き方。
- 関数の定義時に、引数の既定の値の定義や、名前付きのパラメータとして受け取る書き方を使うのかどうか?
「そんなの、動けばどっちでもいいじゃないか」と思いますか? だとしたら、それはあなたが、コードの表記が統一されていないことが原因で発生するトラブルを、まだ体験したことがないからでしょう。
実際には、コードの中に表記揺れが多いと、誤読や誤記によって問題が発生する原因になります。また、コードを変更する人がその都度「こっちの書き方がいい」と思った表記に変えてしまうことが繰り返されると、行の変更理由を調べても意味ある情報を得られなくなってしまいます。コードの表記揺れは、百害あって一利なしです。プロジェクトのオーナーは、ただ意地悪や気分で「馴染まない変更」を拒絶するのではなく、プロジェクトにとって現実的なデメリットがあるから拒絶しているのです。
こういった問題に悩まされずに開発に集中できるようにするために、プロジェクト全体で一定のルールに則って書き方を統一し表記揺れをなくすことを、「コーディングスタイルを揃える」と言います。
「外部のコントリビューターがコードの書き方まで揃えるのは大変だから、プロジェクトオーナー側で変更のマージ後に直してくれればいいのに」と思う人もいるかもしれません。実際に、そのような運用を取っているプロジェクトもあります。
しかし、プルリクエストの数が多いと、プロジェクトオーナーはそのような本質的でない作業に忙殺される羽目になってしまいます。また、リポジトリのコミット履歴も本質的でない変更ばかりになってしまいます。「プロジェクトオーナーの手が回らないところを手助けしてプロジェクトに協力する」のがコントリビューションの本質なのに、そんなふうにプロジェクトの運営を妨げてしまっては本末転倒ですよね。
前述のように機械的な判別が容易な部分のコーディングスタイルは、昨今のOSSでは、ソフトウェアで自動的に揃える運用を取っていることが多いです。prettierやESLintはそのようなことをするソフトウェアの代表的な例ですし、VisualStudio Codeなどのテキストエディターを適切に設定していれば、ファイルの保存時に自動的にコーディングスタイルを揃えさせることもできます。
そのような運用を取っていないプロジェクトでは、コーディングスタイルを意識して合わせる必要があります。
歴史の長いプロジェクトでは、GNUのコーディング規約やMozillaのコーディング規約のように、文章の形でコーディング規約を明記している場合があります。
そういった規約が特に定められていない場合は、そのプロジェクトの他のコードを見て、変更を加えた箇所のコードの書き方が他の箇所と同じになるように揃えることになります。つまり、変更対象の既存のコードを、変更箇所以外も含めてもう少し読み込んで、傾向を把握する必要があります。
また、これは本題から外れますが、コーディングスタイルをソフトウェアで自動的に揃える運用をまだ取っていないプロジェクトでは、現在のコーディングスタイルを強制するルールをまとめた上で、自動化の仕組みを導入する提案をしてみてもよいでしょう(当然、これはその変更だけの単独のプルリクエストにすることを強くお勧めします)。
語彙の選び方、表現の選び方を合わせよう
多くのOSSのコードでは、クラスや変数などの名前付けに英語の単語や熟語が使われています。同じことを言い表すのにも、考え方次第で色々な表現の仕方があり、どのような表現・表記を主に使うかはプロジェクトによって方針が異なります。特にポピュラーな表現のパターンを、以下にいくつか挙げてみましょう。
複数のデータを保持する変数の名前付けには、いくつかのパターンがあります。
-
itemsなどの、データの内容を表す単語の複数形。
-
itemArray、itemListなどの、データの内容+データを保持している構造を表す接尾辞。
似た例として、個数や件数などの*「数」を保持する変数やプロパティの名前付け*には、以下のようなパターンがあります。
-
item_count
-
item_length
-
item_size
-
n_items(数学や物理で「N個の」といった言い方をすることにちなむ表現)
関数名・メソッド名の動詞にもパターンがあります。たとえば、原形にするか現在形にするかは、何をさせる機能かという目的で変わることが多いです。
-
String#split(分割する)などの原形:データに変更を加える、何かをさせるとき。
-
String#includes(部分文字列を含むかどうか)などの三人称単数形:データの状態を問い合わせ、真偽値で結果を返すとき。
特定の文脈で使われやすい単語や表現にも、いくつかの流儀があります。状態を問い合わせる場面では、以下のような例があります。
-
isItem / hasItem:Yes/Noで答える疑問文のように名付ける。
-
getItemState:状態を尋ねる、という趣旨をそのまま名前に採用する。
-
valid?:Rubyのコードでよく見られるパターンで、真偽値を返す場合の接尾辞として「?」を使う。
データの取得や保存に関わる場面でも、以下のような例があります。
-
getItem / setItem:値を取得したり変更したりする物について、set-getのペアで名付ける。
-
readItem / writeItem:ファイルなど何らかのデバイスやストレージに対してデータを読み書きする場合に、そのことを強調するように名付ける。
-
fetchItem / downloadItem / pushItem / uploadItem :データをネットワーク越しに取得・保存する場合に、そのことを強調するように名付ける。
-
save!:Ruby on Rails(のActiveRecord)に見られるパターンで、破壊的な操作をしたり、操作を強行したりする場合の接尾辞として「!」を使う。
また、複数の単語からなる識別子の名前付け自体にも、以下のようなパターンがあり、それぞれよく使われる場面があります。
-
SomethingName:各単語の先頭1文字を大文字にする。Pascal Case3、またはUpper Camel Caseと呼ばれる。クラス名で使われることが多い。
-
somethingName:2つ目以降の単語の先頭1文字を大文字にする。Camel Case4、またはLower Camel Caseと呼ばれる。
-
something_name:単語同士をアンダースコアで連結する。Snake Case5と呼ばれる。
-
something-name:単語同士をハイフンで連結する。ケバブケース6、チェインケース、ハイフンケースなどと呼ばれる。ファイル名で使われる。
-
SOMETHING_NAME:単語をすべて大文字にして連結する。定数でよく使われる。
こういった点の名前付けの流儀を合わせることで、変更箇所のコードがより元のコードに「馴染む」ようになり、プロジェクトオーナーにとっても、よりマージしやすいプルリクエストになります。
プロジェクトによっては、こういった名前付けについてもコーディング規約で定めている場合があります。そのような明示的な規約がない場合は、やはり、変更対象の部分やその周囲の部分など、既存のコードを読んで傾向を把握する必要があります。
コミットログの書き方も揃えよう
コードの変更そのものとは無関係ですが、コミットログの書き方の様式も、そのプロジェクトの流儀に合わせるようにしましょう。プロジェクトによってはコミットログをリリースノート作成時の参考資料にすることがあり、あまりに様式から外れていると、リリース時に開発者に余計な苦労を強いることになってしまいます。
コミットログは基本的には、5W1H7のうちの「Why」にフォーカスをあてて書きます。たとえば以下のような要領です。
-
「ユーザーがグループに所属していないときにログインに失敗する問題を修正」
-
「チケットの作成時に空のフィールドを許容しないように変更」
このような情報が含まれないコミットメッセージは、あまり喜ばれません。たとえば以下の要領です。
-
「変数をリネームした」(何のために?)
-
「チェックを追加した」(何のチェックを、なぜ?)
-
「アルゴリズムを変更した」(どういう意味があって?)
プロジェクトによっては、リポジトリに含まれるドキュメントの修正のコミットメッセージ冒頭には「doc:
」、自動テストの修正のコミットには「test:
」、といった印(プレフィクス)を付けるよう運用で定めている場合もあります。
コードの変更に取りかかるときは、その前に、開発への参加の仕方についての説明があればそれを参照して、運用ルール自体を把握するとよいでしょう。他の変更がどんなコミットメッセージでなされているか、実際のコミットログを確認するのもおすすめです。
まとめ
以上、「一貫性を壊さない(コーディングスタイルを合わせる)」という原則について、実例を挙げて解説してみました。
この記事で紹介した内容以外にも、言語を問わず「読みやすさ」という観点にフォーカスしてコードの語彙を紹介している本として、「リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック(Theory in practice)」(オライリージャパンより、2012年刊行)(解説)があります。(この本の解説は[]で読めます。)この本を読んでからOSSのコードに触れると、「あっ、この書き方は読みやすさのための工夫をしている!」という箇所に気付きやすくなり、プロジェクト内でどのような流儀を採用しているかをより読み取りやすくなるでしょう。
この解説は、OSSへのコントリビューションを増やすことを意図した取り組みであるOSS Gateで開催しているワークショップの中で得られた知見をまとめた本、「これでできる! はじめてのOSSフィードバックガイド」の一部を抜粋・再編集した物です。本編ではこのほかにも、問題の報告の仕方やありがちなミス、フィードバック初心者の方が戸惑いがちな点について、なるべく具体例を示しながら、幅広く解説してみています。リンク先では原稿の全文を公開していますが、手元に置いて参照しやすい形式での販売も行っていますので、読書スタイルに合った形式で参照して頂ければ幸いです。
OSS Gateでは、新型コロナウィルスの感染拡大防止の観点から、現在は東京地域を主体としたワークショップをオンライン(Discord)で開催しています。次回開催予定は10月31日(土曜)10:30からで、ビギナー(ワークショップで初めてのフィードバックを体験してみたい人)・サポーター(ビギナーにアドバイスする人)のどちらも参加者を募集中です。ご都合の付く方はぜひエントリーしてみて下さい。
また、当社では企業内での研修としてのOSS Gateワークショップの開催も承っています(例:アカツキさまでの事例)。会社としてOSSへの関わりを増やしていきたいとお考えの企業のご担当者さまは、お問い合わせフォームからご連絡頂けましたら幸いです。