## Title

持続可能な成長のためのエクステンション

## Patlet  

インナーソースプロジェクトは多くのコントリビューションを受けており、メンテナンスが難しくなっています。メンテナは、プロジェクトのコア部分から離れた拡張機構を提供することで、最小のコストとメンテナンスオーバーヘッドでプロジェクトの能力をスケールアップすることを可能にします。

## 問題

成熟したインナーソースリポジトリへのコントリビューションが急速に増えると、コードレビューやメンテナンスの負担が増大します。これにより、大量のコードレビューのバックログが生じたり、新しい機能のコントリビューションが早期に拒否されたりする結果となります。

ホストチームは、新しい機能のリリースをより早く行い、イノベーションと実験を奨励しつつ、リポジトリを適切にメンテナンスするためにはどのようにすればよいのでしょうか？

## ストーリー

特定のドメインスペース内での最高のイノベーションを一つの共通スタックに集め、共通のインフラストラクチャの再利用を可能にし、標準的なユーザーエクスペリエンスを提供することを目指す戦略的なプロジェクトが存在します。インナーソースを通じて、組織内の各チームは、そのドメインスペース内で働きながら、共通のコードベースにイノベーションをコントリビューションする機会を得ます。

しかし、複数の開発者からの並行したコントリビューションが多くなると、コードベースのメンテナンスが難しくなります。
それは、プロジェクトのメンテナはコードの品質基準に対するオーナーシップを引き受けることになり、さまざまな形式のコミュニケーションを通じてコミュニティを支えるという大きな負担がかかるからです。

プロジェクトのメンテナは以下のようなリスクに直面しています:

- コントリビューターからのプルリクエストのバックログが絶えず存在する
- 職業の不満: メンテナの時間の大部分がコミュニティサポートの支援に使われてしまい、新しいことに挑戦する時間がない
- 成果感の欠如: 提供されたすべての機能が適切なユーザー需要を満たすわけではなく、それによって結果的に採用されるとは限らない
- リリースに時間がかかる: コードベースに多くの機能があると、テストに時間がかかる
- メンテナンス活動の増加: 新しい機能が追加されると、バグが増える

可能性のあるユーザーがその機能を自分のユースケースに適用する機会を得る前に、新しい機能のコントリビューションが成熟するまでに多くの時間が費やされています。もし新しい機能がユースケースを満たさなければ、求められていたコード品質基準を達成するために費やした全ての時間が無駄になります。

## 状況

- 戦略的なインナーソースコードベースが、多数の従業員からの新機能のコントリビューションで急速にスケールアップしています。
- レビュアーとコントリビューションの比率により、プルリクエストのバックログが増加しています。これにより、新機能のリリースがコミュニティに対して遅くなっています。
- コードベースの品質が劣化し、ユーザーエクスペリエンスが悪化しています。
- コードベースのメンテナが負荷を感じ、コントリビューションの増加とコミュニティサポートの増加に対応できなくなっています。
- 提供された機能の一部はユーザーに採用されず、完全に不活動になる可能性があります。しかし、それらが使用されていないにもかかわらず、これらの機能は依然としてメンテナンスのオーバーヘッドを増やしています。
- 組織は、新しい機能のコントリビューションを品質基準を保持するために厳しく対応している一方で、コミュニティがアイデアを探求する前に大量の投資を行っています。
- 次の2つのシナリオのどちらかでパターンが適用されます:
   - メンテナがプロジェクトの範囲を絞り込むために、自身で新しい機能のアイデアを拒否することにより、コミュニティのイノベーションを阻害し、さらなる拡大を制限しています。
   - バックログを減らすために、新しい機能が十分なドキュメンテーション、ハードニング、テストなしにリリースされ、ユーザー体験が悪化します。これにより、コードベースのサイズが大きくなり、依存関係が大きくなり、メンテナンスが難しくなります。

## 組織に働く力学

- メンテナとプロダクトオーナーは、拡張、イノベーション、実験を促進しつつ、コントリビューションに対して過度に制限を設けずに、良好なコードと品質基準を保持したいと考えています。
- 特性を製品基準に適合させ、徹底的にテストするために多くの時間が費やされますが、製品オーナーは新しいイノベーションを機能が成熟する前に製品が探求できるように、新しいイノベーションのリリースを早めたいかもしれません。
- メンテナは、プライマリリポジトリにより多くの依存関係を追加せずに、コミュニティが製品の機能と他のユースケースを組み合わせたイノベーションを共有することを奨励したいと考えています。

