現実を再利用する

彼らはあなたが車輪を再発明する必要はないと言う。 一見すると、これは明らかです。 開発に時間をかけた場合、なぜそれを再度行うのですか、古いソリューションを再利用できますか? すべての側面から良い選択肢のように思えます。 しかし、それほど単純ではありません。 古い白髪のプログラマーとして 、私は組織が何度も何度もこのエラーの犠牲になり、事前設計と開発に投資していますが、再利用によって約束された大規模なROIを達成することはありませんでした。 実際、 利益と再利用の容易さに関する過度に楽観的な評価は、ソフトウェア開発で最も一般的で危険な落とし穴の1つであると考えています



問題の根源は、 ダニエル・カーネマンが 「あなたが見るものは何であるか」というルールとして定式化したものだと思います 。 一言で言えば、利用可能なデータといくつかの基本的なヒューリスティックのみを使用して、迅速な意思決定に対する厳しい制限を説明します。 思考を遅らせるには時間と規律が必要です。そのため、代わりに、完全に理解していない複雑な問題を単純な問題に置き換えようとします。



繰り返し使用する場合、直観は単純かつ説得力を持って、物理世界でアクセスするのが困難なソフトウェアの「輪」としての類推を表します。 これは便利なメンタルモデルであり、再利用に関する決定を行う際によく使用します。 問題は、そのような再利用の概念が間違っているか、少なくとも憂鬱なほど不完全であることです。 理由を見てみましょう...



(簡単な注意:メソッドや関数のレベルではなく、大規模な再利用について話します。DRY原則をより詳細なレベルで適用するつもりはありません。また、社内で作成された特定のサービス、ライブラリ、外部ではありません。独自のJS MVCフレームワークを作成することはお勧めしません!さて、最初に計画した記事に戻りましょう...)



直観



内部にロジックCを含むシステムAを想像すると、すぐに新しいシステムBを構築する必要があり、同じ基本ロジックCも必要です。











もちろん、システムAから単純にCを抽出する場合、再実装の必要なしにシステムBでCを使用できます。 したがって、節約はCの開発のための時間です-Bのために再度ではなく、一度だけ実装する必要がありました。











将来的には、さらに多くのシステムで同じ共通コードが必要になるため、この割り当てと再利用の利点はほぼ直線的に増加します。 独立した実装ではなくCを再利用する新しいシステムごとに、Cの実装に費やした時間と同等の追加の節約が得られます。



繰り返しになりますが、ここでのロジックは単純で、一見皮肉なものです。一度Cを開発してから再利用できるのであれば、なぜCの複数のインスタンスを開発するのでしょうか。 問題は、画像がより複雑であることです-そして、ROIの簡単な踏み台のように見えるものは、高価な拘束衣に変わる可能性があります。 ここに、再利用に関する基本的な直感がどのように私たちをだますことができるかに関するいくつかのオプションがあります...



現実



最初の問題は割り当てです。 直感的に言って、Cはデザイナーのディテールのように届く、美しくて簡単だという。 ただし、共通コードを解く現実は異なる場合があります。1つのパスタをボウルから引き出そうとすると、皿全体が1つの大きなパスタに過ぎないことがわかります。 もちろん、すべてがそれほど悪くないのが普通ですが、コードには多くの隠された依存関係と接続があるため、C領域の最初の考えは、解き始めるにつれて大きくなります。 思ったほど簡単なことはほとんどありません。



さらに、ほとんどの場合、Cは動作するために他のもの(たとえば、他のライブラリ、ユーティリティ関数など)を必要とします。 場合によっては、これらは一般的な依存関係です(つまり、AとCの両方がそれらを必要とします)。 いずれにせよ、A、B、Cの単純な画像は、それほど単純に見えないかもしれません。 この例では、A、B、およびCが共通ライブラリLを使用すると仮定します。











もう1つの問題は変更です。Cユーザーが異なれば、必要な要件がわずかに異なることがよくあります。 たとえば、Cには、Aが呼び出す場合とBが呼び出す場合とで少し異なる動作をする関数があります。このような場合の一般的な解決策はパラメーター化です。この関数は、その動作を理解できるパラメーターを取りますそれを引き起こした人に留意してください。 これは機能する可能性がありますが、Cの複雑さが増し、「呼び出しがAからの場合、そのような論理ブロックを実行する」などのコードでコードがいっぱいになるため、ロジックも乱雑になります。











