はじめに
設計の勉強をしていると、変更容易性とか結合度は低くするのが良いとかそういう話が度々出てくると思います。
ただ、なぜ変更容易性が大切なのかというなぜの部分が自分の中で明確になっていないことに最近気づきました。
そして本を読み、それが自分の中での経験と紐付き自分なりの結論が出たのでまとめてみたいと思います。
なぜ変更容易性が大切なのか
まずはプロダクトを開発する上で変化することを具体的に挙げてみたいと思います。
- 要求
- 顧客との話し合いで要求が変化する
- 運用が進んで使っている人の考え方が変わって要求が変化
- 働いている人のレベルが上がって要求が変化
- チームのメンバーの構成が変わって要求が変化
- 業務領域、ビジネスの変化によって要求が変化
- 実際に開発した機能を使ってみたら実際はもっとこうした方が良かったっていう変化
- 顧客やユーザーが周りの人たち(コミュニティ、家族、友人)などの影響を受けて要求が変化
- テクノロジーの変化による要求の変化
- 業務フローの変化による要求の変化
- ライバル企業のプロダクトの進化による要求の変化
- フレームワークやライブラリ、言語、開発環境、ツールの変化
- 法律の変化
ざっと思い当たる、プロダクト開発における変化しやすいことを上げてみました。
こう上げてみると、色々と変化するんだなと感じるかもしれません。
特に「要求」はめちゃくちゃ変わるんだな〜と感じると思います。
要求はさまざまな要因、影響で変化したり、増えたりします。
変更しやすいプロダクトになっていないと、この変化についていけず、いいプロダクトにならなかったり、競争が厳しい業界だったら他社に遅れをとることになります。
だからこそ変更容易性が大切だとされているのです。
変化に対応できる状態にしておくために
まず変更のしやすさに関しては自分の中では二つ視点があると思っていて、
第一がコードの可読性やコードの量(機能を少なくし、仕様をシンプルにし、できる限り減らす。増やさない)を意識して変更しにくい状態をできるだけ作らないようにする という視点で
第二が結合度や凝集度やDRY原則、単一責務の原則などのコード自体のクオリティーを上げ変更しやすい状態を作る という視点です。
一つ目の視点ですが可読性が優れていて、コードの量自体がとても少ないものであれば、凝集度が低く、結合度が高いプログラムであったとしても変更は容易にできると思います。
可読性が優れていて、コードの行数を少なく保つことができればできるほど、変更の難易度はあまり上がらないで済みます。
二つ目の視点ですが、これはコードの量が増えてきた時に特に効いてくると思っています。
変更する時に、あるメソッドを変更したら全く関係のない場所が壊れた。。。というような状態だったら変更はままなりません。
または、あるロジックを変更したいんだけど一箇所にまとまってなくて、50箇所くらい変更しないといけない。。。という状態でも変更にかかる時間、難易度は増えてしまいます。
そうならないためにも、二つ目の視点も重要です。
(ここから下はかなり自分自身の思想が含まれますが)
とはいえ、最初からガチガチに設計をしたり、クリーンアーキテクチャを厳格に適応したりするのが良いのかというと、自分はそうは思っていなくて、
厳格に設計をするとフォルダの数やファイルの数が増えたり、どの層にどういう役割を持たせるのかの判断や指針の策定、学習などに時間がかかったりして、実装する速度自体は落ちる側面はあると思っています。
(いい悪いは作る予定のプロダクトの規模感や複雑度にもよると思いますが。。。)
まずは形にしてみないとわからない、ぱっと作ってみよう という段階だったとしたらそこまでの設計はやりすぎだと思うのです。
ではどのような設計の方針でいくのがいいのかというと個人的には、作るプロダクトの大きさや複雑さにもよるところはあるとは思いますが、
基本のアーキテクチャはMVCで、
一つ目の視点を大事にし、できるだけ機能は作らず、仕様もシンプルにしておき、できるだけ可読性を高く保ち、
二つ目の視点では凝集度を重視していき、結合度は優先度を下げる(or 考えない)
というのがいい塩梅なのではないかと思っています。
できるだけ早い段階で動くものを作ってユーザーに触ってもえるようにし、フィードバックをもらい、さらに変化にもある程度対応できるように備えておく、できる限り変更やすい状態を保持しておく、
そしてもし限界が来たら「作り直す」という選択をできるような設計が良いと思っています。
作り直す時は、今度は二つ目の視点を重視して設計をしていくことになるのだと思います。
(その「作り直す」という選択が来ないという話もプロダクトの規模や複雑度によってはあるかもしれませんが。。。)
「曳光弾」
「最小限の機能で最大限の学びを得る」
「プロトタイプ」
的な思想を支持しつつ、変化にもある程度対応できる というような設計が良いのではないかなぁ〜と最近は考えています。
最初から「作り直す」ことをしないように、最善の設計を考えてから開発を進めるよりも、「作り直す」ことは前提に設計を考えておいた方がいいことが多いのではないかなと思ったりします。
(もちろん、作る予定のプロダクトの規模が大きすぎて、作り直すなんてできない。。。っていう文脈もあると思います。そういう文脈であれば、また違った考え方をしないといけないと思います。)
第一の視点について
機能は少なく、小さく、シンプルに
これはプロダクトマネジメントに近い考え方があったり、エッセンシャル思考より(より少なく、しかしより良く)な考え方だと思っています。
そもそもコードを書くこと自体が負債になると自分は思っています(究極コードを書かないで問題解決できればそれが一番良い)。
コードの量が多くなればなるほど、バグが発生する確率や保守運用、メンテナンスをするためのコストは上がります。
完成した機能の64%は使われないというデータもありますし、たくさん作ったり、大きく作ってそれが使われないなんてことになったらコスト(人件費など)の無駄にもなります。
機能を大きく作ってしまうと、価値提供をするまでの時間や機能の利用者からフィードバックをもらえるようになるまでの時間が長くなります。
それに手戻りが発生しやすくもなるのです。
機能を大きく作ったり複雑に作ったりするメリットはないのです。
それと機能がたくさんあればあるほど使いやすいプロダクトである ということでもないのです。
あとは複雑なコードになっていると、メンテナンスしにくいのはもちろん、いざ作り直すぞ!っていう時の障害にもなりかねません。
機能を開発するときは、小さく、少なく、シンプルに を意識するといいことが多いのではないかと思います。
コードをシンプルに読みやすく保つ
書いたコードが開発者に伝わりやすいものになっていればいるだけ変更しやすいコードであると言えると思います。
その中でもいくつか要素があると思っています。
命名
綺麗なコードは変数名やメソッド名だけで、具体的な処理のコードを読まなくても大枠どういうことをやっているかを理解することができます。
第三者にも伝わるような命名をしていきましょう。
自分の書いたコードは第三者からみて読みづらくないか
上の命名 とも関連しているのですが、自分の書いたコードは第三者が見て簡単に理解できるかどうか、もし自分のコードを改修するとしたら改修しやすいものになっているかどうか は重要な要素だと思います。
第三者視点で自分が書いたコードを見直してみましょう。
意図をコメントで残しておく
上の第三者からみて読みづらくないか とも関連しているのですが、コードだけではどういうことをやっているのかを表現できたとしても、なぜやっているのか、どうしてこういうことをやっているのかという表現はできません。
意図がコードの文脈だけでは伝わりにくようでしたらコメントで意図を伝えるようにしましょう。
割れ窓は残さない
ソフトウェアも時間と共に無秩序になっていきます。
無秩序になるきっかけになるのが割れ窓です。
割れ窓を放置しておくと(一箇所でも汚いコードがあると)、「残りのコードもよく無いコードだから、同じようにやっておけばいいや」という考え方をもとに無秩序の度合いが上がっていきます。
(悪いコードを参考に、どんどん悪コードが量産されていきます)
割れ窓は放置せずに、もし見つけたらすぐに改善するようにしましょう。
その他原則
- YAGNI
- KISS
- PIE
第二の視点について
- 凝集度は高く
- 結合度は低く
この二つの要素によってコードの良し悪し、クオリティーが測れます。
凝集度、結合度の話はサイバーエージェントさんのエンジニア新卒研修資料の良いコードとは何かにとてもわかりやすく書いてあるので読んでみるのがおすすめです。
あとはテストは重要な要素だと思っています。
テストがあるとリファクタリングやコードを書き直した後に挙動が変わっていないかどうかを確認できるので、変更のしやすさにつながる側面があったり、あとはライブラリをアップデートした時に影響が出ているかどうかがテストを実行することでわかったりするので、そういう意味で変更のしやすさに貢献する仕組みでもあるのかなと思っていたりします。
テストに関してはおそらく単体テストの考え方/使い方を読むのが有意義なのではないかと思います(アフィリエイトではないです)。
第二の視点では他にも
- SOLID原則
- 契約による設計
- デメテルの法則
- DRY原則
- クリーンアーキテクチャ
などなどたくさんの要素があります。
第二の視点に関わってくるような原理原則は
プリンシプルオブプログラミング(アフィリエイトではないです)
を読むとかなり網羅できると思うのでぜひ読んでみてください。
とてもおすすめです。
最後に
本当は一個一個全て自分の言葉で説明、解説したいと思ったのですが、途轍もない時間と、文字数が必要になってくるので、今回はさすがに控えました。。。
この記事でお伝えしたかったのは、要求は様々な要因、影響で頻繁に変化していくものなので、その変化に対応できるようにプロダクトを変更容易な状態にしておかなければいけないということ、
そして変更容易にするには可読性やコードの量(機能を少なくし、仕様をシンプルにし、できる限り減らす。増やさない)という視点と、コード自体の品質、クオリティー(凝集度が高く、結合度が低いのが良い)という視点で考えるのが良いのではないかということ、
そしてプロダクトの一番最初の作りはじめの部分は前者の視点を大事に、そして後者の視点では凝集度を意識して、結合度はあまり考えないくらいの方針がいいのではないのかということでした。
変更容易性を保持しながらプロダクトを開発していかないといけない反面、早く作ってユーザーに触ってもらい、フィードバックをもらって改善する というループを早く回したいとなった時、設計の難しさは際立つ気がしています。
ここは結構トレードオフな気がしています。
早さだけを重視したなら、変更しづらく、変化についていけないプロダクトになると思いますし、
変更容易性だけを重視したなら、変化には強くなりますが、早く作ってユーザーから早くフィードバックをもらうというのが難しくなると思います。
そう考えると、やはり0→1で開発して試行錯誤しながらサービス開始、運用までいくフェーズの設計、その後運用フェーズにのってきたらシステムの成長のための障害を取り除くような設計 というふうに設計にもプロダクトのフェーズやコンテキストによって相応しい、相応しくないがあるんだろうなと思いました。
この記事を書いていて、アーキテクチャの分野ではまだまだ勉強すべきことがあるなと感じたので引き続き、キャッチアップ頑張っていきたいと思います。
(変化のことをについて書いたなら、アジャイルについても触れるべきだったかのかも。。。)