PHPカンファレンス沖縄 2021で登壇した内容。
プログラミング言語に依存しない、質の高いコードを書く技術CBcloud株式会社新垣 雄志(あらかき ゆうじ)
View Slide
自己紹介● 新垣 雄志(あらかき ゆうじ)●Twitter: @arakaji● 職歴○ 琉球インタラクティブ株式会社○ 株式会社Payke○CBcloud株式会社 ←NOW●PHP歴○ 前職、前前職ともにサーバーサイドでPHPを使っていた■CakePHP■FuelPHP○ 現職ではサーバーサイドはRubyを使っている
なぜこのテーマを選んだか?●PHPは前職まではがっつり使っていたが、現職は主にRubyで開発をしている● そのため、個人的にいま「PHP」特有のテーマがみつからなかった● プログラミング言語に依存しないテーマを探した● 現職ではスタートアップあるあるのスピードと成長優先で書かれたプロダクトを引き継ぎ、コードの保守性なども改善しながらもプロダクトの成長をさらに加速させるための開発をしている最中● 「コードの品質」と「プロダクトの成長速度」をどう両立させていくかがテーマ● そこで出会った名作
質とスピード
質とスピード● 和田 卓人(@t_wada)先生の登壇資料● ソフトウェア開発においてよく言われる「コードの品質を一旦犠牲にしてスピード優先で開発する」という言葉に対する誤解を解く内容● コードの品質と開発スピードはトレードオフではない● 品質の高いコードを書くことにより、開発速度が上がる● 質を上げながら開発速度も上げるにはどうすればいいか?ではない● 質を上げることによって開発速度を上げる
次に出てくる疑問● 質の高いコードを書くことが開発速度を上げることに繋がることはわかった。● では「質の高いコードを書く」にはどうすればよいのか ?● 今回はその「プログラミング言語に依存しない質の高いコードを書く技術」についての整理・言語化を試みた
ソフトウェアの品質とは?
SQuaREのソフトウェア品質モデル● システム及びソフトウェアの多岐にわたるステークホルダ(利用者、発注者、開発者など)が持つ多様な品質要求を定義し評価するための基準として定義された国際規格SQuaREシリーズ● 品質モデルでは、品質を構成する上位の要素「品質特性」とこの品質特性をささえる詳細化した下位の要素「品質副特性」を定義している
保守性の高いコードを書く技術● 今回掘り下げるのは「保守性」● 「意図した保守者によって、製品又はシステムが修正することができる有効性及び効率性の度合い」● 保守性を上げる=プロダクトの変更速度を上げること
モジュール性(modularity)● 「一つの構成要素に対する変更が他の構成要素に与える影響が最小になるように、システムまたはコンピュータ・プログラムが別々の構成要素から構成されている度合い」● つまり、依存関係やそれによる影響範囲を適切にコントロールしたコードを書くための技術が求められる
SOLID原則
SOLID原則SOLID原則の目的は、以下のような性質を持つ中間レベルのソフトウェア構造を作ること。● 変更につよいこと● 理解しやすいこと● コンポーネントの基盤として、多くのソフトウェアシステムで利用できること
SOLID原則●S:単一責任の原則(SRP: Sigle Responsibility Principle)← 着目●O:オープン・クローズドの原則(OCP: Open-Closed Principle)●L:リスコフの置換原則(LSP: Liskov Substitution Principle)●I:インターフェイス分離の原則(ISP: Interface Segregation Principle)●D:依存関係逆転の原則(DIP: Dependency Inversion Principle)
単一責任の法則● 「モジュールを変更する理由はたったひとつだけであるべきである」● ソフトウェアを変更するのは、ユーザーやステークホルダーを満足させるため● この「ユーザーやステークホルダー」が変更する理由になる。● ただし複数のユーザーやステークホルダーがシステムをおなじように変更したいと考えることもある。● 変更を望む人たちをひとまとめのグループとして扱いため、そのグループをアクターと呼ぶ● 「モジュールはたった一つのアクターに対して責務を負うべきである」
Employeeクラスの例● 給与システムにおける従業員クラスの例●3つのメソッドはそれぞれ別のアクターへの責務を追っている●calculatePayは経理(CFO)●reportHoursは人事(COO)●saveはDB管理者(CTO)
何が問題か?● アクターの異なるメソッドで同じアルゴリズムを共有してしまった場合、両方のメソッドに影響が出てしまう。● アクターが異なる機能はアルゴリズムが一時的に似ていても使われ方が変わるし変更タイミングも変わる● アクターが異なるコードは最初からモジュール/クラスレベルで分離したほうがよい
再利用性(reusability)● 「一つ以上のシステムに、又は他の資産づくりに、資産を使用することが出来る度合い」● 既存コードを再利用することで新たなコードを書く必要をへらし生産性を上げる○ ライブラリ○ フレームワーク○SaaS● 再利用しやすいコードを自分が書くことでチームメンバー、そして明日の自分の生産性をあげる
再利用性を高めるコードを書くための技術/考え方● ボトムアッププログラミング● 小さいものは美しい(Small is Beautiful)● メタプログラミング
ボトムアッププログラミングLispでは、プログラムをただプログラミング言語に従って書くことはしない。プログラミング言語を自分の書くプログラムに向けて構築するのだ。プログラムを書いているときに、「Lispに○△のオペレーターがあればなあ」と思うことがあるかもしれない。そうしたらそれを書けばいい。後で、新しいオペレータの使用がプログラムの別の部分のデザインを簡潔にまとめることにつながったと気づくだろう。
ボトムアッププログラミング● プログラミング言語を自分たちの開発しているアプリケーションの適する形に変えていくこと● フレームワークやライブラリを使うのはそれと同じ○LaravelやCakePHPを導入することでPHPがWebアプリケーションの開発により適した形に拡張され る○aws-sdkのライブラリを導入することで、AWSを操作するのに適した形に拡張される● これをもっと小さい単位で、自分たちのアプリケーションコードを書くときにも行う
連想配列の値取得の例連想配列内に指定したキーが存在する場合にはその値を、存在しない場合には指定したデフォルト値を返す。一見シンプルだが以下の様な問題もある。●falseと評価される空文字や0がある場合、キー自体はあるのにデフォルト値がかえされてしまう● この差異に以外ときづかず特定ケースでバグを発生させることが多々ある
連想配列の値取得の例● 非常にシンプルだがより厳密な配列での値を取得をするのメソッドを定義する● これによりほんの少しだがバグ発生率やコード量を削減することができる。● シンプルなので他の人も理解しやすく再利用しやすい。● この小さな積み重ねが中長期的な生産性の大幅な向上につながっていく
小さいものは美しい● 小さなプログラムはわかりやすい● 小さなプログラムはシステムリソースにやさしい● 小さなプログラムは他のツールとくみあわせしやすい○ 再利用性を高める!!
メタプログラミングメタプログラミング(metaprogramming)とはプログラミング技法の一種で、ロジックを直接コーディングするのではなく、あるパターンをもったロジックを生成する高位ロジックによってプログラミングを行う方法、またその高位ロジックを定義する方法のこと(fromwikipedia)別の言い方をすると、「プログラムを書くプログラムを書くこと」とも言える。
メタプログラミングってPHPで出来る?● メタプログラミングで一級市民的な言語はある○Lisp○Ruby●PHPではどうか?○ マジックメソッドを使うことでメタプログラミングのようなことができる
Driverクラスのtype判別メソッドの定義● 配送ドライバーには複数種類ある○ 軽貨物ドライバー(kei_car)○ 一般貨物ドライバー(ippan_car)●Driverインスタンスがどのタイプかを判別するためのis_hogehoge()というメソッドを定義● 問題点○DRIVER_TYPEが増えるたびに同じようなメソッドを再定義しないといけない
Driverクラスのtype判別メソッドの定義●__callマジックメソッドは存在しないインスタンスメソッドがコールされたときに呼び出される●__callマジックメソッド内で呼び出されたメソッド名を判別し、より抽象化されたtype判別メソッドを呼び出す● これによってDRIVER_TYPESの定数に値を追加するだけでis_hogeというメソッドが使えるようになる
メタプログラミングのリスク● デバッグが難しい● コードが複雑かつわかりづらくなりがち● 使うことによって得られるメリットとデメリットを天秤にかけて使うかどうか判断したほうがよい● もし使うならメタプログラミングの部分ににはバグが入り込まないように厳密にテストをすることと使い方を説明するドキュメント(コメント)が必要。
解析性(analyzability)● 製品若しくはシステムの一つ以上の部分への意図した変更が製品もしくはシステムに与える影響を総合評価すること、欠陥もしくは故障の原因を診断すること、または修正しなければならない部分を識別することが可能であることについての有効性及び効率性の度合い。● つまり「問題が起きたときの発生箇所、影響範囲、原因の特定がしやすいかどうか」
解析性を高める技術● オブザーバビリティを高めるツールの導入● 適切なログ出力●
オブザーバビリティアプリケーション、インフラ、ログなどあらゆるテレメトリーデータを収集、相関、意味付け、可視化し、複雑なソフトウェアシステムを理解しやすくするための取り組み- NewRelic- Datadog
NewRelic●NewRelicのAPM、クランド連携、agent、ログ収集を行うと問題発生時に以下のようなことがワンストップできるようになる。○ エラーレートの可視化○ 発生したエラーのスタックトレースを表示■ どのAPI(パス)を叩かれたのかもわかる○ そのエラーが発生したときのトランザクション中のログを一覧で表示● これによって問題発生箇所、影響範囲、原因が非常に素早く把握することができる
適切なログ出力●NewRelicで調査しやすくなったとはいえ、適切なログが出力されていないとそこで息詰まる。● なにか問題が起きたときに原因調査するための情報があるか?という視点でログ出力を行う○ 本来発生しない例外をキャッチした場合、その内容をエラーログに出力する○ 外部連携(SaaS含む)をする場合、そのRequest/ResponseをINFOログとして出力する○Webアプリケーション場合Requestのログを出力する(APIであればResponseもログ出力する)■ 秘匿情報はマスクするのは前提○ 大きいバッチ処理をする場合経過状況をINFOログに出力する
試験性(testability)● システム、製品または構成要素について試験基準を確立することが出来、その基準が満たされているかどうかを決定するために試験を実行することができる有効性及び効率性● つまり、「プロダクトの受け入れ条件など決めて、それを満たせているかどうかをテストできるかどうか」
テスト駆動開発(TDD)「動作するきれいなコード」を書くためのプログラミングプラクティスシンプルなテスト駆動開発のルール● まずはシンプルな自動テストを一つ書く● すべてのテストを走らせ、新しいテストの失敗を確認する● 小さな変更を行う● すべてのテストを走らせ、すべて成功することを確認する● リファクタリングを行って重複を除去する
テストカバレッジ● ソフトウェアコードのうちテストがなされている比率(網羅率)● テストカバレッジが100%であれば良いかというとそういう訳ではない。コードカバレッジは、コードのテストされていない部分を発見するための有用なツールである。ただテスト自体がどれだけ良いかという指標としては、テストカバレッジはほとんど役に立たない。カバレッジの数値がほしい理由はわかる。テストが十分かを知りたいのだ。カバレッジの数値が低い場合、たとえば50%以下の場合は、おそらく問題があるだろう。けれど高いカバレッジの数値にはあまり意味はない。ダッシュボードの数字に意味がなくなる助けをするだけだ。from: https://bliki-ja.github.io/TestCoverage/
質の良いテストを書く必要がある
質の良いテストコードを書く技術●3角測量○ 一つのメソッドをテストするとき、2つ以上の実例を持ってテストをする■ それによってメソッドの実装の一般化が抽出される■ ただテストコードの重複によるテスト実行速度の悪化にもつながるので、テストコードを書くときのフローの中で行うが、実装・テストともに完了したら重複を除去することもある● 欠陥挿入○ プロダクトコードの任意の行の意味合いを変えて、テストが失敗するかどうかを検証する○ もし失敗しない場合は適切にテストが書かれていないこと(漏れがある)を示しているのでテストの追加・修正が必要だとわかる
修正性(modifiability)● 欠陥の取り込みも既存の製品品質の低下もなく、有効的に、かつ、効率的に製品またはシステムを修正することができる。● つまり、バグやパフォーマンスの悪化などを起こさずにどれだけ早く機能の追加・修正ができるかどうか
修正性を高める技術● 他の要素を高めること=修正性を高めること○ モジュール性○ 再利用性○ 解析性○ 試験性
質の高いコードを書いて、良いソフトウェアを作って、売上をあげて良い給料を得てよりプログラミングを楽しんでいこうな!
参考書籍● クリーンアーキテクチャ●On Lisp●UNIXという考え方● テスト駆動開発
参考資料● 質とスピード〜ソフトウェア開発の典型的な誤解を解く〜(2020秋バージョン)○https://speakerdeck.com/twada/quality-and-speed-2020-autumn-edition● つながる世界のソフトウェア品質ガイド○https://www.ipa.go.jp/files/000044964.pdf●X 25010 : 2013 (ISO / IEC 25020 : 2011 )○http://kikakurui.com/x25/X25010-2013-01.html● 開発者が知っておくべきSOLIDの原則○https://postd.cc/solid-principles-every-developer-should-know/● 単一責任の原則(Sigle responsibility principle)について、もう一度考える○https://www.ogis-ri.co.jp/otc/hiroba/others/OOcolumn/single-responsibility-principle.html● メタプログラミング○https://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%BF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
参考資料●Webアプリケーションのログ出力について指針を考えてみた○https://blog.mmmcorp.co.jp/blog/2018/05/25/web_application_logging/● 「質」のいいユニットテストを書くためのプラクティス○https://speakerdeck.com/hgsgtk/practices-to-write-better-unit-test● テストカバレッジ○https://bliki-ja.github.io/TestCoverage/●