Allied ArchitectsAllied ArchitectsEngineer Blog

開発手法の名著をふまえ、チーム内ルールを決めて実践してみる・前編

2021/10/21 開発手法

はじめまして。
ヒコーキ好きのみこやんと申します。

「みこやん」はもちろんペンネーム(って、今はそんな呼び方しないのか…)です。
もちろん本名だなんて思う方はおられないでしょうけれど……。

ちなみにミリタリー好きな方ならなんとなく予想できたかと思いますが、「みこやん」という名前は旧ソ連の航空機メーカーでMiG戦闘機などを手掛けているミコヤン設計局にちなんだものです。
不定期ながらも、細々とした記事を書かせていただきます。
どうぞ、よろしくお願いいたします。

まえふり

※若干長いので、本題に入りたい読者は読み飛ばしてください……。

実はヒコーキだけではなく読書もそれなりに好きだったりします。
若い頃は文学からラノベまで無差別無秩序に読みあさっていた時期もありますが、最近はめっきり技術書ばかり読むようになりました。

ITスキルや分野の裾野が広がってきたことも大きく影響していますが、多岐にわたる業務に関わるにあたり、必須となる知識を増やしていく必要があるという背景もあります。
2010年以降から、AWSやGCPといったクラウドを中心とした*aaS系サービスや、Dockerなどのコンテナ技術、Ansibleなどの構成管理といった、いわゆるDevOpsを中心とする開発手法の台頭は、とりわけ開発者にとっても大きな変化といえるかもしれません。

それ以外にも、1990年代後半以降のJavaブームから派生したオブジェクト指向言語による開発手法の浸透は、ソフトウェア開発シーンに絶大な影響力を与えたと個人的には感じています。
もちろんオブジェクト指向というパラダイムはもっと古くから存在していましたが、一般の開発現場に浸透するまでには非常に長い熟成期間があり、その間に培われてきた「抽象化」という概念を用いた開発手法は、Javaブーム以前と以降では受け入れられ方が全くことなると個人的には感じます。
そして、そのオブジェクト指向開発によるソフトウェア開発現場レベルでの知見も、同時期に蓄積が始まり、ようやく2000年代以降に世界的に花開いていったと言えるでしょう。

その中でも2012年に日本語訳が出版され、いまだにベストセラーとなっている「リーダブルコード」(オライリー刊)や、ボブおじさんことRobert C.Martinさん執筆の名作「Clean Code」など、枚挙にいとまがないほど、オブジェクト指向開発時代のコード洗練技法はオープンにされてきており、私たちの知るところとなっています。

だからといって、これが常に徹底されているかということとは、また別問題です。
ともすると、私たちは(私だけ、かもしれませんが…)、知っていたとしても過ちを犯してしまうこともあります。コードレビューで同僚デベロッパーから指摘されて「あちゃーわかってたはずなのに」という経験をされた方もいるのではないでしょうか。

「リーダブルコード」や「Clean Code」は確かに素晴らしい反面、読んで理解するまでに多少の時間がかかったり、実践する必要がないパートも実際には存在します。たとえば「Clean Code」では、記載項目によっては相反する内容が記載されている場合もあり、著者もそれを認めています。つまり「時と場合による」「必ずしもすべて履行する必要はない」という知見も、確かに存在します。

私の携わっているプロジェクトでは、そうした中からエッセンス的に抽出したものを現場で実践するべく、社内の開発Wikiにまとめて公開しています。
今回は、その内容をかいつまんでご紹介したいと思います。
あくまでも弊社の特定プロジェクトにカスタマイズされた内容ですので、読者の皆様全員に共通して適用できるかどうまでは自信がありませんが、少しでも参考になれば幸いです。

執筆の目的

そもそも、なぜこうしたドキュメントを書いて開発Wikiで公開してるかというと、すべては「コード可読性」というキーワードに行き着きます。
プロジェクトの開発Wikiには、「この文書の目的」として、以下を示しています。

  • 誰が書いたのか分からない、非個性的で読みやすい(readableな)コードに保つ技術を獲得する。
    • 「昨日の自分は他人」 (可読性の悪いコードであれば、自分が書いた昔のコードすら読めない)
    • 「コードを憎んで人を憎まず」 (コードと人が癒着してはいけない)
  • 「動くコード」はプロフェッショナルなら当然のことである。プロは「正しく動く、正しいコードを書く」ことを常に考える。

