Coding hygiene, photo by Jernej Furman (https://www.flickr.com/photos/91261194@N06/49820964568)

2021年11月19日

Malik Olivier Boussejra

コード衛生 (Coding Hygiene)

trivia

コード衛生 (Coding Hygiene)

今回の投稿では,信頼性の高いアプリケーションを速くリリースするための「グッドプラクティス」を紹介します.

無論,全てには例外があるとはいえ, このプラクティスは信頼性の高いソフトウェアを速くリリースしたいチームにとっていい基本となると思います.

継続的デリバリー (Continuous Delivery, CD)

メインのブランチにマージするコードなら,そのコードが運用に適していなければいけません. コードがメインなブランチに含まれる時点から,テスティングのため, 一日以内にステージングサーバにリリースされ,そのあと間もなく運用サーバーにリリースされ, お客様に届くと考えるべきです.

大きいリリースはしません.逆に 小さいリリースをいっぱいします

毎クオーター2・3週間ぐらい大きい機能と多数のバグ修正をテスティングするよりも, 毎日一時間程度,機能およびバグ修正の細かい部分集合をテスティングしたほうがいいです.

可読性第一

成熟したソフトウェアを改修するとき,エンジニアの工数の約80%はコードを読むことです.

なので,コードの可読性はとても大事です!

Keep It Simple, Stupid

KISS:シンプルで愚鈍にしろ.

  • 本問題を解決するため,一番簡単な実装はこの実装か?
  • 誰でも構成を読んで理解できるか?

コードを書くなら,常にこの2つの質問を頭に入れておくと良いです. 何よりも,余計に複雑なデザインに気づかず陥りやすいです.

いつも,戻って簡単化しましょう.

報酬が書いた行数に依存する訳ではありません. むしろ,機能の損失なくモジュールの行数を減らすことは、優秀なプログラマーの特徴です.

コード量と不具合の数は比例します. よって,コードが少ないほど,不具合も少ないでしょう.

関係しているコードを一緒に入れましょう

あるコードの断片を理解するために全然違うディレクトリの全然違うファイルに飛ばなけれいけないことほど,コードを読んでいるときに不便なことはありません.

関係しているコードを一緒に入れましょう. 理想のコード構築は,1つの論理ユニットがスクリーンの縦(20行ていど)に収まるべきです.

重複を避ける

誰も同じバグを10箇所以上修正したくありません.

2回だけ重複されているコードなら,コードの断片がそこまで長くなければ,重複のままでいいかもしれません. ただし,重複が2回を上回るなら,リファクタリングをしたほうがいいですね. この概念をよく rule of three と言います.

ステートレスになろう

アプリのほとんどの不具合はステートの不具合が原因である.

最低限のステートで仕様を満たしましょう. できるなら,ステート(特に可変なステート)は避けるべきです.

常に 信頼できる唯一の情報源 (Single Source of Truth) を守りましょう.誰も同じ値を数箇所で編集したくありません. 自分や仲間が,一箇所でも編集し忘れればひどい不具合が生じます.

コメントアウトのコードは厳禁

コメントアウトされたコードは負債のもと.

運用環境にコメントアウトしたコードをプッシュしてはいけません.

そのコードの存在意義は誰にもわかりません.コメントアウトされた理由も誰にもわかりません.

あなたもコメントアウトにした理由を数ヶ月以内に忘れてしまうでしょう.

コードスタイリング

作業中のプロジェクトのリンターとフォーマッターを使いましょう. 文句は無用.

チーム内の規約と統一性は個人の好みよりも重要です. いずれ,最初は嫌でも時間が経つとフォーマッターのルールになれてしまいます.

命名の規約

命名の規約を守りましょう.ケースも含みます (camelCase, PascalCase, snake_case, kebab-case, ALL_CAPS)が,名詞の単数形・複数形,動詞も含みます.

可読性とコンテキストに集中しましょう. 例えば,ステートを変更する関数を書く場合,そのアクションを表現する動詞を使うと良いです.

わからない場合,改修中のブロックの周りのコードを参考にし,同じようなルールを守ると良いです.

コメント

  • 良いコメントを良い
  • 悪いコメントは悪い
  • コメントは明確で有用で簡潔であるべき.
  • コメントはコメントしているコードの隣にあるべき.
  • コメントは正しくなければなりません
  • コメントは保守しなければいけません

コメント無しは,悪いまたは誤解を招くコメントよりマシです.

*Self-documenting code*も書くと良いです.

物語コメント

関数で分けにくいブロックがあれば,その論理ユニットを説明コメントで分けると可読性がよくなります. 各説明コメントが論理ユニットのタイトルのようなものになります.

// Yという現象があり,パーフォーマンス向上のため,X APIから foo を取得する
const x = ...
/* ... コードが続く ... */
const foo = f(x)

// foo で Z を行う
const bar = g(foo)
/* ... コードが続く ... */

コードの透明性とバックアップ

ドラフト中なら,自分にとって効率いい手段をとれば良いです. ですが,作業の途中のワーク・イン・プログレス(WIP)をマージリクエスト(MR)に入れるとなお良いです.

なぜなら,そのWIPをバージョン管理に入れることで他マシンにバックアップでき, 加えてそのコードの透明性のおかげでチームの仲間たちはいつでも入って手伝えます. 一石二鳥ですね.

分割統治法

大きいタスクを小さくて,テストしやすくて,実現しやすいタスクに分割して,全問を統治します.

たくさんの小さいマージリクエストで大きいタスクを完成しましょう.

細かい改善の重ね合いは山のように積もります. 千里の道も一歩からです.

コミットの履歴とバージョン管理

バージョン管理には git を使用します.

投稿しているマージリクエストはパッチの系列だと考えてください. 各パッチは1つのコミットになります. そう考えたら,コミット履歴は基本一次であるべきです.

これはコードのレグレッションを防止し,レビューの効率を上げるための手段です(レビュー効率アップ → 不具合が見つかりやすい → 品質向上).

**Linuxカーネルのパッチ投稿プロセス**は非常に参考になります. 下記,私にとって一番重要な部分を引用して,和訳してみました.

変更を表現せよ (Describe your changes)

問題を表現してください. 自分のパッチが1行のバグフィックスであれ,5000行の新機能であれ, この作業の動機になった水面下の問題が存在するはずです.

ユーザへのインパクトを表現してください.クラッシュは説得力が高いですが,[...] そこまで目立たない繊細な不具合も存在します.

変更を分割せよ (Separate your changes)

論理変更は違うパッチに分割せよ.

例えば,変更が1つの[コンポーネント]のバグ修正およびパフォーマンス改善を含むなら,その変更を2つ以上のパッチに分けてください. 変更にAPIアップデートおよびそのAPIを使用する[コンポーネント]が含まれているならば,それらを2つのパッチに分けてください.

一方で,1つの変更で,一括で複数のファイルを改修するなら,その変更を1つのパッチにまとめてください. そうすれば,1つの論理変更は1つのパッチに収まります.

要は,各パッチはレビューアにとって検証しやすいように,わかりやすい変更としてまとめるべきです. 各パッチは独立な存在としても有意義でなければいけません.

変更をパッチの系列に分割するときに,各パッチのあとにアプリケーションが正常に動作するよう慎重に作業をしておいてください. 不具合を追跡するため,git bisectを使うプログラマーはパッチの系列をどこでも切る可能性があります. 真ん中にバグが入ってしまうと怒られるでしょう.

また,作業中のパッチの系列を常にメインブランチのトップに rebase しておいてください. そのほうが色々テスティングしやすくて,不具合が入った場合すぐ気づきます.

パッチの意図とは無関係な変更をパッチに含めないでくさい(例:空行の追加や削除). スタイリングが駄目だと思う場合,違うコミットでスタイリングの修正を行いましょう。

コミットメッセージ

大きめのプロジェクトの場合,コミットメッセージは下記の構成に作ります.

[$component] $subComponent: $Title

Y法を活かして,本コミットはXという問題を解決する.
*hogehoge*のため,Y法はZ法より望ましい.

コミット中の変更に対して,コンポーネントもサブコンポーネントもないなら, "[$component]"または"$subComponent:"を省略して大丈夫です.

$Title は長くないほうがいいです. また,タイトルの冒頭に,変更を表現する Action Verb を使うと良いです (例:○ "Add feature X", ☓ "Adds", ☓ "Added", ☓ "Adding", ☓ "add").

コミットメッセージの可読性を向上するため,引用した素材を除いて, 72欄目でテキストをラッピングしてください.

わからない場合,プロジェクトのコミット履歴を参考にし,類似したコミットを見て, 統一したコミットメッセージの書き方を狙いましょう.

コードレビュー

コードの品質を保証するため,マージする前,コードのレビューを行います. 新機能のリリースが少し遅れても良いので,壊れたコードはリリースしてはいけません.

レビューを依頼する際に,上記の「コード衛生」資料でまとめたポイントを確認しましょう. コードの衛生が良くないなら,その理由を説明すると良いです.

理想は,レビュアは些細な問題に時間を費やすのではなく,話すべき難問に時間を使うべきです.

自分のマージリクエストの全てのコードをちゃんと自分で確かめたら, "Needs review"ラベルを付けて, レビュアにレビューを依頼します. 投稿者が勢いを失わないように,レビュアは1日以内にレビューをすると良いです.

レビュアは対応すべき問題を指摘したら,"Needs review"ラベルを外します.ディスカッションをし,投稿者とレビュアは一緒に改善余地を検討します. マージリクエストが最大良くなるまで,このプロセスを繰り返します.

ライター

Malik Olivier Boussejra

CTO at Epigno