CがAとBに本当に完璧な場合でも、DやEなどの新しいシステムの出現により、ほぼ間違いなく変更を加える必要があります。Cをそのまま使用することもできますが、それ自体は、小さく、または大きくする必要があります。 繰り返しになりますが、Cの新しい各フィクスチャはさらに複雑になり、以前はCで理解しやすかったものが、C、D、E、Fなどのニーズを満たすようになり、さらに難しくなります。 次の問題につながる...



複雑さが増すにつれて、開発者がCの機能と使用方法を理解するのが難しくなります。 たとえば、開発者Aは、システムEとFにのみ適用されるため、関数Cのパラメーターを理解できない場合があります。ほとんどの場合、エントリーと終了を説明するために、ある程度のAPI ドキュメント (場合によってはSwagger 、Javadocなど)が必要です条件およびその他のSLA /期待。 ドキュメント自体は良いことですが、問題がないわけではありません(たとえば、最新の状態に保つ必要があるなど)。











複雑さの増加の別の結果は品質を維持するのがより難しくなるということです 。 現在、Cは多くのホストにサービスを提供しているため、テストには多くの境界ケースがあります。 さらに、Cは現在、他の多くのシステムで使用されているため、特定のバグの影響は増幅されます。これは、Cが一部またはすべてのシステムで表面化する可能性があるためです。 多くの場合、Cに変更を加える場合、共通のコンポーネント/サービスのみをテストするだけでは不十分であり、A、B、D、および他のすべての依存システムにもある程度の回帰テストが必要です(変更Cがこのシステムで使用されているかどうかは関係ありません!)



繰り返しますが、重要なスケールでの再利用について話しているので、Cは別の開発チームによって開発される必要があり、自律性失われる可能性があります。 通常、個々のグループには独自のリリーススケジュールがあり、場合によっては独自の開発プロセスがあります。 明らかな結論は、チームAがCの改善を必要とする場合、おそらくプロセスCを経る必要があるということです。つまり、チャンピオンAは要件を提供し、優先順位を守り、テストを支援する必要があります。 つまり、チームAは、Cが実装する機能に関する独自の運命を制御しなくなりました。Cを提供するチームに依存します。











最後に、Cを更新すると、定義により、 異なるバージョンが表示されます。 再利用の性質に応じて、さまざまな問題が発生する可能性があります。 アセンブリ段階(ライブラリなど)で繰り返し使用する場合、さまざまなシステム(A、Bなど)が動作バージョンにとどまり、適切な更新タイミングを選択できます。 欠点は、Cの異なるバージョンがあり、すべてのバージョンで1つのエラーを修正する必要がある可能性があることです。 ランタイムで繰り返し使用する場合(たとえば、マイクロサービス)、Cは1つのインスタンスでそのAPIの複数のバージョンをサポートするか、下位互換性を考慮せずに単純にアップグレードする必要があるため、AとBも強制的に更新する必要があります。 いずれにせよ、そのような再利用をサポートするためのプロセスと組織の信頼性と厳密さの要件は大幅に増加します。











おわりに



要約すると、私のポイントは、大規模な再利用を避けるべきではないが、直感が言うほど簡単ではないということです。 再利用は非常に困難であり、それでも欠点を上回る利点を提供できますが、これらの欠点は現実的であり、事前に検討する必要があります。



慎重に分析した後でも、大規模な再利用正しい場合 、その方法を決定する必要があります。 経験から、中毒矢印に注意することをお勧めします。 「再利用」が制御されている場合の再利用は、ほとんどの場合、再利用されたリソースがシステムにアクセスするときの再利用よりも実装および管理が容易です。 上記の例では、Cがライブラリまたはマイクロサービスである場合、AとBが制御を取得します。 私の意見では、これにより実装が高速化され、長期的には管理/調整のレベルが低下します。



Cをフレームワークまたはプラットフォームに変えると、依存関係の矢印が切り替わり、制御が複雑になります。 現在、AとBにはCがあります。このタイプの再利用は実装が難しい(そして正しく実行する)だけでなく、さらに強力なロックにつながります(つまり、AとBは完全にCに依存します)。 「ライブラリはツールであり、フレームワークは生き方である」と言われています。











最後に、大規模な再利用に関するあなたの意見や経験を知りたいと思います。 いつ機能し、いつ失敗しますか?



All Articles