エラそうなゴタクが並んでるように見えるかと思いますが、わりとまっとうなことを書いているなと個人的には思っています。
「非個性的」というのは、コードは常に没個性でなければならないという意味です。個性的で「これは彼・彼女が書いたコードだね」というものは、他のコードと比べて異質であることを意味するものです。独創性を出すべき箇所は他にあるはずで、少なくともチームや組織における複数人開発にとって、個性的なコードは死を意味するものととらえるべきと考えています。

また、えてしてやってしまいがちなのは、バグや可読性にすぐれないコードに出くわした際に(思わず)git logなどしてしまう、いわゆる「犯人探し」ではないでしょうか。
私もたまにやってしまいますが、誰が実装したにせよ、悪いコードは良いコードに直せばよい、ただそれだけです。「コードを憎んで人を憎まず」は、そういう意味を込めています。

そして「動く」コードはプロじゃなくても書けるわけです。商売としてのソフトウェア開発者が、その責務として持っているものの1つは「正しく動く、正しいコードを書く」ことではないでしょうか。

ちなみに前出の「リーダブルコード」では、何を持ってリーダブルコードというかの定義が示されています。詳細は、ぜひ書籍をお読みください。

  • 最短時間で理解させることができるコード
  • 余計なことを考えずに読めるコード
  • 読み進めるために覚えておく内容が最小のコード
  • ミスリードを起こさせないコード
  • 読むことを拒否させないコード
  • 脳を疲れさせないコード

名前に関すること

プログラミング言語Rubyの作者のまつもとゆきひろさんの名言「名前重要」は、「Ruby」というインタプリタへの命名についてのみ言及しているのではなく、あらゆるものについて語っていると思います。
定数、変数、関数・メソッド、クラス、ファイル……などなど、開発者は命名に日夜あけくれているはずです。
良い名前をつけるのは本当に大変で、ときには雑に考えてしまうこともあると思います。しかし、その場はしのげたとしても、のちのち我が身に災いとして降りかかってくる可能性も考えると、やはり手を抜くわけにはいかないセオリーだと思います。
冒頭に紹介した「Clean Code」でも「名前はソフトウェアの読みやすさの9割がたを支配している」と明言されているほど、名前は重要であり、私の所属するプロジェクトでも、とにかく命名について徹底をしています。

マジックナンバーの禁止

  • クラス内でのみ使用する定数ならprivate const、他のクラスから利用できる定数ならpublic const、システム全体で定義されるべき定数ならconfig関連のファイルでdefineもしくはLaravelのconfig配列等で対応する。
  • 定数の定義位置やファイルは、フレームワークやコンポーネント(Laravelなど)で一般的に規定されたパターンを踏襲するのが望ましい。特に全体で使用する定数は、固有のディレクトリ位置やファイルなどに分散させず、規定のファイルで管理をするのが望ましい。

マジックナンバーは百害あって一利なしの典型だと思っています。コード再利用性の問題以前に、謎の即値がコード中にちりばめられていることで、最短時間での理解を妨げる上に、実装中にうっかり数値を変更してしまうなどのミスがあった場合でも気が付きにくいものです。
運良くコードレビューで差分に気づき、修正に至るならまだマシというものですが、そもそもそんなことでレビュアーを疲弊させる必要性はまったくありません。
とにかく、悪いことずくめでしかありません。
定数として宣言しておくことで、再利用性にも対応でき、少なくとも定数そのものをや付近を修正する以外の作業で1文字変わったとしても、lintですぐに気づくことができます。

定数をどこで定義するかは、言語やフレームワークでの流儀があると思います。あえて独自の流儀を持ち込む必要性はなく、むしろ既存の流儀にあわせることでコード可読性を高めることに寄与できると考えています。

