「コア」ライブラリが悪い考えになるのはいつですか?


8

ソフトウェアを開発するとき、私は多くの場合、さまざまなプロジェクトで共有および参照できる便利なコードを含む集中型の「コア」ライブラリーを持っています。

例:

  • 文字列を操作する関数のセット
  • 一般的に使用される正規表現
  • 一般的な展開コード

しかし、私の同僚の一部はこのアプローチを避けているようです。バグが修正されると、多くのプロジェクトで使用されるコードを再テストすることによるメンテナンスのオーバーヘッドなどの懸念があります。今私はこれをいつすべきかを再考しています。

「コア」ライブラリを使用することが悪い考えとなる問題は何ですか?


コードが一般的に再利用される場合は、コアライブラリを用意することをお勧めしますが、単体テストやその他の宇宙技術を含め、厳しくテストする必要があります。
ジョブ

安定していて変化しない場合は、この方法をお勧めします。
マーティンヨーク

再テストの懸念は非常に有効です。6か月前にメンテナンスプロジェクトを中断したことを確認しますか?

必要になるたびにすべてのユーティリティコードを書き換えるなんて想像もできません。

回答:


12

コアライブラリは、機能のクリープに悩まされ始めたときは悪く、十分にメンテナンスされていないと非常に悪いものになります。

この記事は、(私が心から同意する)拡張された視点から見て興味深いかもしれません。

http://www.yosefk.com/blog/redundancy-vs-dependencies-which-is-worse.html


Don Knuth:「私にとって、「再編集可能なコード」は、編集不可能なブラックボックスやツールキットよりもはるかに優れています...再利用可能なコードがほとんど脅威ではないと私が納得することは決してないでしょう。」


3

複数のプロジェクトがコアライブラリに依存している場合にコアライブラリが悪いという考えを使用することは、Web用のjQuery、* nixアプリ内のlibxml、またはその他のフレームワークやライブラリを使用するべきではないと言うようなものです。最新の開発のエコシステム全体(DRY、OOPなど)を見てください。すべてのアプリは、一連のライブラリとフレームワークに基づいて構築されています。

悪いのは、どのタイプの単体テストも、回帰テストも、ライブラリでどのタイプのAPI / ABIも使用していない場合です。すべてのアプリケーションに適切なテストがある場合、ライブラリには適切なテストがあり、関数呼び出しを中断した場合は、APIバージョン番号を適切に更新します。

完全にカバーするには、おそらくライブラリに変更が加えられたときに、APIが壊れていないこと、およびすべてのコードの実行にバグがないことを確認する一連のテストを実行できます。次に、最新のライブラリ更新をアプリケーションに取り込み、同じテストセットを実行できます。APIを更新する場合は、それを文書化して、それを更新するためにアプリケーションで何をする必要があるかを理解してください。どちらの方法でも、アプリケーションのテストを実行すると、テスト中と同じように、何も壊れていないという自信を持つことができます。

jquery、mootools、JavaScriptライブラリやフレームワークを使用する場合、新しいバージョンを盲目的に使用するだけでなく、悲しいことに1.6.zのマイナーリリースを使用することもできません。


3

バグが修正されると、多くのプロジェクトで使用されるコードを再テストすることによるメンテナンスのオーバーヘッドなどの懸念があります。

コアライブラリの単体テストの包括的なセットがある場合。それは問題ではありません。すべてのテストに合格しない限り、コードはチェックインされません。欠陥を導入する場合は、失敗したテストを作成して欠陥を再現し、修正します。その後、常にそのエラーについてもテストします。永遠に。

また、説明する機能は単体テストを書くのが非常に簡単です。

副次的な問題として、複数のコアライブラリが必要になる場合があるため、必要でない限り、RegExコードを含める必要はありません。


2

これについては少し異なる見方をします。多くの場合、コアライブラリは優れたアイデアです。

2つの別々のプロジェクトがある場合、それらは2つの別々のコードリポジトリにある必要があります。現在、それらは共通の機能に依存しています。例として、パケット処理アプリケーションを考えてみましょう。共通の機能には次のものがあります。

  • メモリアロケータ
  • アドレス解決プロトコル
  • AVLツリー
  • バイナリプロトコルのシリアル化コード
  • 動的配列
  • 単一にリンクされたヘッドと二重にリンクされた中間ノードを持つLinuxカーネルスタイルのハッシュリスト
  • ハッシュ表
  • TCP / IPヘッダー処理コード
  • 二重にリンクされたヘッドと二重にリンクされた中間ノードを持つ通常のリンクリスト
  • ロギングライブラリ
  • その他(私を信じてください、あなたは小さくて些細なもののためにこれが必要です、またはあなたの異なるモジュールの数は100と同じくらい素晴らしいでしょう!)
  • パケットキャプチャライブラリ
  • パケットI / Oインターフェイスライブラリ
  • パケットデータ構造
  • スレッド間通信のブロッキングキュー
  • 乱数ジェネレータ
  • 赤黒木
  • ある種のタイマー実装

