MJHD

エモさ駆動開発

読みやすいコードって何だろう

読みやすいコードってなんでしょうね。  新卒の頃、たくさんリテイクを受けて書き直しした経験からよくよく考えはじめましたが、永遠に結論はでなそうです。

ひとまず、ここ数年考えている頭の中身を書き出したいと思います。

「コード」ってなんだ?

「読みやすさ」とは何か、の話に移る前に、そもそも「コード」とは何か考えてみます。

ここでいうコードは、もちろんあるプログラミング言語の文法に従って入力されたコードを差すわけですが、その本質は何でしょうか?

プログラミング言語の発祥を考えると、まずマシン語がありアセンブリ言語があり、より人間が読みやすく高度な構造化ができるよう高水準言語たちが生まれてきました。(と思います)

この言語の読者は、機械と人間であり、両方にとって読みやすい共通語として役割があります。この事実は、人間が読みやすいという自然言語との共通点でもありますし、人間以外の機械も読みやすいという差異でもあります。

今回の記事は、読者が人間である場合の「読みやすいコードとは何か」について深掘りたいと考えているので、ここでいうコードは人間の読み書きする文章としてのコードに重きを置きます。なので、安全性やパフォーマンス、その他の機械にとっての意味論は一旦前提として、人間にとっての意味論を考えます。

人間にとっての意味論を考えるとき、おそらく「読みやすい"文章"とは何か?」と考えても、結論は似たようなものになるのではないでしょうか。

読みやすい文章の話

よくネットで見かけるミームに「完全に理解した」→「何も分からない」があります。
私自身よく体感するのですが、例えば入門書を読んだ直後は「完全に理解した」状態になります。
一種の無敵感と言いますか、頭の中に矛盾が一切なく、読んだ内容を誰かに伝えたい、応用にチャレンジしたいとすら思っています。

ここで実際に他人に読んだ内容を伝えたり、もしくは自分で応用にチャレンジしたとき、今度は「何も分からない」状態になります。

不思議ですね…読み終わった直後は矛盾が一切なかったのに、他人から質問を受けたり、応用する中で新しい事実に出会ったときに「実は何も理解してなかったんじゃないか…?」と不安になります。
そしてこの不安をTwitterフィルタを通して書き込むと「何も分からない」になるのです。

私は、この「完全に理解した」→「何も分からない」の繰り返しのことを、事実の再グループ化だと思っています。

事実

ここでいうところの事実とは、実際に目で見て出会った出来事です。近所に新しくできたごはん屋さんに入ったら美味しかった。遠くの国で人が死ぬニュースを見た。実験の結果A群が優秀だった。なんでも良いのですが、とにかく当人にとって新しい出来事であり、集まると思い出や知識、陰謀論、宗教、いろいろな概念を形作ります。

グループ化

例えば、全く同じ事実をそれぞれ別の人に与えたとして、同じ概念には至らないと思います。地震が発生したとして、「良くあることだ」と思う人もいれば「某大学が実験をしている」と思う人もいるでしょう。(いるんですか?)

また、様々な心理的なバイアスによってもこの結び付く力関係は変わってきます。

事実同士をどう結び付けるか、逆に言うとどう線を引くのか、このグループ化によって概念はできていそうです。

再グループ化

「完全に理解した」→「何も分からない」の繰り返しを、事実の再グループ化と言いました。

「完全に理解した」は、入門書を読み終わった後など、自分の頭の中に矛盾がない一種の無敵状態でした。

「何も分からない」は、他人から質問を受けたり、応用する中で新しい事実に出会ったときに「実は何も理解してなかったんじゃないか…?」と不安になることでした。

ここで起きていることは、質問や応用で出会った「新しい事実」を、どうグループ化するかという悩みであり、これは事実の再グループ化と言えそうです。

入門書というのはよくできているので、分かりやすい事実(例や問題など)とそのグループ化の基準を最低限教えてくれます。当然、本としてまとまっているのでその中に矛盾はなく、しっかりと読んでいれば矛盾のない事実とグループ化による概念が読者の頭の中に構築されます。

しかし、その後「新しい事実」に出会ったときどうグループ化するのかは読者にゆだねられています。人は「完全に理解した」→「何も分からない」を繰り返しながら、頭の中の事実マップを拡張していき、事実のグループを適切にメンテナンスしていくことで、学習をしているからです。

文章

事実のグループ化を踏まえて読みやすい文章について考えたいと思います。

読みづらい文章というと、どんな文章を思い浮かべるでしょうか。「知らない単語がある」「何を言っているのか分からない」「矛盾している」「説明が端折られている」などが最近読んだ本だと思い当たります。読者の中で「分からない」が発生していると考えられそうです。

ということで、少なくとも「何も分からない」状態が起こりうる文章は分かりづらいはずです。まあ当たり前ですね、何も分からないのですから。

読みやすいコードの話

では、読みやすいコードとは何でしょうか。

文章における読みやすさはコードにも通じるはず、という前提なので、文章における事実のグループ化の構造にコードを当てはめて考えてみたいと思います。