名前をつける・熟考する

  • 変数、定数、プロパティ、関数、メソッド、クラス、インタフェース、カラム、テーブル、フィールドなど、名前をつけるすべてのものについて、誰もがその名前から役割や機能が正しく想像できるような名前にするよう熟考する。安易・安直につけてはいけない。場合によってはペアプロやチームと相談・意見交換して決定してもよい。
  • たとえば、以下の「BEFORE」ような命名は、その時点での実装者には理解できても、他者ならびに未来の実装者には一瞬で理解しがたいものである。これを「BETTER」のようにするだけで、瞬間的に理解しやすい。

    // BEFORE
    $name = User::getName($request['id']);

    // BETTER
    $userId = $request['id'];
    $userName = User::getName($userId);

ただ単に名前をつけるだけでは意味がなく、命名をする際には、その意味や役割が明確になる名前を採用しなければ、結果的にコード可読性を失いかねないため、命名のために割く時間を節約すべきではないでしょう。
上記のBETTERが必ずしもベターかどうかは議論の余地があるかもしれませんが、何であるかを明らかにするための命名が必要であることは言うまでもないと思います。

  • 複雑な式をifの条件や関数の引数などに指定しない。説明変数などに一旦保存してから使用する。
  • 小さなコードであっても、2箇所で同じ処理が必要な場合や、複雑性が高くなりコード可読性が失われやすくなる実装にどうしてもなってしまう場合、その処理はprivate methodとして抜き出して使用し、その名前は適切なものにする。

レガシーコードでよくお目にかかるものの1つが「長いif文」かと思います。特に&&や||などの論理演算子が連なったものほど読みにくいものはありません。
PHPなどのC言語ベースの文法由来でif文の条件式の中に代入式が記述できる言語であると、そうした記述をみかける場合もあります。
長くなりがちな条件が発生する場合、抽象化が甘いことも多いと思います。しかしどうしても避けられない条件となることもあるでしょう。
そういう状況では、なるべく条件が可視化されコード可読性が高まるように、個別の条件ごとに変数を定義することで「条件そのものに命名」することで回避できます。

同じ処理を別の場所で複数回使う場合、メソッドとして抽出すると思います。これも一種の名前付けになりますが、仮のそのクラス内で1度しか使われないコードでも、複雑すぎるといった理由から、何をしているか明確にするための命名としてプライベートメソッドに抽出する手法も有用でしょう。

コメントは適切に

適切なコメントほど難しいものはありません。コードは書いて動かすため、ユニットテストに失敗したりバグとして報告されれば、それが不適切であることは証明されます。
コメントは実行されないため、書いたら書いたままそこに居座り続けます。そのため、非常に厄介なものと言えるでしょう。

  • 「なぜここでこれをしなければならないのか」がコードを見ただけではわからない・わかりにくい場合のみ、明瞭簡潔にコメントを記載する。
  • ただしコメントを入れる前に「helperやutils、あるいはprivate method化して適切な名前を付与することでわかりやすくなるかどうか5分は考える」習慣をつけ、その上でコメントなのかメソッドなのかを判断すること。
  • 基本的には命名が重要であり、処理中の変数名や定数名、メソッド名などで適切かつ明瞭に内容や意図を説明できていればコメントは不要である。

実装をそのまま解説するコメントがある場合、それはDRY原則に反するだけではなく、メンテナンスされにくいコードが生まれることになります。
さきほど書いたように、コードは実行されますが、コメントは実行されません。
実装担当者が、コードの改修には注意を払うのは当然ですが、コメントは「見えているのに見えないもの」のように扱われる可能性は十分にありうると考えるべきです。
その場合、コードが変わった瞬間から、コードとコメントの解離が発生することになります。
開発チームに、コードとそのコメントの両方を担保するように指示すれば、それは重複コードをメンテナンスさせることとほぼ等しいとも言えるコストが掛かってくるはずです。

これを回避するためには、コメントではなく命名によって解決するのがベストといえるでしょう。
そのために5分なり10分なり考えることは、まったく理にかなっているどころか、その程度の時間で決まればお買い得ともいえるのではないでしょうか。