現在、異なるパケット処理アプリケーションでは、これらの異なるサブセットが必要になる場合があります。1つのソースコードリポジトリで1つのコアライブラリを実装する必要がありますか、またはこれらのモジュールのそれぞれに18の異なるリポジトリがある必要がありますか?これらのモジュールには相互依存関係がある可能性があるため、これらのモジュールのほとんどは、その他のモジュールなどに依存している可能性があります。

コアライブラリを1つ持つことが最善のアプローチであると私は主張します。多くのソースコードリポジトリのオーバーヘッドを削減します。それは依存関係の地獄を減らします:メモリアロケータの特定のバージョンは、雑多なモジュールの特定のバージョンを必要とするかもしれません。その他2.5に依存するメモリアロケータバージョン1.7と、その他2.6に依存するAVLツリーバージョン1.2が必要な場合はどうでしょうか。その他の2.5とその他の2.6を同時にプログラムにリンクできない場合があります。

したがって、先に進み、次の構造を実装します。

  • コアライブラリリポジトリ
  • プロジェクト#1リポジトリ
  • プロジェクト#2リポジトリ
  • ...
  • プロジェクト#Nリポジトリ

構造からこの種の構造への切り替えを確認しました。

  • プロジェクト#1リポジトリ
  • プロジェクト#2リポジトリ
  • ...
  • プロジェクト#Nリポジトリ

非コピーペーストメカニズムによるメンテナンスの削減とコード共有の増加につながりました。

次の構造を使用したプロジェクトも見ました。

  • メモリアロケータリポジトリ
  • アドレス解決プロトコルリポジトリ
  • AVLツリーリポジトリ
  • バイナリプロトコルリポジトリのシリアル化コード
  • 動的配列リポジトリ
  • 単一にリンクされたヘッドと二重にリンクされた中間ノードリポジトリを持つLinuxカーネルスタイルのハッシュリスト
  • ハッシュテーブルリポジトリ
  • TCP / IPヘッダー処理コードリポジトリ
  • 二重にリンクされたヘッドと二重にリンクされた中間ノードリポジトリを持つ通常のリンクリスト
  • ロギングライブラリリポジトリ
  • その他のリポジトリ(信頼してください。小さくて些細なものにはこれが必要です。そうしないと、さまざまなモジュールの数が100にもなります!)
  • パケットキャプチャライブラリリポジトリ
  • パケットI / Oインターフェイスライブラリリポジトリ
  • パケットデータ構造リポジトリ
  • スレッド間通信リポジトリのブロッキングキュー
  • 乱数ジェネレーターのリポジトリ
  • 赤黒木リポジトリ
  • ある種のタイマー実装リポジトリ
  • プロジェクト#1リポジトリ
  • プロジェクト#2リポジトリ
  • ...
  • プロジェクト#Nリポジトリ

...そして依存関係の地獄とリポジトリ数の急増は本当の問題です。

今、あなたはあなた自身のものを書く代わりに既存のオープンソースライブラリを使うべきですか?あなたは考慮する必要があります:

  • ライセンスの問題。20のライブラリには通常20人の異なる著者がいるため、提供されるドキュメントで著者にクレジットを与えるという単なる要件が多すぎる場合があります。
  • 異なるオペレーティングシステムバージョンのサポート
  • 特定のライブラリの依存関係
  • 特定のライブラリのサイズ:提供された機能には大きすぎませんか?提供する機能が多すぎませんか?
  • 静的リンクは可能ですか?動的リンクは望ましいですか?
  • ライブラリのインターフェースはあなたが望むものですか?場合によっては、希望するインターフェイスを提供するラッパーを作成する方が、コンポーネント全体を自分で書き換えるよりも簡単な場合があります。
  • ...そして私がこのリストで言及していない他の多くのこと

