はじめに
本記事は、リンクアンドモチベーション Advent Calendar 2023 の6日目になります。
私の所属するチームでは、コードのBest Practiceを作るための活動「撲滅!Bad code Busters!(通称:BBB)」を行なっています。ここでは、本活動の内容とその中で印象に残った「Good codeにするための3つの考え方」を紹介します。
撲滅!Bad code Busters! とは
チーム内における、品質の良い/悪いコードの認識を合わせる活動です。エンジニアとして、品質の良いコードを書きたいと考えることは自然だと思いますが、品質の良いコードとは、本質的に主観によるものと思います。
品質向上の具体策を進めていく前提として、チーム内で、品質の良い/悪いの認識を合わせることが必要ではないかという話になり、輪読会形式で、品質の良い/悪いコードとは何かについて議論しています。この活動を、「撲滅!Bad code Busters!(BBB)」と呼んでいます(名前は私の案 😋)。書籍は、Good Code, Bad Code ~持続可能な開発のためのソフトウェアエンジニア的思考です。
本活動は、OKRとは別にチームの「チャレンジ目標」という形で明示的に掲げ、チーム全員で達成を目指して実行し続けられる工夫をしています。
以下、活動内で印象に残った「Good codeにするための3つの考え方」を書きます。上記の書籍のChapter2, 3あたりの話が中心となります。
1. 「コードを書くことは、小さなAPIを公開していること」と考える
一般にWeb APIを設計するとき、命名や型といったフォーマットから、一貫性や機密性まで多くのことを検討します。ごく簡略化すると、呼び出し元から知る必要のあるもの/必要のないものという視点です。コードを書くときにも、(それが今は自分しか使用しないメソッドのように思えても)、小さなAPIを公開しているように、知る必要のある概念と詳細を分けて考えることは、品質向上に繋がる考え方だと思います。この考え方は、綺麗な抽象化レイヤーを作ることに役立ちます。
抽象化レイヤー
抽象化レイヤーは、大きな関心を持つ問題を解決するために、その問題を小さな問題に分ける仕組み、またはその小さな問題が織りなす階層のことです。特定のレイヤーの詳細については、抽象的な概念として隠蔽されます。
例:HTTP通信の実装(大きな関心を持つ問題)をする場合、コネクションのopen/closeというプログラム(小さな問題たち)は書くが、HTTPに関する様々な複雑な問題(別の小さな問題たち、たとえばOSI参照モデルの下のほうの階層でやられていること)は隠蔽されており、抽象的な概念として捉えることができる
APIの側面からコードを考えることは、きれいな抽象化レイヤーを作ることに役立ちます。なぜなら、APIは呼び出し元に公開される概念を定義し、それ以外は実装の詳細だからです。何かしらコードを変更する際に、実装の詳細が(入力パラメーターや戻り値の型、パブリック関数から)APIにまで漏れ出しているなら、抽象化レイヤーが本来あるべきように、きれいで明確ではないのは明らかです。
(引用:Good Code, Bad Code ~持続可能な開発のためのソフトウェアエンジニア的思考 2.3.1)
そして、綺麗な抽象化レイヤーを作ることは、以下の点でコードの品質向上に寄与します:
- 読みやすさ(一度に扱う概念が少なくなる)
- モジュール性(干渉せず分割されていれば、置き換えられる)
- 再利用性と汎用性(他のシナリオに役立つ可能性がある)
- テスタビリティ(小さな問題は完全にテストしやすい)
2. コードでの契約、あるいは我々は利用規約を読まない
当初自分しか使わないと考えていたメソッドは、いつの日かチームメンバーに利用される日が来ます。その時に、うまく(誤解なく)使われて欲しいです。ここでは、そういった時に役に立つかもしれない、コードでの契約という考え方について書きます。
契約プログラミング(または契約における設計)というソフトウェア設計の方法論があります。ごく簡単にいうと、コードを書くときに、コード間で契約を結んでいるかのように考えることです。事前条件、事後条件、不変条件として形式的な条件として表明するもので、たとえば
- 呼び出し元に向けた、セットアップや入力
- 起きることと、返すもの
- 呼び出し前後で変わらないもの
を明確にすることです。より正確には「契約プログラミング」などで調べてみてください。
コードでの契約を前提としたとき、次の2つのことが大切なんじゃないかなーと思いました。
契約は強制される
# 蓋が閉まっている場合のみ、本メソッドを呼び出せる
def sentaku_kaishi
...
end
上記のコメントがあっても、実際呼び出せてしまう場合、問題が発生する可能性があります。蓋が閉まっている場合のみ、sentaku_kaishi
が呼び出せる契約を行うのがいいです。これは、フールプルーフを意識して実装方針を考えるとも言える気がします。
フールプルーフ (英: fool proof) とは、安全工学における用語のひとつで、工業製品やシステムを設計する際、誤操作や誤設定などの間違った使い方をしても、少なくとも使用者や周囲にとって危険な動作をしないように、あるいはそもそも間違った使い方ができないように配慮する設計手法のこと。
(引用:Wikipedia)
契約は読まれる
何かしらの既存のプログラムを利用する場合、コードの情報を集めるいくつかの方法があります。例えば、命名、型、コメント、ユニットテスト、デバッグ、実装内容、ドキュメント、チャットツールで検索、直接聞く...など。利用するコードについて完全に理解できることはもちろん素晴らしいですが、立場・状況・制約・めんどくせぇという気持ちなどにより、よしなに利用する場合もあります。
利用する人が、それら情報を集めるときに、契約が読まれる必要があります。命名や型は読まれると思いますが、コメントは全て読むことを期待できるかわかりませんし、その他の情報も制約や信頼性の観点から怪しいです。
我々は、実世界において何らかの契約を行うとき、その契約書の利用規約をすべて読む人は少なく、おそらく、その最も明確な部分を把握するにとどまることが多いという話に近いです。
3. コードを書いているとき、おそらくその瞬間、あなたは世界で一番そこに詳しい
コードを書くとき、つい忘れてしまいがちですが重要な前提として、その瞬間は、自分が世界で一番そのコードについて詳しい可能性が高いということです。その自分が当然と思っていることは、そのコードを利用する人(将来の自分を含む)にとっては必ずしも自明ではありません。
この考え方を念頭に置きつつ、前述した、コードを書くことを小さなAPIを公開していると考えること、綺麗な抽象化レイヤーを作り、呼び出される側の視点を踏まえ、コードでの契約を意識していくことを行なっていきたいものです。
最後に
本活動をする中で、多方面のロールや経験を持っている人が参加しているため、様々な視点や、別言語・フレームワークの話が毎回のように出て、なかなか勉強になります。
10月から始め、2ヶ月経ちますが、チーム全員が毎週予習して参加しており、まぁまぁすごいんじゃないかな〜と思ってます。輪読会って続けるのなかなか難しいし。
今月の残りも引き続き、ワイワイガヤガヤしながら、Good Codeについて考えていきたいと思います 😃