どうしても必要なら、適切なコメントを書く。
そして、書いたらコメントのメンテナンスも未来永劫必要になるという覚悟を持つ。
それができない、あるいは不必要だと判断できたら、コメントは可能な限り書かず、命名で回避する。

上記ブロックがプロジェクトWikiに記載されているのは、これを徹底する意図があります。

コードに関すること

ソフトウェア開発者とは、ユーザのニーズをソフトウェア(コード)によって具現化する技術者です。
コードを書くことと無縁でいられることは絶対にありません。
そのため、上述した2冊のバイブルにも、コードに関するルールづけは数多く言及されています。それゆえに相互の項目が矛盾することもありますが、そうであっても言及せずにはいられない経験則が、過去の偉大なデベロッパーたちに起きていて、その貴重な知見を我々につぶさに残してくれている証明でもあると言えるでしょう。

可能な限りコードを書かない

  • バグはコードから生まれる。バグの量はコード量に比例する。したがって、コードをたくさん書くことで、バグの量も増える。
  • 優秀なエンジニアほど、コードを書かない。優秀なエンジニアは、機能の追加・改修を実装するために、十分に実証・テストに耐えた洗練された既存のコードを可能な限り再利用し、最小限のコードを記述して実装する。

「コードを書くためにエンジニア・デベロッパーがいるのでは?」と思われる方も多いでしょう。
もちろん、必要なコードは書きます。
というか、必要なコードだけ書きます。

バグというのは、コードに内在します。
つまり、コードが存在しなければ、バグも存在できません。
禅問答のようですが、すなわち、不要なコードが多ければ多いほど、不適切なコードも内在する確率が高まるといえるでしょう。

優秀なエンジニアは手が早いとも言われます。その理由は、単にコードを書く能力が高く速いという以上に、コードの再利用を熟知しているからとも言えます。
プログラミングの世界では、すでに存在する実装を独自で最初から実装することを「車輪の再発明」と揶揄することがあります。
車輪の再発明が必要な場合も確かにありますが、多くの商業プロダクトにおいては、真っ向から否定される論理だと思います。組織がそこに投資する価値があると判断することが、ほぼないためです。

現代のシステムは、特殊な環境下で運用されるものを除いて、ネットワークに接続されないシステムはほぼ皆無といっても過言ではないと思います。
十分に検証されたシステムやライブラリを再利用することで、その安全性のメリットをプロダクトに持ち込むことができます。もちろん外部ライブラリすべての安全性が保証されているわけではありません。しかし多くの開発者によって利用実績のあるものほどリスクは低いと考えるのが一般的でしょう。
セキュリティ面からも、可能な限りコードを書かないというセオリーは理解されるものであると言えるでしょう。

重複を回避する

まさに、前述した「DRY原則」にほかなりません。
同じ意味や機能を持つコードを2つ以上実装することにより、あらゆる問題を内包します。

  • 変数、関数、メソッド、クラスのみならず、条件検査、ループ、ロジック、構造、DBテーブル……など、コードのみならずあらゆる概念において、ソフトウェア開発では重複しないように設計・実装する。
  • 一見すると重複と思わないコードでも、重複をしている場合がある。以下の例では、別の変数に含まれている同じ要素を二度チェックしている。たとえば3項演算子やNull合体演算子のような条件式を使っている場合、1行で簡潔に記述されているため直感的にはわかりにくいが、内部的にif文を使っているのと変わらない。したがって、以下のようなコードはif文を3回実行していることと変わらない。

    // BEFORE
    $username = $request['username'] ?? null;
    $password = $request['password'] ?? null;
    if (empty($username) || empty($password)) {
    return INVALID_ACCOUNT_PARAMETERS;
    }

    // BETTER
    $hasAccountElements = isset($request['username']) && isset($request['password']);
    if (!$hasAccountElements) {
    return INVALID_ACCOUNT_PARAMETERS;
    }
    $username = $request['username'];
    $password = $request['password'];

  • 同じ要素に対して、3項演算子やそれに類する演算子に続いてif文を使っている場合、条件判定が2度以上繰り返されていると認識し、実装を変更する。