事実

コードにおける事実は、例えば変数や関数などのシンボルが最も分かりやすいです。

letやvarなどの変数宣言や、fnやdeffunc、functionなどの関数宣言はまさに「新しい事実だよ」と伝えています。これらのシンボルを組み合わせて、ロジックを構築していきます。

もちろん、人間の読むコードはもっと曖昧ですので、プログラミング言語の文法と事実が完全に一致していないケースもあります。素晴らしいワンライナーなどはまとめて一つの事実となりそうです。今回は例として、シンボルに限った話をしたいと思います。

グループ化

コードにおけるグループ化は、例えばパッケージやファイル、関数やクラス、空行で区切ったコードの塊などが当てはまると思います。

他の関数や変数などのシンボルをまとめて、一つの意味を持たせたグループです。空行で区切ったコードも、暗示的に意味を持っていたり、または明示的にコメントで役割が説明されています。

コンテキスト

ここでもう一つ、コンテキストという概念も登場させたいと思います。

ここでいうコンテキストは、事実をグループ化した概念の中でも、コードを読む上で読者(レビュワーなどコードリーディングをしている人、またはコンパイラや機械)が前提として持っていなくてはいけない概念たちのことです。

例えば、AファイルのB関数を読んでいる場合、Aファイルでimport/include/useされているパッケージの情報は前提として読者が持っていなければ、B関数の中で使われているシンボルを読んだときにグループ化されていない新しい事実が生まれてしまいます。

これは、人間にとっては「何も分からない」ですし、機械にとっては「undefined symbol」です。

よって、「何も分からない」を防ぐためには、コンテキストも大切になってきます。

ただし一点だけ注意が必要で、「人間は、機械よりもコンテキストが曖昧」です。機械はメモリのある限りコンテキストを厳密に記憶し参照できますが、人間の認知はより忘れやすく曖昧でノイズも多いです。例えば、「人が覚えられる事柄はせいぜい3~5個」のような心理学のお話を見かけますが、これはコンテキストにも当てはまるため、「機械的には解決できるシンボルも、人間には解決できない」ことが多々あります。

強いコンテキスト、弱いコンテキスト

どのコンテキストが失われやすいのか?を把握するために、強いコンテキスト、弱いコンテキストという言葉を錬成したいと思います。

強いコンテキスト: 直前で定義された概念(画面内に収まる)、現在行よりも上で定義された概念(人は上から下に読む)、現在ファイル内で定義された概念、現在ファイル内で明示的に参照された概念(名前付きimport, 明示的なself, 引数など)…

弱いコンテキスト: ファイル外で定義された概念、暗黙的に参照される概念(グローバル変数など), 演算子オーバーロード

これは一例ですので、状況によってグラデーションは変わってきますが、弱いコンテキストに含まれる概念はIDEなどの支援なしでは読めない可能性が高そうです。また、プロジェクト内で普遍的な関数群などは強いコンテキストに含まれるかもしれませんし、新参者にとっては弱いコンテキストかもしれません。

よって、読みやすいコードの要素として、弱いコンテキストへの参照が少ない、という項目も挙げられそうです。

読みやすい、とは

本題ですが、「読みやすい」とはなんでしょうか。ここまで出た話をまとめれば「何も分からない」を防ぐため、適切な「事実」と「グループ化」を与えることだと言えそうです。

さらに、人間の認知の制約上、コンテキストは失われやすいという性質も考慮に入れる必要があるため、「弱いコンテキストへの参照が少ない」ことも挙げられました。

まとめると、「適切にグループ化され」「弱いコンテキストへの参照が少ない」コードが読みやすいと言えそうです。

「適切なグループ化」は色々な文脈で色々な言葉で語られていますが、「弱いコンテキスト」の話はあまり見かけない気がしています。(無知なだけかもしれません、既にあれば教えていただきたいです)

Go言語の例

以前、視覚に障害のある開発者からみたGo言語の良さに関する記事で「読みやすい/読みづらい」についての言及がありました。

zenn.dev

Go言語のレシーバー(他言語でいうthisやself)が明示的に記述されているためメソッドであることが確定する点や、public/privateが命名に現れるため参照しやすい、型が明示的であるためオブジェクトの動作を把握しやすい…などが挙げられています。

視覚情報が限られた状況では、強いコンテキストと弱いコンテキストのグラデーションはより濃くなるため、強いコンテキストの多いGo言語は読みやすいと言えるのでは、と思っています。

この話は晴眼者であるかに関わらず起こる問題で、コードレビューのような限られたdiff、限られた時間で読まなくてはいけないコードではグラデーションが濃くなると思います。

三行まとめ

コードは文章の一種。

適切なグループ化はやはり大事。

弱いコンテキストは読みづらい、特にレビュー時など。

 

余談

この「事実」「グループ化」「コンテキスト」の話は、文章、コード以外にもUIデザインだとか物語、音楽など、シーケンスがあり人間が認識する色々なものに適用できるんじゃないかとなんとなく思っています…。