マスターブランチよりも多くのカスタマイズされたブランチを維持する


140

現在、共有リポジトリにPHPアプリケーションのマスターブランチが1つあります。当社のソフトウェアの加入者であるクライアントは500人を超えており、そのほとんどが異なる目的のために、それぞれ個別のブランチにカスタマイズされています。カスタマイズは、異なるテキストフィールド名、まったく新しい機能またはモジュール、またはデータベース内の新しいテーブル/列にすることができます。

私たちが直面する課題は、これらの数百のカスタマイズされたブランチを維持し、クライアントに配布する際に、時々新しい機能を提供し、マスターブランチを更新し、更新するためにマスターブランチの変更をカスタムブランチにプッシュすることですそれらを最新バージョンに。

残念ながら、これによりカスタムコードで多くの競合が発生することが多く、すべての競合を解決するためにすべてのブランチを何時間も費やしています。これは非常に非効率的であり、これらの競合を解決する際に間違いは珍しくありません。

クライアントブランチをマスターブランチに合わせて最新の状態に保ち、マージ中の労力を軽減するより効率的な方法を探しています。


11
「Xツールを使用できます」という答えを与えないで申し訳ありませんが、ありません。
オービットの

3
または、ビルド中(おそらくより一般的です)。ただ..完全に別々のコードベースではありません。
オービットのライトネスレース

15
@FernandoTan-あなたの目に見える症状はコードかもしれませんが、あなたの病気の根本原因はあなたの製品の断片化であり、治療はコードのクリーンアップではなく、製品フォーカス/製品機能マッピングから来る必要があります-それは最終的に起こります。私は私の答えに、より詳細にしました- programmers.stackexchange.com/a/302193/78582
アレックスS

8
これも経済的な問題になる可能性があります。500のクライアントすべてから本当にお金を稼いでいますか?そうでない場合は、価格モデルを考え直し、顧客が追加料金を支払わない場合に変更要求を拒否する必要があります。
クリスチャンストレンファー

13
これは私の心をほんの少し壊しました。幸いなことに、他の人はすでに正しい答えを叫んでいます-私の唯一の追加の推奨事項は、あなたがこれを書き、それをTheDailyWTFに提出することです。
zxq9

回答:


314

あなたはブランチを完全に乱用しています!バージョン管理の柔軟性ではなく、アプリケーションの柔軟性によってカスタマイズを行う必要があります(これは、この種の使用を目的とするものではありません)。

たとえば、テキストフィールドラベルをテキストファイルから作成し、アプリケーションにハードコーディングしないでください(これが国際化の仕組みです)。一部の顧客が異なる機能を使用している場合は、厳格で安定したAPIによって管理される厳格な内部境界を使用してアプリケーションをモジュール化し、必要に応じて機能をプラグインできるようにします。

コアインフラストラクチャおよび共有機能は、一度保存、保守、テストするだけで済みます。

これは最初から行っているはずです。既に500種類の製品のバリエーション(!)がある場合、これを修正することは大きな仕事になりますが、継続的なメンテナンス以上のことはありません。


142
「最初からこれを行うべきだった」ための+1。このレベルの技術的負債は会社を破壊する可能性があります。
デニス

31
@Daenyth:率直に言って、500のカスタムブランチがあるのに驚かされます。誰が物事をこれほど悪くするのですか?lol
オービットのライトネスレース

73
@FernandoTan私はとてもそうです、とても申し訳ありません
...-エンダーランド

20
@FernandoTan:私も。:(たぶん、インタビューでもっと質問するべきだったのですか?;)明確にするために、私の答えの「あなた」は組織です。それは抽象化です。個人を責めるつもりはありません。
軌道上の明るさのレース

58
最初に、より多くの洞察を得ます:開発者に、現在のバージョンとカスタマイズされたブランチの差分を作成させます。したがって、少なくともどのような違いがあるかを知っています。そのリストを使用すると、ブランチの最も迅速な削減に勝つことができる場所を確認できます。50にカスタムフィールド名がある場合は、そのフィールド名に焦点を当てると、50のブランチが保存されます。次に、次のものを探します。復元できないものもあるかもしれませんが、少なくともその量は少なくなり、クライアントを増やしてもそれ以上成長しません。
リュックフランケン