Null合体演算子を実装する言語の場合、ある種の条件が1行で記述できるため見通しよく書けると考え、多用するエンジニアもいます。
適切かつ効果的に利用すれば強みのある武器となりますが、単に見た目が1行というだけで、内部的には無駄に条件文を重ねているコードも時々お目にかかります。
上記のPHPのコード例のBEFOREは、そうしたパターンの過剰な一例ともいえます。Null合体演算子を利用した代入のあとにif文で条件判定をしていますが、内部的には3つの条件文を実施しているのと変わりません。
説明変数を用いて条件を命名化し、if文ひとつで判定したあとに、必要な変数に代入するほうが理解の妨げになりにくいと考えます。

ガード節と早期リターンの使用

  • 型や不正値を防ぐために、関数やメソッドの導入部でガード節を用いる。
  • 不正と判定した場合に、リカバリーができなければ、呼び出し元に即座にエラー通知とともにreturnする。
  • ガード節と早期リターンを活用することで、その後に続く処理は、その関数が本来果たすべき責務だけに限られ、コード視認性が高まる。

冒頭に紹介した2大バイブルをご存知でない方であっても、ガード節と早期リターンはコードレビューで誰かしらから指摘を受けるなどで知っている方が多いコードパターンではないでしょうか。
if文をネストさせ、複雑な条件を掻い潜ったものだけが実行されるというおぞましいコードは、おそらく誰もが見るに耐えない苦痛を強いられることでしょう。
しかし案外、自らが実装者になった場合、仕様書どおりに書いた結果、if文のネストが出来上がってしまった……という経験をお持ちの方もいるかもしれません。

中でもガード節と早期リターンは、コード可読性と不具合を混入させにくいという両者の目的を達成させるのに不可欠な、代表的な2大スキルともいえるでしょう。

早期リターンを活用することで、機能をメソッドや関数に分離する効果も期待されるため、コード可読性が高まり、場合によってはユニットテストコードも書きやすくなるでしょう。

残りは後編で…

私が所属するプロジェクトのWikiでチームに公開し「布教」しているコーディングルールなどを、かいつまんでご紹介してみました。
ここで紹介した内容の多くが、冒頭で紹介した「リーダブルコード」と「Clean Code」ですでに指摘されている内容であり、先人の知恵に授かりつつ再構成したといったところです。
したがって、その他の多くのブログや解説記事などでも言い尽くされている感はあったかと思います。

しかし意外なほど実践されていないものもあり、知識として持っていることと、実践できることとは、まったく別の次元の話であることもまた、これらの記事の多さが物語っていると思います。

次回は後編として、さらにコードに関するルールをご紹介できればと思います。

みこやん
  • みこやん
  • アニメおたく
  • ヒコーキ(航空)は「見る・撮る・乗る・知る」ぜんぶ大好き。飛行機も空港も管制も、ヒコーキに関することなら全部好き! ITはサーバもフロントもインフラ(オンプレ中心)も組み込みも雑多にやってきた古株ですが、フルスタックではありません。

RELATION ENTRY

Vimでphp-cs-fixerを使えるようになったらプロジェクトのコーディングルールに合わせてカスタマイズしちゃって一層快適実装ライフ

Vimでphp-cs-fixerを使える...

2022/01/28 Vim

こんにちはこんにちは!! ヒコーキ好きのみこやんです。 前回は、Vimmerとして現代的なIDEのようなCode FixerやLinterなどを使って、編集中のコードを動的にチェックして警告やエラーを発してもらうことで即座に不具合を修正するために、ALEというプラグインを導入したお話をしてみました...

続きを読む

VimのLintサポート非同期エンジンALEを使ってphp-cs-fixerのコード支援の恩恵を受けて開発効率をアップ

VimのLintサポート非同期エンジンA...

2022/01/11 エディタ

新年あけましておめでとうございます。 リアルタイムで読まれていない方は、こんにちは。 ヒコーキ好きのみこやんです。 VimmerなのでVimネタ続きになってしまいますが、こちらもやっとマトモに調整できた嬉しさから記事に。 とはいえ、今回のネタは「ちゃんと従来から理解して運用できていたVimmer...

続きを読む

みこやんの記事一覧へ

ページTOPへ