はじめに
皆さんこんにちは、Webエンジニアのtomoです。
コード・テストは言わずもがな重要ですが、それと同じぐらい重要なのが コードコメント です。
コードコメントは、コードやテストとは違い、100%人間のために書くものであるため、「他人が読んだ時にどう見えるか」が非常に重要です。
本記事では、いくつかの記事を参考にしながら、
- コードコメントのアンチパターン
- より良いコードコメントを書くためのtips
について解説していきます。
コードコメントの目的は、コンテキストの共有
まず第一に、コードコメントを書く最大の目的は、「実装者しか知り得ない情報を他の開発者にわかりやすく伝えること」です。
たとえば、チームに週2で働く業務委託のエンジニアがいるとして、その業務委託のエンジニアが見てもなんのことかわかるようなコメントが望ましいです。
逆に基本的な文法など、調べれば簡単にわかるものは共通認識なので、コメントを書くとノイズになります。
よって、良いコードコメントを書くのと同じぐらい、悪いコードコメントを書かないことも重要です。
コードコメントのアンチパターン3選
1. 当たり前すぎるコメント
たとえば以下のようなコメントは、基本的な文法の説明になってしまっているので、コメントとしての意味をなしていません。
// iを1増やす
i++;
これは「コードの再翻訳」といい、わかりやすい会話のための公理として知られる「グライスの量の公理」に反します。
コメントは、必要なことを、必要なだけ書くようにしましょう。
2. リファクタで改善できるコメント
以下のコードでは、ループの説明をコメントで補っていますが、これは本来関数名によって表現すべきです。
// このループは配列内の全数値の合計を計算します
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
良い例はこちら
public int calculateSumOfArray(int[] arr) {
int sum = 0;
for (int num : arr) {
sum += num;
}
return sum;
}
有名な書籍である「リーダブルコード」にも書かれてあることですが、関数名・変数名に情報を過不足なく入れることは、読みやすいコードを書く上で非常に重要です。
コードコメントに逃げず、コードで表現できないか、考えてみましょう。
3. メンテナンスされていないコメント
時が経つと仕様が変わり、コードもコメントも古くなっていきます。
よって、コードを修正するときはコメントも一緒に修正しないと、以下のように誤解を招くコメントを残してしまう恐れがあります。
# ユーザーがアクティブかどうかを判定
def is_user_active(user)
return user.last_login > 1.year.ago # コメントとは異なり、実際には1年以内にログインしているかを判定
end
たとえば関数名を実装に合わせて変更したり、is_user_active
の中のロジックを別の関数として切り出したりして、コメントを常に最新に保ちましょう。
def is_user_active(user)
# 今後、他の活動状況に基づく判定が追加される可能性がある
return user_logged_in_within_one_year?(user)
end
def user_logged_in_within_one_year?(user)
return user.last_login > 1.year.ago
end
より良いコードコメントを書くための3つのtips
1. Why notを書く
Whyはコードから読み取れることもありますが、Why not、つまり「なぜAではなくBを選んだか」は、ほとんど実装者しか知り得ない情報です。
これを他の開発者に伝えないと、
- 他の開発者がコードをリファクタするときに、実装者が想定していないバグを埋め込んでしまう。
- 結果、その実装者に聞かないとコードをリファクタできなくなってしまう。
ということになりかねません。
比較軸が明確に存在するものは、Aである理由だけでなく「BやCではない理由」を残しておくことで、実装者のコンテキストを共有できる良いコメントになります。
# ここではscikit-learnのRandomForestClassifierを使用しています。
# なぜXGBoostやLightGBMを使わないかというと、このプロジェクトではモデルの解釈性が重要であり、
# RandomForestの方が特徴量の重要度を直感的に解釈しやすいからです。
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier()
2. 難しい前提知識のために最も参考となる外部リンクを示す
専門の技術領域や細かい言語仕様など、「知らない方が過半数である」と言い切れる内容については、参考となる外部リンクを添えてあげると親切です。
# ここではPythonのGIL(Global Interpreter Lock)を回避するために、
# multiprocessingを使用しています。詳細は以下のPython公式ドキュメントを参照してください。
# https://docs.python.org/3/library/multiprocessing.html#introduction
import multiprocessing
def worker_function(x):
return x * x
if __name__ == "__main__":
with multiprocessing.Pool() as pool:
results = pool.map(worker_function, range(10))
公式ドキュメントやGitHubのイシューなど、なるべく1次情報に近しいものが望ましいですが、必ずしも2次情報がダメというわけではありません。
他の開発者がコードを読解する時に、最も助けとなるリンクを添えてあげることで、再度検索させて時間を浪費させないようにしましょう。
3. アノテーションコメントを適切に使う
注釈をつけたコメントのことを、「アノテーションコメント」といいます。
具体的には、コメントのprefixに
- TODO
- FIXME
- NOTE
- WARNING
などをつけることで、そのコメントがどんな意味か、一目見てわかります。
また、VS Codeであれば TODO Highlight という拡張機能を入れることで、色がつき目立つので、コードとコメントを区別して読みやすくなります。
それはさておき、それぞれのアノテーションコメントの意味を押さえておくことが重要です。
まず最もよく使うであろうTODOは、「あとで追加・修正する」という意味を表します。
public void calculateTotalPrice() {
// TODO: 税金計算を追加する
// 現在は商品価格の合計のみを計算していますが、税金を考慮する必要があります。
int totalPrice = itemPrice1 + itemPrice2;
}
次にTODOと混同しがちなFIXMEは、「既知の不具合があるコード」に対して使用します。
def divide_numbers(a, b):
# FIXME: 0で割る場合の例外処理が不足している
return a / b
次にNOTEは、「なぜこうなったのか」、つまりWhyやWhy notを示すのによく使います。
// NOTE: この関数は特定の環境でのみ動作します(Node.js v14以降)。
// この関数は`fs.promises` APIを使用しており、それがNode.js v14以降でしか利用できないためです。
const fs = require('fs').promises;
async function readFileAsync(filePath) {
try {
const data = await fs.readFile(filePath, 'utf8');
return data;
} catch (err) {
console.error(err);
}
}
最後にWARNINGは、文字通り「注意が必要なコード」に対して使用します。
// WARNING: このメソッドはxxという副作用を持ちます。呼び出しには注意が必要です。
public void UpdateDatabase() {
// データベースの更新処理
}
他にも、rubyで言えばrdocやyardなど、コードをドキュメント化してくれる仕組みもあります。
アノテーションコメントやコードドキュメント化ツールをうまく利用し、コードに人間的な意味を過不足なく持たせることが重要です。
まとめ
コードコメントはコードの品質を高めるために、非常に重要な要素です。
しかし、その効果はコメントの質によって大きく変わります。
良いコメントを書くためには、その目的を明確にし、アンチパターンを避け、有用なTipsを活用することが重要です。
何かの参考になれば幸いです。