93

500のクライアントを持つことは素晴らしい問題です。ブランチでこの問題を回避するために時間を費やしていた場合、クライアントを獲得するのに十分な時間取引を続けることができなかったかもしれません。

まず、カスタムバージョンを維持するためのすべての費用をクライアントが十分に請求できることを願っています。クライアントは、カスタマイズを再度行うための費用を支払うことなく、新しいバージョンを取得することを期待していると思います。まず、ブランチの95%で同じファイルをすべて見つけることから始めます。その95%はアプリケーションの安定した部分です。

次に、ブランチ間で数行だけ異なるすべてのファイルを見つけます。これらの違いを削除できるように構成システムを導入してください。そのため、たとえば、テキストフィールドラベルが異なるファイルを100個持つのではなく、テキストラベルをオーバーライドできる構成ファイルが1つあります。(これは一度に行う必要はありません。クライアントが最初に変更したいときに、テキストフィールドラベルを構成可能にするだけです。)

次に、戦略パターン、依存性注入などを使用して、より難しい問題に進みます。

クライアント独自のフィールドに列を追加するのではなく、データベースにjsonを保存することを検討してください。これは、SQLでこれらのフィールドを検索する必要がない場合に役立ちます。

ファイルをブランチにチェックインするたびに、mainと差分を取り、空白を含むすべての変更を正当化する必要があります。多くの変更は不要であり、チェックイン前に削除できます。これは、1人の開発者がコードのフォーマット方法についてエディターで異なる設定をしているだけの場合があります。

最初に、多くの異なるファイルを持つ500のブランチから、少数の異なるファイルのみを持つほとんどのブランチに進むことを目指しています。まだ生きるために十分なお金を稼ぎながら。

長年に渡って500のブランチを持っているかもしれませんが、それらが管理しやすいなら、あなたは勝ちました。


br3w5のコメントに基づく:

  • クライアント間で異なる各クラスを取ることができます
  • クラスの外部から呼び出されるすべてのメソッドを定義する「xxx_baseclass」を作成します
  • xxxがxxx_clientNameと呼ばれるようにクラスの名前を変更します(xxx_baseclassのサブクラスとして)
  • クラスの正しいバージョンが各クライアントに使用されるように、依存性注入を使用します
  • そして今、賢明な洞察のためにbr3w5が思いつきました!静的コード分析ツールを使用して、現在複製されているコードを見つけ、それを基本クラスなどに移動します

簡単な穀物を得た後にのみ上記を行い、最初にいくつかのクラスでそれを追跡します。


28
実際の問題へのアプローチを提供しようとするための+1
イアン

35
あなたが答えを書いたのと同じ@Ianではないことに気付くまで、あなたが自分の答えを祝福してくれたことを本当に心配していました。
セロンルー

2
おそらく、静的コード分析ツールを使用して、コードのどの部分を複製するかを絞り込む必要があります(同じファイルをすべて特定した後)
br3w5

1
また、クライアントは、コードのどのバージョンを持っているチームのトラックを助けるために、バージョンのパッケージを作成
br3w5

1
「ちょうどあなたのコードをリファクタリングする」という長い風変わりな方法のように聞こえます
ローランドテップ

40

将来的には、インタビューでジョエルテストの質問をしてください。あなたは列車の難破に歩いていない可能性が高くなります。


これは、ああ、どういうことか...本当に、本当に悪い問題だ。この技術的負債の「金利」は非常に高くなるでしょう。回復できない可能性があります...

これらのカスタム変更は「コア」とどの程度統合されていますか?それらを独自のライブラリにし、単一の「コア」を持ち、特定の各顧客が独自の「アドオン」を持つことができますか?

または、これらはすべて非常にマイナーな構成ですか?

ソリューションは次の組み合わせだと思います:

  • ハードコーディングされたすべての変更を構成ベースのアイテムに変更します。この場合、誰もが同じコアアプリケーションを持っていますが、ユーザー(またはあなた)は必要に応じて機能のオン/オフ、名前の設定などを行います。
  • 「クライアント固有の」機能/モジュールを別のプロジェクトに移動します。そのため、1つの「プロジェクト」の代わりに、簡単に追加/削除できるモジュールを備えた1つの「コアプロジェクト」があります。または、これらの構成オプションも作成できます。

