開発品質を上げる手法についてディスカッションしてみた
株式会社SunAsterisk でまったりエンジニアをしているtakeです。
今年はアドベントカレンダーに参加してみました🙌
概要
社内勉強会の一環で、開発品質を上げるためにはどうすれば良いか?という主題のもと
各個人の課題認識やそれに対する改善案などを収集して共有するイベントを開催しました。
そこで出てきた改善案の中から応用可能な知見や具体的な実践方法について整理してみました。
課題認識
課題認識について問うべく、
コード品質に関して、品質が低いと感じるシーンはどのようなシーンですか?
と言う題目で意見を募ってみたところ
- 非機能要件面の考慮が不足した実装がある
- パフォーマンスの問題がある(N+1など)
- セキュリティの問題がある(SQLインジェクションなど)
- 拡張性に乏しい実装がある
- 可読性の低い実装がある
- DRY原則が守られていない
- 関数やクラスの責務が不明瞭で肥大化している
- エラーハンドリングが不十分で処理フローの考慮漏れがある
- テストケースが適切に定義されていない
- テストカバレッジが低い
- テストが不十分なために仕様を実装から読み解くしかない
- 複雑なロジックに関してコメント等の補足情報が少ない
等の意見が上がってきました。
可読性が低い、テストカバレッジが低い、非機能要件や例外処理等の考慮漏れがある
といった成果物に対して品質の低さを感じることが多いようです。
これらの問題点に関してはPullRequestのレビュー時に指摘されることとなりますが
コードレビューのタイミングで毎度100%正確に都度指摘するには限界があります。いつの時代もレビュアーの工数には限界があるものです
レビュアーの限られた工数の中でどのように品質向上させるか、については後続のセクションで触れていきます。
品質向上へ向けたソリューション
品質向上の手段として検討できるものはいくつかありますが
それぞれの手段で具体的にどのようなものがあるかについてディスカッションしたものを紹介します。
規約/ベストプラクティスの順守
開発チームに守るべきコーディングルールや規約、ベストプラクティスを周知、導入する方法です。
具体的なものとしては以下のようなものがあります
- アプリケーションレベルでのアーキテクチャ設計の明確化/導入
- DDDのアーキテクチャパターンを導入(e.g.レイヤードアーキテクチャ、クリーンアーキテクチャ、etc.)
- 既知のコーディング規約/原則を守るべき原則としてルール化
- DRY,SOLIDなど
- 変数/関数の命名規則をルール化/明文化
- ブランチ運用戦略の明文化/ルール化
- トランクベース / Github flow / Git flowなど
- コミットメッセージのルール化
など
後半の項目に関してはコーディング規約ではなくGitの運用ルールについて触れたものになります。
コミットメッセージにルールを適用することで変更履歴がより読みやすくなる、追跡しやすくなるメリットがあったり、
ブランチ運用を一定のルールに基づいて行うことで、リリースと開発の依存を減らすことができたり、変更履歴が追いやすくなったりなど
コーディングルール以外においても規約やルールの適用によって開発体験や保守性の改善につながったりします。
テストの適切な実装/実施
テストを適切に実装、実施するというシンプルな手段です。
社内でディスカッションする中ではテスト手法について触れられました。
テスト手法
テスト手法としては大まかな分類[1]として
- Unit test
- Integration test
- UI test
があり、それぞれrspecやjestといった実行環境に応じたテストライブラリを利用してコードベースでテストを実装する。
UI testなど結合度が高いものに関しては手動でテストを行う、必要に応じてテストライブラリを用いたコードベースでの自動テストを適用する、などの手法があります。
またアドホックでパフォーマンステストやペネトレーションテストや脆弱性診断等の非機能要件面の検証を行うケースが多いようです。
こちらもチームやプロダクトの状況に応じて、
- どのテストを自動化するか
- どのタイミングでテストを実施するか
- リリース時
- 毎リリース時
- 大型リリース時
- git push時
- mainブランチへのマージ前(Pull Requestの作成時など)
- リリース時
等を考慮する必要があります。
ツールの利用
前述の項目にある、テストの実施やルールの適用をより確実なものとするための手段として
ツールの利用が挙げられます。
具体的なものとしては以下があります
- 静的解析ツール
- commit lint(pre commit)の適用
- コミットメッセージのルールが守られているかコミット時にチェック
- linter/formatterの適用
- Rubocop,eslint,swiftlintなど静的解析を行い、コーディングスタイルを強制する方法
- Periphery(Swift),deadcode(Go),oneshot coverage(Ruby)等を利用して、デッドコードを検知する
- brakeman gem,bullet gem(Ruby,RoR)を利用して脆弱性のあるコード、パフォーマンスの問題があるコードを検知する
- commit lint(pre commit)の適用
- テストカバレッジの計測
- Slather(swift),SimpleCov(ruby),pytest-cov(python)等を利用して、テストカバレッジを定量的に計測する
- ※正確にはテストカバレッジを定期的に計測し改善活動に繋げるためであり、カバレッジの計測自体が直接品質改善に繋がるわけではありません
- その他
- 生成AIレビューツールの活用など
ツールの利用も適切な運用(必ず実行する、実行した結果を元に改善する)に載せることが前提で、運用がうまくできていない場合形骸化してしまいます。
運用に載せる手法の例として、Github Actions等のCIパイプラインやgitのcommit hook等と組み合わせることで、特定のタイミングで自動的に解析が起こるように強制することができます。
レビュープロセスの最適化
ツールや標準化したルールではカバーできない問題点や改善箇所をレビュー/修正するのは未だ人の手に依存する要素となります。
具体的にどのようなレビュー観点があるか、列挙してみます。
- チーム内で選定したアーキテクチャパターンに則っているか
- DRY原則を守っているか(過度な最適化をしていないか)
- 可読性が低くないか
- テストカバレッジが十分か
などが声として多く上がりました。
チーム内で設定したディレクトリ構成や実装ルールなどのアーキテクチャに則って実装してあるか、これが守られないとコードが無秩序化する上に、設定したアーキテクチャの恩恵が受けられなくなります。また設定したルールから外れた無秩序なコードの混在は技術負債を加速的に増やしてしまうので絶対に避けたいところです。
また、これらの観点は現在時点では自動で判断しにくいものが多くPullRequestレビュー時のレビュー観点表としてドキュメンテーションしておくなど、ナレッジを残し運用を徹底することでより高品質になります。
継続的な改善
どんなに気をつけて実装を行なっても、レビューを厳格に行なっても技術負債は時間と共に増えるものです。そもそも実装者には責務がないような負債(ライブラリや言語のversionのEOL、新規脆弱性)もあります。それらに対応するためには継続的に改善活動を行うことが大前提となります。
継続的な改善活動を再現性を持って行うための手法について深掘りしてみました。
- NewRelicやdatadogなどを利用した監視
- チューニングや修正
- dependabotによるアラートの導入
- コンテナレジストリサービスの脆弱性検知機能の利用
- ビジネスチームとの開発優先度の継続的なすり合わせ
- 開発チームでの継続的な振り返り/課題抽出の実施
- コードの複雑度を測定する
などの手法がありました。
具体的には
監視を通して、パフォーマンスの問題やバグの検知を行い負債のチケット化を行う。
dependabotやコンテナレジストリの脆弱性検知機能を通じて脆弱性のあるパッケージやライブラリを検知、チケット化を行う
コードの複雑度を定期的に計測することで、慢性的に負債化しているコードを検知、チケット化を行う
といったある程度自動化できる方法で負債を検知、チケットに起こすといった手法や
開発チームでの継続的な振り返りの実施を通じて、上記のような一定の手法にとらわれない、技術負債や課題のチケット化を行う方法があります。
ビジネスチームとの開発優先度の継続的な擦り合わせという観点に関して、
技術負債への対応は以下のような理由で優先度が下がりがちです。
- 短期的にはプロダクトへの影響が見えにくい
- ビジネス優先度とのギャップが生じる
解決のためには - 工数の一定割合を必要最小限の保守コストとしてリファクタリングにあてる
- 会議体やissueの起票内容をもとにbiz/dev間でビジネス優先度の比較、すり合わせを行う
といった手法があります。
工数を確保する方法に関しては成功事例や統計データなど定量定性的に工数確保の妥当性を表現することで交渉しやすくなります
優先度のすり合わせはbiz/devのメンバーが対等な立場で情報共有できることが重要です。双方が対等にコミュニケーションを行うために、bizメンバーが考慮しているビジネスKPIや顧客価値に対する考え方に関してdevメンバーも一定の理解が必要です
まとめ
これまでの内容を要約すると
開発品質を高めるための手法としては以下ようなフローを確立することが重要となってきそうです
開発においてもPDCAを回すことが重要になってきますね。
- 課題を定期的に検知する(自動/手動/集合知の活用)
- 検知した課題をチーム全体で優先度付けする(Biz/Dev間のコミュニケーションを強化)
- 優先度に基づき適切に対応する(リファクタリングの工数を確保)
-
Unit test < Integration test < UI testの階層構造は
Mike Cohnの「Succeeding with Agile」という本で触れられている「テストピラミッド」という概念で触れられています。
ここではテスト手法、テストを実行する方法についてのみフォーカスして紹介していますが、テスト手法ごとの費用対効果や責務も併せて覚えておくとテストの価値の最大化につながります。 ↩︎
Discussion