😎

効率アップのために共通部品を作る? ちょっと待って、その前に!

2024/09/08に公開

効率アップのために共通部品を作る? ちょっと待って、その前に!

やったことがない開発よりは以前開発した製品と似ている開発の方が効率が良くなります。勘所が分かっているので開発の工数が減るのもそうですが、見積もりの精度を上げやすく、リスクが減ります。ですので、過去開発したものと似たようなものを作って楽して稼ぎたいと思のは自然です。

過去の製品のソースコードをベースに新しい案件を始める方法もありますが、お勧めできません。

  • 請負開発では、以前の製品に残っている業務知識が他の会社に漏れる。
  • バグの際の修正で、製品をまたがって、修正箇所を探して、それぞれで対応しなければならない。しかし、これを正しく行うのは難しい。

ということで、似ている部分をまとめて共通化して、さまざまな製品で共有しよう! となるわけです。

ちょっと待って、その前に!

「共通部品を開発して使いまわそう!」という発想はごく自然なものです。組織の強みを共通部品化して、強みを生かしやすくするのは当然です。気を付けなければならないのは「共通部品がうまく開発できた場合」のみ効果があるということです。

共通部品の出来が悪いとアプリケーションの開発に悪影響を及ぼします。アプリケーションの要求の変化に合わせて共通部品を修正し、それが関係のないアプリケーションの不具合を発生させます。こういったことを繰り返すうちに共通部品の実装はより複雑になり、可読性が低下し、修正を困難にし、テスト工数を増大させます。テストで利用できる時間も限られていますので、不具合が頻発し、その対処にも時間を奪われるようになります。

このようなことを避けるためにまず理解しなければならないのは、アプリケーション開発のノリでは共通部品の開発はできないということです。共通化をしようという発想自体は素晴らしいものですが、やみくもに始めるのではなく、一度立ち止まって考える時間が必要です。

  • 共通部品の不具合はアプリケーションのそれと比べて少なくなければならい
  • 共通部品の仕様は安定していなければならならない
  • 共通部品は未知の要求に耐えられなければならない

共通部品の不具合はアプリケーションのそれと比べて少なくなければならい

別業界ですが、とある自動車のリコール情報を見ると、一つの部品のリコールが複数の車種にまたがっていることが分かります。

  • ノア
  • レジアスエース
  • ランドクルーザー
  • クラウン
  • etc...

最近のリコールのすべてでこのような傾向がみられるわけではありませんが、昔と比べると一つの部品のリコールが及ぼす車種の範囲が広がっているように思えます。

自動車の開発はコストに対する要求が厳しく、部品の流用が以前より多くなっているようです。(もちろん、流用できない部品も数多くあります) 結果的に一つの部品の不具合がたくさんの車種に影響を与えることになります。

これらの共通部品の不具合は車種固有の部品の不具合と比べると規模が大きくなります。ですので、共通部品はより高い品質レベルが要求されます。(もちろん、車両固有の部品の品質をないがしろにしていいというわけではありません)

これはソフトウェア開発でも同様です。

共通部品の仕様は安定していなければならい

共通部品が仕様変更されるとアプリケーション側の修正も必要になります。

例えば、共通部品を組み込んだアプリケーション A で、共通部品の仕様が合わずに共通部品の仕様変更を行うことがあります。しかし、同じく共通部品を組み込んだアプリケーション B には悪影響しかありません。

アプリケーションの要求に合わせてフラグで分岐することもできますが、何度もこういったことを繰り返すとソースコードの可読性が低下します。分岐がノイズとなり、具体的に何をやってるかがソースコードを読んでも分かりにくくなります。また、アプリケーション A のための修正で他のアプリケーションへの影響が避けられず、可読性の低いコードを注意深く読み、さなりには他のアプリケーションに影響がないかたくさんのテストを行う必要があります。それでも不具合の発生を防ぎきれません。

このような品質の悪化は二次曲線的に増大することに注意してください。なんかおかしいと認識できた時には手遅れなことが多いです。

共通部品は未知の要求に耐えられなければならない

「共通部品の仕様は安定していなければならない」と説明しました。だからと言って仕様を強制的に固定化はできません。ビジネスの変化に追従するために、共通部品に対しての変更要求は無視するわけにはいきません。

将来のビジネスの要求を分析して設計するという方法もありますが、5 年も先になると予測はほとんど当たりません。

この二つの相反する要件を高度に両立する必要があります。

どうしよう?