500以上のクライアントでここにたどり着いたように、どちらも些細なことではありません。これを分離する際の変更は、非常に時間のかかる作業になると思います。

また、すべてのクライアント固有のコードを簡単に分離して分類する際に重大な問題が発生するのではないかと思います。

変更の大部分が具体的な言い回しの違いである場合は、言語のローカライズに関するこのような質問を読むことをお勧めします。複数の言語を完全に実行する場合も、サブセットのみを実行する場合も、ソリューションは同じです。これは特にPHPとローカライズです。


1
また、これは(控えめに言っても)巨大なタスクになるため、この問題に多大な時間とお金を投じるようにマネージャーを説得することさえも、大きな課題となります。@FernandoTanこのサイトには、この特定の問題に役立つ質問と回答がある場合があります。
ラドゥマーゼ

10
ジョエルテストのどの質問が、会社が支店を悪用していることを伝えたでしょうか?
SpaceTrucker

2
@SpaceTrucker:さて、「毎日ビルドをしますか?」助けたかもしれない。500のブランチでは、おそらくブランチを持っていなかったか、一部のブランチでのみ行うと述べていたかもしれません。
sleske

17

これは、VCSでヒットする最悪のアンチパターンの1つです。

ここでの正しいアプローチは、カスタムコードを構成によって駆動されるものに変えることです。その後、各顧客は、構成ファイル、データベース、またはその他の場所にハードコーディングされた独自の構成を持つことができます。機能全体を有効または無効にしたり、応答の外観をカスタマイズしたりできます。

これにより、1つのマスターブランチを運用コードに保持できます。


3
これを行う場合は、自分自身を支持し、可能な限り戦略パターンを使用するようにしてください。これにより、単にif(getFeature(FEATURE_X).isEnabled())全体を軽視するよりも、コードの保守がずっと簡単になります。
TMN

13

ブランチの目的は、メインブランチの安定性を損なう危険を冒すことなく、開発の1つの可能性を探ることです。最終的には適切なタイミングでマージするか、行き止まりになる場合は破棄する必要があります。あなたが持っているのはそれほど多くのブランチではなく、同じプロジェクトの500個のフォークであり、それらのすべてに重要なチェンジセットを適用しようとすることは非常に難しいタスクです。

代わりに行うべきことは、コアコードを独自のリポジトリに配置し、構成によって動作を変更し、逆依存関係によって許可される動作を注入するために必要なエントリポイントを用意することです。

クライアントのさまざまなセットアップは、外部で構成された状態(データベースなど)によって互いに区別するか、必要に応じてコアをサブモジュールとして追加する個別のリポジトリとしてライブできます。


6
メンテナンスブランチを忘れてしまいました。これは基本的に、回答で説明したブランチの反対です。:)
オービット

7

すべての重要なことは、ここで良い回答によって提案されています。プロセスの提案として5ペンスを追加したいと思います。

長期または中期の範囲でこの問題を解決し、ポリシー、つまりコードの開発方法を採用することをお勧めします。柔軟な学習チームになるようにしてください。誰かがソフトウェアを構成可能にする代わりに500のリポジトリを許可した場合は、今までどのように作業してきたのかを自問する時が来ました。

つまり:

  1. 変更管理の責任を明確にする:顧客が何らかの適応を必要とする場合、がそれらを販売し、が許可し、がコードを変更するか決定しますか?何かを変更する必要がある場合、ネジはどこに回すのですか?
  2. 役割、チーム内のが新しいリポジトリを作成することを許可されているか、が許可されていないかを明確します。
  3. チームの全員がソフトウェアに柔軟性をもたらすパターンの必要性を認識していることを確認してください。
  4. 管理ツールの明確化:どの顧客がどのコードを採用しているかをどのようにしてすばやく知ることができますか。いくつかの「500のリスト」は迷惑に聞こえますが、必要に応じて「感情的な経済」をいくつか示します。顧客の変化を短時間で伝えることができない場合、リストを開始する必要があるかのように、さらに失われ、描かれていると感じます。次に、そのリストを使用して、他の人の回答がここに示した方法で機能をグループ化します。
    • マイナー/メジャー変更による顧客のグループ化
    • サブジェクト関連の変更によるグループ化
    • マージしやすい変更とマージしにくい変更でグループ化する
    • いくつかのレポジトリに対して行われた同等の変更のグループを見つけます(そうそう、いくつかあります)。
    • 多分あなたのマネージャー/投資家と話をするために最も重要な:高価な変更と安い変更によるグループ化。