私は通常、プログラマーの専門知識を超えて何かを必要としない1000行未満のコードはすべて自分で実装するという規則を使用しています。注:1000行には単体テストが含まれています。したがって、単体テストで追加の10 000行が必要な場合は、自分で1000行のコードを書くことはお勧めしません。私のパケット処理プログラムの場合、これは私が使用した唯一の外部コンポーネントが以下であることを意味します:

  • 標準のLinuxディストリビューションによって提供されるものすべて。コード行が多すぎるため、Linuxを再実装しても意味がありません。Linuxの再実装の一部も、私の専門知識レベルを超えています。
  • LALR解析は私の専門知識レベルを超え、1000行を超えるコードであるため、Bison / flexです。自分で再帰的降下パーサーを作成することは確かにできますが、Bison / flexは非常に便利なので、便利だと思います。
  • 1000行を超えており、私の専門知識レベルを超えているため、Netmap
  • DPDKからのスキップリストベースのタイマー実装です。コードは1000行未満ですが、専門知識レベルを超えているためです(スキップリストを使用しない別のタイマー実装があります)。

シンプルであるために自分で実装したものには、次のようなものも含まれます。

  • MurMurHash
  • SipHash
  • メルセンヌツイスター

...これらのカスタム実装は重いインライン化を可能にし、パフォーマンスの向上につながるためです。

私は暗号化を行いません。自分で作成した場合は、ある種の暗号ライブラリをリストに追加します。自分で暗号アルゴリズムを作成すると、公式のアルゴリズムと互換性があることが徹底的なユニットテストで示されても、キャッシュタイミング攻撃の影響を受ける可能性があるためです。


1

複数のプロジェクトがコアライブラリに依存している場合、コアライブラリは不良になる可能性があります。コアへの変更をテストする必要があるだけでなく、依存するすべてのプロジェクトを回帰テストする必要もあります。次に、すべての依存プロジェクトをリファクタリングする必要があるため、コアAPIが変更されることはありません。ライブラリを使用するプロジェクトが多いほど、トラップは深くなります。

もう1つの問題は、コアライブラリに「共通」のすべてを投入し始め、それを膨らませ、小さな断片を取り込むのが難しくなる傾向があることです。たまたま、その多くのコアライブラリのいずれかに触れることが怖くなった場所を聞いたとき、QA回帰テストのオーバーヘッドが非常に大きかったと言います。

代わりに、コードスニペットリソースを作成して、プロジェクトチームが必要なコードを検索して取得し、メンテナンスやリグレッションの問題から切り離すことができるでしょうか。とにかく、私は家でそうしています。


4
複数の場所にコピーアンドペーストされたコードスニペットのバグを修正するのは、はるかに困難ですよね。
アレックスアンガス

Donald Knuthからの引用:「私はまた、再利用可能なコードのファッションに対する強い偏見を認めなければなりません。私にとって、「再編集可能なコード」は、手に触れられないブラックボックスやツールキットよりもはるかに優れています。これについて。再利用可能なコードがすばらしいと完全に確信しているなら、おそらく私はあなたを揺さぶることはできないでしょう。
Patrick Hughes

@AlexAngas:それは本当ですが、ライブラリにバグがある場合がありますが、他の一部のライブラリには最初のバグを相殺する微妙なバグがあるためにのみ正しく機能します。両方のバグセットは実用的に修正する必要がありますが、2番目のライブラリのソースコードのコピーを最初のプロジェクトの一部にすることは、そのコードに適用されたバグ修正がプロジェクトに認識可能な変更になることを意味します。破損した場合、一時的にロールバックされる可能性があります(そのため、破損の原因として特定できます)。
スーパーキャット'18

@AlexAngas:もちろん、2番目のルーチンの修正を破損の原因として特定しても、2番目のルーチンを修正するのではなく、そのルーチンの誤った動作に誤って依存しているコードがあるということを示しています。 ; その発見は、実際の問題を効率的に解決するための鍵となります。対照的に、以前は自発的に機能していたコードが機能しなくなったことを知っている場合、それについて何をすべきかを追跡することは非常に困難になります。
スーパーキャット

1

まだ言及されていない1つの点は、たとえそれが文字通り組み込みマイクロコントローラーのROMで実行されている唯一のものであっても、コードは何かに依存することになるということです。コントローラの製造元が、コードが依存する動作を変更した場合、変更後に製造されたチップで動作するようにコードを変更する必要があります。そうでない場合、コードを使用するデバイスの製造元は、何らかの方法でチップを取得する必要があります。変更を組み込んでいない-おそらく彼らに価格プレミアムを支払う。