共通部品の開発がアプリケーションの開発と同じノリでできないことを見てきました。ではどうすればいいのでしょうか?

  • 共通部品の機能は最大公約数で提供する
  • 共通部品の機能は足し算で使えるようにする
  • 既存のアプリケーションをそのまま共通部品に流用しない
  • よくできたフレームワークを研究する
  • SOLID 原則・複雑度・凝集度などのプログラム設計を身に着ける
  • テストを自動化する

共通部品の機能は機能は最大公約数で提供する

共通部品の開発の失敗でやりがちなのが、あらゆるアプリケーションの要求を共通部品側に取り込む、最小公倍数で開発してしまうことです。最小公倍数では特定のアプリケーションの要求の変更が共通部品の仕様変更を招き、仕様が安定しなくなります。

これを避けるために、共通部品の機能は各アプリケーションの要求の共通の部分だけの最大公約数で提供するようにします。各アプリケーション固有の要求はそれぞれで実装するため、共通部品への要求が減ります。

最大公約数で作成された共通部品 (真ん中の小さな黒丸) は三つの円で示されたアプリケーションそれぞれの共通の要求の変更でしか修正されませんが、なんでも取り込んだ共通部品 (少し大きなグレーの丸) はそれぞれのアプリケーションの要求の変更を受けます。

共通部品の機能は組み合わせで使えるようにする

たくさんのことをこなす共通部品の関数は便利です。しかし、似ているように思えるアプリケーションであっても予想以上に違うものです。たくさんのことをこなす関数は「この機能はいらない」「この機能を追加してほしい」が発生しやすく、仕様が安定しなくなります。

例えば、入力用モデルを検証してエラーをポップアップする共通部品の関数がそれです。

TodoInputModel model = GetInputModel();

ValidateAndPopupErrors(model);

この関数は次の問題が考えられます。

  • 入力モデルの検証以外のエラーもポップアップしたい
  • 入力モデルのエラーに応じて処理を分けたい
  • エラーをポップアップダイアログではなく、ウインドウ内に表示したい

アプリケーション側の実装は面倒になりますが、入力用モデルの検証とエラーをポップアップする処理はそれぞれ別の関数として提供したほうが、各アプリケーションごとの要求の違いに対処しやすくなります。

TodoInputModel model = GetInputModel();

var errors = Validate(model);
if (errors.Any()) PopupErrors(errors);

ウインドウ内にエラーを表示するのはすぐにはできませんが、アプリケーション側でその実装を書くだけで共通部品を修正する必要はありません。

この例のように、共通部品の関数はひとつのことだけをこなす関数を提供し、アプリケーション側で組み合わせで使えるようにします。もちろんたくさんのことをこなす関数が必要であればそれを用意しても構いません。その場合であっても、一つのことだけをこなす関数も合わせて提供します。

また、アプリケーションの実装の際に、共通部品の関数がたくさんのことをこなしすぎて不便だと感じたら、その関数を複数の関数呼び出しに分割し、組み合わせでも利用できるようにします。

既存のアプリケーションをそのまま共通部品に流用しない

新しく開発するアプリケーションも以前開発したものと似ているので、そのアプリケーションを共通部品として流用しようと思う方は多いと思います。これは避けてください。

  • 多くのアプリケーションは共通部品で要求されるような不具合の少なさを達成できていません。元のアプリケーションでは不具合として発生しないものが、共通部品として他のアプリケーションから利用して初めて不具合として露見することもよくあります。
  • 多くのアプリケーションは流用を考えられておらず、そのままでは最小公倍数的になる。(流用を考えて開発するのはよくないので、流用を考えずに開発すること自体は問題ありません)

これらの問題を避けるために、新しいアプリケーションの開発をしながら、元になるアプリケーションのソースコードと合わせて分析し、必要な最大公約数を抽出していきます。この方法は時間がかかるように思えるかもしれませんし実際に時間がかかりますが、共通部品を使う将来の製品の開発が楽になります。

(余談ですが、自分は共通化は三度目くらいから本気出すことが多いです。二つ目は共通化のテストヘッドを兼ね、いろんな設計をトライしてノウハウを得ます。そのノウハウを反映した共通部品を三つ目と並行して開発します)

よくできたフレームワークを研究する

共通部品は安定した仕様と未知の要求への対応を両立する必要があると説明しました。それらのいいお手本があなたの目の前にあります。それはあなたが今開発で利用しているフレームワーク (ASP.NET Core や Spring Boot などなど) です。それを研究してください。作るアプリケーションの要求に合わせてフレームワークの修正をそれの開発元に依頼するなんてやったことありませんよね? (やったことがある方は、このドキュメントを読む必要はないと思う...)