## ソリューション

高規模なインナーソースコードベースへの[拡張/プラグイン(英語)](https://en.wikipedia.org/wiki/Extensibility)を許可することで、リポジトリのメンテナに対するメンテナンスの負担を軽減し、新機能のリリースを探求する製品のリリースを早めることができます。これにより、機能のメンテナンスを拡張所有者に移し、プライマリリポジトリがより広範に採用され、より戦略的にサポートされる機能を支援することが可能になります。

拡張機能は、最終的にプロジェクトのコアに移動する可能性のある新たな機能のフィルターとして機能します。また、拡張機能はインキュベーションとコミュニティのハードニング環境としても機能し、そのハードニングが高価なレビュープロセスではなく、自然に行われるようにします。

拡張モデルを成功させるためには、いくつかのアーキテクチャ上の考慮事項が必要です:

1. **作成が簡単:** コミュニティの参加を得るためには、拡張機能を簡単に作成できる必要があります。
   - 拡張機能がスタートポイントとして使用するべきリポジトリテンプレートを作成します。これにより、拡張機能は新しいリポジトリに新機能を追加することができ、コアプロジェクトとは別になります。テンプレートはプライマリリポジトリと同じモジュール構造を提供し、拡張機能のパッケージ化とリリースのフレームワークを含むべきです。
      - プライマリリポジトリが変更されると、テンプレートが適切に保守されていることを確認します。プライマリリポジトリのメンテナがテンプレートを更新し、メインプロジェクトとの互換性を保証します。良いバージョニング規則を守ること、例えば、[semver](https://semver.org/)の使用がこれを容易にします。
      - さらに、新しいバージョンがリリースされると、プライマリリポジトリのメンテナが古いバージョンのテンプレートに基づく拡張機能の更新方法についてのガイドラインを提供することを推奨します。
   - テンプレートから開発された拡張機能の例を追加します。これにより、プロジェクトの開発者は、どのようにして良いパターンの拡張機能を書くべきかを理解することができます。
   - レビューをバイパスして拡張機能を作成する寄稿者の要件を緩和し、リリースや実験を早めることを許可します。
2. **疎結合:** 機能を含むモジュラーなコンポーネントを持つことで、拡張機能への変更がメインコードベースや他の拡張機能の品質に影響を与えない疎結合を可能にすることができます。
3. **依存性管理:** 各拡張は、それが構築されるプライマリリポジトリのバージョン範囲をピン留めするように注意する必要があります（他の依存関係と同じように）。また、それがプライマリリポジトリの依存関係をシャドーする他の依存関係の使用についても注意深くなければなりません。選択した依存関係のバージョンがプライマリリポジトリの選択したバージョンと互換性があるようにします。プライマリリポジトリとの任意の競合は、拡張のテストフレームワークで検出できます。
4. **テスト戦略:** 拡張機能を個別に、または組み合わせてテストする方法は？
   - **拡張機能を個別にテストする:** 拡張機能のテンプレートは、拡張機能の開発者が追加した機能をテストするためのテストフレームワークを提供します。これには、ユニットテスト、ランタイムパフォーマンステスト、品質テストのフレームワークが含まれることがあります。
   - **拡張機能をプライマリリポジトリと組み合わせてテストする:** 拡張機能の開発者は、プライマリリポジトリの特定のバージョンに対して自分の拡張機能をテストするための良好なパターンの方法を持つべきで、これにはプライマリリポジトリのメンテナの関与は必要ありません。
   - **拡張機能を他の拡張機能と組み合わせてテストする:** このシナリオのためにテストフレームワークを提供することは、特に、ユーザによってまだ調査中であり、すべての拡張機能を組み合わせて使用する可能性が低い多数の拡張機能がある場合、過剰になる可能性があります。もしユーザが拡張機能を組み合わせて使っているときにコンフリクトに遭遇した場合 (十分な疎結合であればありえないはずです)、ユーザはそれぞれの拡張機能の所有者に問題を提起することができます。拡張機能がライフサイクルの後半に達し、プライマリリポジトリにマージされるようになると、ライブラリの残りの部分と組み合わせてテストされるようになり、依存関係の衝突はその時点で解決されなければならなくなります。
5. **発見性と利便性:**
   - ユーザーが作成し、製品の使用に共有したいと思っている拡張機能を表示する公開ページで、拡張機能を容易に発見できるようにします。
   - ユーザーが元のプロジェクトと一緒に拡張機能を利用できるように、プライマリプロジェクトに拡張機能の登録を許可します。これにより、同じユーザーエクスペリエンスが維持されます。
6. **拡張機能のライフサイクルと保守性:** 拡張機能の作成からプライマリコードベースへの移植までのライフサイクルを確立し、明確な所有権ガイドラインを作成します。
   - 拡張機能の作成者は、拡張機能のメンテナンスを続け、サポートを提供し、欠陥を修正します。保守されていない拡張機能は、公開ページからリストから削除されます。
   - 内部製品による拡張機能の採用と機能への需要など、プライマリリポジトリへの移植が可能になる基準を作成します。
   - 拡張機能のプライマリリポジトリへの移植プロセスは、ライブラリメンテナによって設定されたより厳格なコードレビューガイドラインに従います。

![ソフトウェアアーキテクチャと拡張機能](../../../assets/img/extensions-for-sustainable-growth/extensions-for-sustainable-growth.png)

これらの原則に従うことで、以下が確保されます:

- 開発者は、大量の[ボイラーテンプレート(定型)](https://en.wikipedia.org/wiki/Boilerplate_code)コードを書くことなく、プロジェクトのエコシステムに新しい機能を追加することができます。
- 拡張機能は、プライマリプロジェクトの全てのユーザーによって反復可能な方法で発見できます。コードがまだメインリポジトリに存在していないとしても、それが価値がないわけではありません。
- メンテナの負担は、拡張機能がプライマリプロジェクトで重要なギャップを埋めていることを示すまで軽減されます。
- コアプロジェクトの共通コード（例えば、ベースクラスやユーティリティ関数）は、プロジェクトの領域を拡張する新しい開発の出発点となります。これにより、革新的な作業を後から移植する必要がなくなり、プロジェクトにとって新しい機能を開発する全体的な負担を軽減します。
- 開発者は、コードベースのためのコミュニティのメンテナンスとコミュニティ構築にコントリビューションし、関与し続ける可能性が高くなります。これは全体のプロジェクトエコシステムの健康にも良い影響を与えます。

## 結果の状況

- プロジェクトは、新しい機能の追加により拡大することができ、プライマリプロジェクトリポジトリにメンテナンスのオーバーヘッドを追加することなく、拡大することができます。
- コミュニティが探求する新機能のリリースが早まり、革新と実験を奨励します。
- 機能がその有用性を証明するまで、高額なコードレビューと機能強化プロセスが削減されます。これにより、組織に対するコスト節約の利益が得られます。
- 一つの問題が導入される可能性があります - 拡張機能がフルライフサイクルを完了できない場合、何が起こりますか？
   - 拡張機能が一定期間採用されず、それをサポートするコミュニティを形成できなかった場合、そのメンテナンスを続けるかどうかは拡張機能のオーナー次第です。もし拡張機能がメンテナンスされなくなった場合、それは非公開になります。
   - 拡張機能の開発者がそれ以上のプロジェクトを維持できなくなり、コミュニティ内の他の開発者がそれを引き続きサポートしたい場合、彼らはその拡張機能の維持を進めていくことができます。

## 事例

* **IBM社**は、[インナーソース AIライブラリ](https://youtu.be/Lz-tIc2cyRM)を拡大するためにこの解決策を採用しています。拡張機能を使用することで、開発者はAIライブラリに更に多くのアルゴリズムを追加し、彼らの革新を企業内のコミュニティと共有することができます。コアライブラリには、採用されて検証された戦略的なアルゴリズムのみが含まれており、これにより我々はコントリビューションをスケールするにつれてそれらをより簡単に維持することができます。

## エイリアス

大規模にコントリビューションを管理するための拡張

## ステータス

* Structured

## 著者

- Sukriti Sharma, IBM
- Alexander Brooks, IBM
- Gabe Goodhart, IBM