ライブラリを使用してさまざまなハードウェア機能を実行すると、コードはライブラリに依存するようになりますが、以前はライブラリに依存していませんでしたが、コードとハードウェア間の依存関係も解消されます。たとえば、チップメーカーは、常に特定のI / O機能を特定の方法で実行する、現在および将来のすべてのチップ用のライブラリを提供することを約束する場合があります。それらのI / O関数を実行するためにそのライブラリを使用するコードは、そのライブラリの適切なバージョンを提供する製造元に依存するようになりますが、これらの機能の同じハードウェア実装を使用する製造元に依存しなくなります。

残念ながら、将来を保証するコードに対する正しいアプローチがどれであるかを知ることはしばしば困難です。変更されたチップにアクセスするために使用されていても、チップベンダーがライブラリの動作を(新しいチップに対応するために)変更するケースを見てきました。チップメーカーがハードウェアの動作を変更した場合も見ましたが、提供されたライブラリは適切に調整されたため、ライブラリルーチンを使用するコードは変更なしで機能し続け、ハードウェアに直接アクセスするコードは調整する必要がありました。

Windowsアプリケーションでも同様の状況が存在します。マイクロソフトは、アプリケーションが物事を行うために必要とされる方法を変更したい場合があります。このようなものに特定のライブラリを使用するコードは、ライブラリを更新するだけでアップグレードできますが、更新されたライブラリを使用しないコードは手動で更新する必要があります。


1

Denis de Bernardy依存関係を最小限に抑えることと冗長性を最小限に抑えることについての回答とリンクされた記事が大好きですが、私はこれに対する少し異なる見方でチップインしたいと思っていました(コードの再利用はバランスをとる行為であると私が考えるこの問題に関する私自身の考えを非常に反映しています)。

coreライブラリで私が抱えている最大の問題は次のとおりです。

いつ完成しますか?必要なすべてのことを行い、効果的に「完了」する安定点にいつ到達しますか?

そして、答えは「決して」ではない可能性が非常に高いと思います。特にこのライブラリーが、十分に期待された目標を前もって持っているのではなく、ソフトウェアの開発中に進化している場合は、そのような曖昧なアイデアをモデル化しているため、人々は常にそれに追加したくなるかもしれません。そして、ライブラリへの追加は、ライブラリへの既存の依存関係を壊さないので、世界で最悪のものではないかもしれませんが、そのような曖昧な目標を考えると、ライブラリはますます折衷的になり、醜くなり、興味のある誰かの異なる機能を提供する可能性がありますライブラリを使用しても、ニーズに適用できるライブラリのごく一部しか見つからない場合があります。

コードベースの依存関係は、理想的には非常に安定したパッケージに向かって流れる必要があります。coreあなたのコードベースの巨大な部分はそれに向かって流れるの依存関係を持っていながら、パッケージには、簡単に非常に不安定な自分自身を見つけることができます。

したがって、ライブラリをチームメイト間のより良い調整でより均一な方向に成長できるように、「人々が頻繁に必要とする可能性のあるものすべてのコアライブラリ」に特化したことに専念するより均一なライブラリにライブラリを分割する価値があると思います正確に何をすべきか、そしてもっと重要なことはすべきではないことについて、そしてそれは十分にテストされており、相対的に追加する必要があるものは何もないように感じない安定点に達する可能性があります。完全」で安定している(変化しないように)。


0

文字列やリンクされたリストのような基本的なもののためのライブラリを書くことは、この千年紀においてかなりばかげています。すでにコア機能を備えている、バッテリーに含まれるプログラミング言語を使用します。

コアランタイムサポートライブラリを単に楽しみながら書くのが好きな場合は、新しいプログラミング言語を設計してください。これをアプリケーションで行うと、本質的には言語をその側面から成長させていることになります。

その上、誰かがあなたが使っている言語ですでにN個の異なるコアライブラリを書いていないのではないですか?既存のフレームワークを調査し、最適なフレームワークを選択することは、ゼロから行うよりも時間を有効に活用できる場合があります。


私の分野では、確かに電池内蔵のプログラミング言語を使用する高性能パケット処理はオプションではありません。Cは当然の選択です。そして、いいえ、ハッシュテーブルなどに使用できるN個の異なるコアライブラリは、Linuxカーネルの実装よりも劣っています。Linuxカーネルの実装はGPLに準拠しているため、Linuxカーネルのソースコードを調べることなく、手動で同様の実装を手動で実装する必要がありますが、Linuxカーネルの実装が使用する高度なハッシュテーブル機能を知っています。ただし、これは現場で異なる場合があります。
juhist
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.