これらのフレームワークは簡単なアプリケーションならば手軽に作れますが、より複雑なアプリケーションの開発にも耐えられるように設計されています。例えば HTTP リクエストやレスポンスに介入できるようになっており、アプリケーション側でかなり細かな制御ができます。これら以外にも未知の要求に耐えるための工夫が随所に見られます。これらは本当に参考になります。

日頃使い慣れたフレームワークを研究し、どうやって仕様の安定と未知の要求への対応を両立しているのかを参考にし、真似してください。

SOLID 原則・複雑度・結合度・凝集度などを学ぶ

コンピューターが生まれたプログラムがお仕事になった黎明期はプログラミングは少人数でやるものでした。コンピューターの性能が向上するにつれ、ソフトウェア開発の規模も大きくなります。1960 年代にはそれまでにない開発規模の OS/360 (書籍「人月の神話」で有名) や月まで有人飛行を行うアポロ計画などの開発が増えていきます。

1960 年代の後ろの方になるとソフトウェア危機 が叫ばれます。ソフトウェア開発の規模はどんどん拡大するため、品質の悪化・開発工数の増大がどんどん進行し、最終的には開発が不可能になるといったものです。

頭がいい人たちの努力のおかげで様々なものが生み出され、ソフトウェア危機で叫ばれた問題の多くを小さくすることができるようになりました。その恩恵を私たち開発者は享受してアプリケーション開発を行っています。

ソフトウェア開発の規模は今でも増大の一途です。今では少人数開発でも以前からは考えられないくらいの規模となりました。小さく無視できるようになった問題が無視できない大きさになってきていると感じます。

共通部品はアプリケーションよりこれらの問題が顕著化しやすい傾向があります。頭がいい人たちが考えた、SOLID 原則・複雑度・結合度・凝集度などの原則や指標が安定した仕様と未知の要求への対応には欠かせません。

もちろん、これらの原則や指標を私のように誤解して使うと痛い目にあいますので、誤解しないよう識者に助言をもらいながら学んでいくのが重要となります。

テストの効率を上げる

共通部品の多くは共通部品だけでは手動でのテストができません。共通部品を利用しているアプリケーションでテストをする方法もありますが、そのアプリケーションが利用していない機能や分岐はテストができません。共通部品のテストを手動に頼っていてはアプリケーションの何倍も時間がかかるようになります。期限でテストを打ち切ることもままあり、不具合が多発します。

自動テストはもちろんのこと、テストしやすいプログラム設計にする必要があります。

もちろん、仕様が安定していればそれほとテストの頻度は高くありませんが、そのような完璧な共通部品はなかなか作れません。仕様を安定化させるリファクタリングのためにもテストは重要です。

始めよう!

共通部品の開発って大変ですね! だからやめましょう! ってのはよくありません。品質のいい共通部品を様々なアプリケーションで共有し、開発コストとリスクを下げ、会社の強みを生かし、より要求に近いところの開発に集中できるようになるのが理想で、それを目指すのは当然のことです。

とはいえ、いきなり理想的な共通部品を開発する技術力が身についたりはしません。ちょっとずつというのが重要です。

まずは今開発しているアプリケーションでプログラム設計の品質を高める学習や実践を試行錯誤してください。試行錯誤は失敗も含めてノウハウですが、共通部品での失敗はアプリケーションのそれより痛い目を見ます。もちろん、共通部品でも試行錯誤の連続ですが、まずはアプリケーションの中の共通なところを部品化する試行錯誤で練習してください。

そして、共通化は小さく始めます。特定のアプリケーションからそのまま共通部品を抽出するのは避けてください。明らかに流用すると便利だろうと思えるもののうち、小さいものから少しずつ共通部品化し、学習・試行錯誤しながら共通部品を拡充していきます。

この共通部品おかしいかな? と思ったら早めに対処するのも重要です。アプリケーションの開発では見ないふりが通用した問題でも、共通部品ではそうはいきません。しっぺ返しの痛さが全然違います。

最後に

長々と書きましたが、アプリケーション開発では共通部品ほど高品質 (ここでいう品質は不具合が少ないだけでなく、可読性・変更容易性など様々なもの) が要求されないだけで、アプリケーション開発でこれらを無視していいわけではありません。

「開発のノリが違う」と全く異なる知識や技術が要求されるような説明をしましたが、実際はそうではありません。共通部品の開発はアプリケーション開発の延長線上にあります。

開発効率をあげようと何かを変えたのに、よくなった実感がわかない場合、その多くは手段ではなく基礎的な問題なことが多いです。邁進する前に、一度何が問題なのかを見つめなおすことも大事です。

Discussion