これは、チームに悪いプレッシャーをかけることを意味するものではありません。むしろ、まずこれらの点を自分自身で明確にし、サポートを感じる場合はどこでも、チームとともにこれを整理することをお勧めします。すべての体験を向上させるために、テーブルに親切な人を招待してください。

次に、このフレームを小さな炎で調理する長期的な時間枠を確立してください。提案:毎週少なくとも2つのリポジトリをマージして、少なくとも1つのリポジトリを削除してください。定期的に監視することで、2つ以上のブランチをマージできることを頻繁に学ぶかもしれません。そうすれば、1年で最悪の(最も高価な?)ブランチに対処でき、2年でこの問題を軽減してより優れたソフトウェアを実現できます。しかし、最終的には誰もこれに「時間を割く」ことはないので、これ以上期待しないでください。しかし、あなたはあなたがソフトウェアアーキテクトであるのでこれをもう許さないでしょう。

私があなたの立場にあった場合、これは私がそれを処理しようとする方法です。しかし、私はあなたのチームがそのようなことをどのように受け入れようとしているのか、ソフトウェアがこれを本当に許しているのか、あなたがどのようにサポートされているのか、そしてあなたがまだ学ばなければならないことも知りません。あなたはソフトウェアアーキテクトです-ただそれのために行きます:-)


2
技術的な問題の背後に潜んでいる社会的/組織的問題の解決についての良い点。これは見過ごされがちです。
sleske

5

すべての反対意見とは対照的に、実際のビジネスニーズを想定しましょう。

(たとえば、成果物はソースコードであり、顧客は同じ業種であり、したがって競合相手同士であり、ビジネスモデルはその秘密を秘密にすることを約束します)

さらに、のは(のは言わせて、すべての枝を維持するためのツールを持っている会社が、それはマンパワーのどちらかであると仮定しましょう100の開発者は 5日放出の遅延を想定し、合併に捧げ;または仮定10の開発者50日レリーズラグが OKである)、または自動マージがすべてのブランチのコア仕様拡張仕様の両方に対して真にテストされるような素晴らしい自動テスト。したがって、「きれいに」マージされない変更のみが人間の介入を必要とします。顧客がカスタマイズだけでなくその保守に対しても支払いを行う場合、これは有効なビジネスモデルになる可能性があります。

私(および反対意見)の質問は、各顧客への配達を担当する専任の担当がいますか?たとえば、1万人の会社の場合は、そうかもしれません。

これは、プラグインアーキテクチャによって処理される場合があります。たとえば、コアがトランクであり、プラグインがトランクまたはブランチに保持され、各カスタマーの構成が一意の名前のファイルであるか、カスタマーブランチに保持されているとします。

プラグインは、実行時にロードすることも、コンパイル時に組み込むこともできます。

本当に多くのプロジェクトがこのように行われ、根本的に同じ問題が依然として適用されます-単純なコアの変更は統合するのは簡単で、競合の変更はロールバックされるか、多くのプラグインに変更が必要です

プラグインが十分ではない場合があります。プラグインインターフェイスの数が多すぎて処理できないほどコアの内部を多く調整する必要がある場合です。

理想的には、これはトランクがコアコードであり、ブランチがアスペクト(エクストラをコアに接続するための追加のコードと指示)であるアスペクト指向プログラミングによって処理されます。

簡単な例として、fooコアの前または後にカスタムを実行するklass.fooか、それを置き換えるか、またはラップして入力または出力を変更できるように指定できます。

