プログラミングを学び始めて3年が経過した。
エンジニアとして本格的に働くようになった今だからこそ、改めてリーダブルコードを読み直してみた。
リーダブルコードとは、他人が理解しやすいコードのことなんだ。
はじめに
プログラミングを始めて3年が経過し、知識と経験を得た(まだまだ未熟ですが🔥)段階で改めてリーダブルコードを読み直し学んだことを、自分なりにまとめて行こうと思います。
この記事は、リーダブルコードの内容を咀嚼し、自分で再構築してまとめることを目標としています。
本のコードや文章を参考にしつつ、自分の技術分野で考えられそうなパターンに置き換えてまとめています。
ご指摘等ございましたら、是非コメント欄の方からお教え頂けますと幸いです🙇♂️
理解しやすいコード
そもそもの大前提として、「理解しやすいコード」とは一体何か。
リーダブルコード本書では
他の人が最短時間で理解できること
だと定めています。
現代当たり前に使われているサービスにおいて
「天才一人が作り上げたプロダクトで今もなお一人で運用している」
というプロダクトはかなり珍しいと思います。
どんなプロダクトにも複数人のエンジニアが在籍し、共同で開発を進めるスタイルが主流です。だからこそ、コードは自分が理解できることが重要なのではなく、他人が理解しやすいコードこそがReadableだと思います。また、よく言われることですが数ヶ月後先の自分は今の自分からしたら他人になります。数ヶ月後の自分が見てもすんなりと理解できるコードは質の高いコードと呼べると思います。
第一部: 表面上の改善
名前に情報を詰め込む
明確な単語を選ぶ
例えば関数名で下記のようなものがあったとします。
GetPage(url);
この関数名のGetの解釈には曖昧性があります。
インターネットからのダウンロードなのか、ローカルキャッシュなのか、DBからの取得なのかなど他人が見た時に疑問が生じるコードには改善の余地があります。
インターネットからの取得の場合はFetchやDownloadなどの命名が適しているでしょう。
汎用的な名前を避ける
tmpやretvalなどの汎用的な変数は、使用範囲を考慮した上で使用するかを決める。
変数名に適切に情報を詰め込むことが重視すべきポイントです。
なので、tmpやretvalという命名をするな!というわけではありません。
例えば下記のような隣接する要素のスワップにはtmpという変数名は適しているでしょう。
var tmp = items[left]; items[left] = items[right]; items[right] = tmp;
この場合tmp変数は文字通り一時的に変数を格納しておく場所であり、それ以上の用途はありません。
しかし、下記のような場合は命名を見直す余地があります。
var tmp = string.Empty;
tmp += DonwloadManager.DownloadAssets(playerAudio);
tmp += DownloadManager.DownloadAssets(playerModel);
downloadResultlabel.text = tmp;
この場合のtmpは、アセットをダウンロードした結果をプレイヤーに表示するために使用しています。一時的に格納しているわけではないので、この変数の情報を適切に表現するためには、donwloadAssetsLogなどの変数名のようが良いでしょう。
抽象的な名前よりも具体的な名前を使う
抽象化はアーキテクチャにおいて重要な観点の一つですが、コーディングに関しては具体的な名前を用いる方が他者に理解されやすいコードになります。
if (ServerCanStart()) { // サーバとの通信処理 }
サーバのポートを指定してLISTEN状態か確認する機能を持った関数がServerCanStart()だとすると、この命名は抽象度を下げることができます。
if (CanListenOnPort(8000)) { // サーバとの通信処理 }
接尾辞や接頭辞を使って情報を追加する
これはprivateなフィールド変数は「 _ 」から始めることや、フラグ変数にはis~とするなどコーディング規約として定めることもありますが、単位や暗号化の有無などを表すためにも使うというのは新しい知見でした
elapsedTime_ms = GetElapsedTime(); raw_password = PasswordInputLbl.text;
などなど
名前の長さを決める
「長い変数名は悪」と思われがちですがケースバイケース。広いスコープの変数に対して短い変数名に無理やり圧縮するくらいなら、少々長くなっても必要な情報を詰め込んであげるべきということ
名前のフォーマットで情報を伝える
privateな変数は「 _ 」から始める。マクロはコンスタントケース、定数はkを接頭辞とする。などと言ったルールを決めておくことで、宣言先を見にいかなくても変数の種類が判断できるようになる。つまり、他人に理解しやすいコードになるということです。
誤解されない名前
自分のコードをなるべく客観的に「誤解されそう」な点を探し、改善すること。
例えば下記のコード
playerList = playerList.Fliter(player => player.level >= 30);
このフィルタ関数は引数にFunc<Player, bool>を受け取って、フィルタリングする関数ですが、引数のラムダ式の結果がtrueのものを取得するのか、trueのものを省くのか、Filterという関数名からは明確にはできません。ここで誤解が生まれる可能性があります。前者の場合は関数名をSelectにするだとか、後者の場合はExcludeにするなど、関数名から推測される意味に誤解されにくいものを使用するように心がけるべきです。
また、ユーザ(他人)の期待通りの実装であるべきです。
int playerCount = NetworkManager.CurrentRoom.GetPlayerCount();
上記のコードを見たところ、NetworkManager.CurrentRoomのPlayerCount変数に対するゲットアクセサーっぽく見えるので、処理的には軽く見えそうです。しかし内部実装でCurrentRoom内のPlayerリストを全操作して個数を判定するオーダーNの関数であった場合、ユーザの意図する軽い処理ではなくなります。関数名から推測する処理と、実際の処理に差異が生まれないように意識すること。
美しさ
ここでの美しさとは、処理の実装が美しいことではなくコードのレイアウト自体の美しさを指します。「意味のまとまり事に段落分けされている」であったり、「インデントが整理されている」ことであったりなど、将来自分のコードを誰かが見るであろうことを想像してきれいに整えることが重要ということです。
コメントすべきことを知る
コメントには価値のあること、つまり「自分の思考」を書いてあげることが重要。なぜなら他者がコードを読むときは自分の考えはコードにしか現れないからで、自分がその場にいて一つずつ説明するわけにもいきません。だからこそ、コーディングしている時の自分の頭の中を整理してコメントとして付加することが重要です。
一方下記のようなコメントは不必要なコメントになります。
// Playerクラスの定義 class Player { // Introductionメソッド public void Introduction() { } }
コメントをつけるにあたっての大原則として、「優れたコード > 悪いコード + 優れたコメント」ということです。整理されていないコードを、コメントを使って改善しようという考えはせず、まずは優れたコードを作成し、それに付加する形でコメントがあるということを忘れないようにしましょう。
コメントは正確で簡潔に
ここで意識することは - 的確に意図を伝えるコメント - 情報の密度を高く
まず、「的確に意図を伝えるコメント」とは下記のようにwhy, whatが伝わるコメントを書くことです。
Player[] players = GameManager.GetAllPlayers(); // プレイヤーをレベル昇順にソートする foreach (player in players) { // 内部処理 今は省略 }
次に、「情報の密度を高く」とは、だらだらと説明文書を書くのではなく、専門用語(プロジェクト固有のものではなく、コンピュータサイエンスの知識としての専門用語など)を用いて簡潔に説明できる場合はそちらを採用するということです。
続き
リーダブルコードは200ページ程度で一日あれば読み切れる分量です。
僕は一度読んで終わり!と言うよりは、定期的に読み返してしっかり理解しやすいコーディングを意識出来ているか見直すように使っています。