そのためには多くのライブラリがありますが、マージの競合の問題はなくなりません。クリーンなマージはAOPによって処理され、競合は依然として人間の介入を必要とします。

最後に、このようなビジネスは本当にブランチメンテナンスに関与する必要があります。つまり、顧客固有の機能Xが非常に一般的であるため、すべての顧客が料金を支払っていなくても、コアに移行する方が安価ですか?


3

症状を見て病気の根本原因を解決しているわけではありません。「コード管理」アプローチの使用は対症的ですが、長期的には問題を解決できません。根本的な原因は、「適切に管理された」製品の機能、機能、拡張機能、バリエーションの不足です。

あなたの「カスタム」のコードは、の拡張子は何も示していない製品の機能および機能データフィールド他人の変更を。

製品のコードベースを「サニタイズ」するのに、カスタム機能がどれだけ広範囲に、どのように異なり、どのように文脈的に類似しているか、またはそうでないかが大きく影響します。

コーディングやバージョン管理の方法だけでなく、製品管理、製品アーキテクチャ、データアーキテクチャが関係する場所でもあります。真剣に。

なぜなら、結局のところ、コードは、クライアントへのビジネスおよび製品の機能/サービスの提供に他ならないからです。それはあなたの会社が支払われているものです。

これをより適切に処理するには、コードの観点ではなく、「機能」の観点から行う必要があります。

あなた、あなたの会社、そして製品はすべての人にとってすべてではありません。500のクライアントというまともな収益基盤ができたので、今度は自分が意図するものを製品化するときが来ました。

また、複数の製品を提供している場合、製品の機能を体系的にモジュール化することは理にかなっています。

あなたの製品はどれくらい広く、深く行きますか?さもなければ、これは「サービス品質」の問題と「製品の希釈と断片化」につながります。

あなたは次のようになりますCRMERPや注文処理/ディスパッチまたはMicrosoft Excelの?

既存の拡張機能は、大規模なソフトウェアの主要な、ロールアップと調和させる方法を必要引っ張る内合流し、スタートアップから取得した製品を。

強力な製品管理とデータアーキテクチャの担当者が以下をマップする必要があります。

  • マスターブランチ、その製品機能、および機能ベース
  • カスタム拡張機能、タイプ、およびバリエーション
  • 「カスタムフィールド」の重要性とバリエーション

..コアアプリケーションの壮大なコンテキストで、これらすべての緩やかな製品スレッド/ブランチの同化と調和のロードマップを作成する。

PS:私とつながってください、私はあなたがこれを修正するのを助けることができる人を知っています:)


-5

これに関連することができます。私は多くのプロジェクトを引き受けました。実際、開発作業の90%がそのような問題を修正しています。すべての人が完璧というわけではないので、バージョン管理を正しい方法と場所で使用することをお勧めします。可能な場合は、次の操作を実行できます。

  • これから、顧客が更新を要求したら、それらを新しい分岐リポジトリに移動します。
  • それらをマスターにマージする場合は、最初にそれを実行し、競合を解決します。
  • 次に、リポジトリで問題とスプリントを管理し、マスターで起動したいものをマスターに保持します。これにより、リリースサイクルにより多くの負荷がかかる可能性がありますが、時間の節約になります。
  • 新しい顧客のためにメインリポジトリのマスターブランチを維持します。メインリポジトリには、将来の作業のために作業しているブランチのみを含める必要があります。レガシーブランチは、顧客のリポジトリに移行したら削除できます。

40のブランチを持つGitHubからリポジトリをBitbucketに個人的にインポートし、40のリポジトリを作成しました。わずか4時間でした。これはWordPressテーマのバリエーションであったため、プッシュとプルは迅速でした。

「最初に正しくやらない」には多くの理由があり、それらをすぐに受け入れて「今回は正しくやる」に進む人は常に成功すると思います。


16
複数のリポジトリを使用すると、どのようにメンテナンスが簡単になりますか?
数学

私たちのようないくつかのケースでは、顧客は各レポにアクセスし、カスタマイズされたソリューションになったときに自分の問題を管理する必要があるため、独自のレポを持っているので管理が簡単になります。多くの場合、機能しない場合があります。
ファルークスバニ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.