少し背景を説明します。私はおよそ12人のRuby on Rails開発者(+/-インターン))の会社で働いています。リモートでの作業が一般的です。製品は2つの部分で構成されています。かなり太いコアと、それに基づいて構築された大規模な顧客プロジェクトまでです。顧客プロジェクトは通常、コアを拡張します。主要な機能の上書きは行われません。コアにはかなり悪い部分があると付け加えるかもしれません仕様はありますが、ほとんどはお客様のプロジェクト用です。コアの最悪の部分はテストされていません(本来あるべきではありません...)。
開発者は2つのチームに分かれ、各スプリントに対して1つまたは2つのPOを使用します。通常、1つの顧客プロジェクトは、チームとPOの1つに厳密に関連付けられています。
今私たちの問題:かなり頻繁に、私たちはお互いのものを壊します。チームAの誰かがコア機能Yを拡張またはリファクタリングすると、チームBの顧客プロジェクトの1つで予期しないエラーが発生します。ほとんどの場合、変更はチーム全体で発表されていないため、ほとんどの場合、予期しないバグが発生します。 POを含むチームBは、機能Yは安定していると考え、変更を認識せずにリリース前にテストしませんでした。
それらの問題を取り除く方法は?どんな「告知テクニック」を勧められますか?
私は Michael C. Feathersによるレガシーコードで効果的に動作する を読むことをお勧めします。それは本当に自動化されたテストが本当に必要であること、それらをまだ持っていない場合に簡単に追加する方法、そしてどのようにリファクタリングする「コードの匂い」かを説明しています。
その上、あなたの状況におけるもう1つの中心的な問題は、2つのチーム間のコミュニケーションの欠如のようです。これらのチームの大きさは?彼らは異なるバックログに取り組んでいますか?
アーキテクチャに応じてチームを分割することは、ほとんどの場合悪い習慣です。例えば。コアチームと非コアチーム。代わりに、機能ドメインでチームを作成しますが、クロスコンポーネントです。
コアの最悪の部分はテストされていません(あるはずです...)。
これが問題です。効率的なリファクタリングは、一連の自動テストに大きく依存します。それらがない場合、あなたが説明している問題が現れ始めます。これは、Rubyなどの動的言語を使用する場合に特に重要です。Rubyのように、パラメーターをメソッドに渡すことに関する基本的なエラーをキャッチするコンパイラーはありません。
ユニットテストの改善を示す以前の回答は良いですが、対処すべきより根本的な問題があるかもしれないと思います。顧客プロジェクトのコードからコアコードにアクセスするには、明確なインターフェイスが必要です。このようにして、コアコードをリファクタリングした場合インターフェイスを通じて観察される動作を変更せずにであれば、他のチームのコードは壊れません。これにより、「安全に」リファクタリングできるもの、およびおそらくインターフェースを壊すような再設計が必要なものを簡単に知ることができます。
他の回答では重要なポイント(単体テスト、機能チーム、コアコンポーネントへの明確なインターフェイス)が強調されていますが、欠けているのはバージョン管理です。
リリースしてコアの動作をフリーズした場合1 そのリリースをプライベートアーティファクト管理システムに入れます2、その後、任意の顧客プロジェクトがコアバージョンへの依存関係[〜#〜] x [〜#〜]を宣言でき、次のリリースX + 1。
その後、「発表方針」は、リリースごとにCHANGESファイルを用意するか、新しいコアリリースのすべての機能を発表するためにチーム会議を開催することになります。
また、「コア」とは何か、そしてその「サブセット」とは何であるかをより明確に定義する必要があると思います。 「正しく」「主要コンポーネント」に多くの変更を加えないように見えますが、「コア」に頻繁な変更を許可しています。何かに依存するには、それを安定に保つ必要があります。何かが安定していない場合は、コアと呼ばないでください。多分私はそれを「ヘルパー」コンポーネントと呼ぶことを提案できますか?
[〜#〜] edit [〜#〜]: Semantic Versioning System の規則に従う場合、コアのAPIの互換性のない変更は、メジャーバージョンの変更でマークする必要があります。つまり、単に新しいものを追加するだけでなく、既存のコアの動作を変更したり、何かを削除したりする場合です。この規則により、開発者はバージョン「1.1」から「1.2」へのアップデートは安全ですが、「1.X」から「2.0」へのアップグレードは危険であり、慎重に検討する必要があります。
1:Rubyの世界では、これは宝石と呼ばれています
2:JavaまたはPythonのPyPIでのNexusに相当)
他の人が言ったように、ユニットテストの優れたスイートは問題を解決しません。各チームのテストスイートに合格しても、変更をマージするときに問題が発生します。
TDDについても同様です。どうすればこれを解決できるのかわかりません。
あなたのソリューションは非技術的です。 「コア」の境界を明確に定義し、「開発者」またはアーキテクトであれ、誰かに「ウォッチドッグ」の役割を割り当てる必要があります。コアへの変更はすべて、このウォッチドッグを通過する必要があります。彼は、すべてのチームからのすべての出力が余計な損害なしにマージされることを確認する責任があります。
より長期的な修正として、チーム間のより適切でタイムリーなコミュニケーションも必要です。たとえば、コア機能Yなどを使用する各チームは、機能の計画されたテストケースの構築に関与する必要があります。この計画は、それ自体で、2つのチーム間の機能Yに固有のさまざまな使用例を明らかにします。機能がどのように機能するかが明確になり、テストケースが実装されて合意されると、必要な実装スキームに追加の変更があります。機能をリリースするチームは、それを使用しようとしているチームではなく、テストケースを実行するために必要です。衝突が発生するタスクがある場合、それはどちらかのチームから新しいテストケースを追加することです。チームメンバーは、テストされていない機能の新しい側面を考えた場合、自分のサンドボックスに渡されることを確認したテストケースを自由に追加できます。このようにして、発生する唯一の衝突は意図レベルであり、リファクタリングされた機能が実際にリリースされる前に釘付けにする必要があります。
すべてのシステムには効果的なテストスイート(とりわけ自動化)が必要ですが、これらのテストを効果的に使用すると、これらの競合が現在よりも早く検出されますが、根本的な問題には対処できません。
質問は、少なくとも2つの根本的な問題を明らかにします。個々の顧客の要件を満たすために「コア」を変更する習慣と、チームがコミュニケーションを取り、変更の意図を調整できないことです。これらはどちらも根本的な原因ではなく、修正する前にこれがなぜ行われているのかを理解する必要があります。
最初に決定することの1つは、開発者とマネージャの両方がここに問題があることを認識しているかどうかです。少なくとも一部がそうであるならば、あなたは彼らがそれについて何もすることができないと彼らが考えているのか、そうしないことを選んだのかを知る必要があります。そうでない人のために、あなたは彼らの現在の行動が将来の問題をどのように作成するかもしれないかを予想する彼らの能力を増すことを試みるかもしれません、またはそれらをできる人で置き換えるかもしれません。どのように問題が発生しているのかを認識している従業員がいるまでは、問題を解決することはできません(少なくとも短期的には、それでも解決できない可能性があります)。
少なくとも最初は、問題を抽象的な用語で分析するのは難しい場合があるため、問題の原因となった特定のインシデントに焦点を当て、それがどのように発生したかを判断してください。関係する人々は防御的である可能性が高いので、実際に何が起こっているのかを見つけるために、自己奉仕と事後の正当化について警戒する必要があります。
可能性は非常に低いため、言及するのをためらうことが1つあります。顧客の要件が非常に異なるため、共有コアコードを正当化するための共通性が不十分です。その場合は、実際には複数の個別の製品があり、それらをそのように管理し、それらの間に人工的な結合を作成しないでください。
主に、通信の問題があります(おそらくチームビルディング問題)なので、あなたのケースの解決策は、開発手法ではなくコミュニケーションに焦点を当てるべきだと思います。
私は当然のことながら、顧客プロジェクトの開始時にコアモジュールをフリーズまたはフォークすることはできません(それ以外の場合は、コアモジュールの更新を目的とする顧客関連以外のプロジェクトを会社のスケジュールに統合する必要があります)。
したがって、チーム間のコミュニケーションを改善しようとする問題が残ります。これには2つの方法で対処できます。
通信プロセスとしてのCIの詳細については こちら を参照してください。
最後に、会社レベルでのチームワークの欠如に問題があります。私はチームビルディングイベントの大ファンではありませんが、これはそれらが役立つケースのようです。定期的に開発者全体の会議がありますか?他のチームの人々をプロジェクトの回顧展に招待できますか?それとも、金曜日の夜にビールを飲むことがありますか?
私たちは皆、単体テストが進むべき道であることを知っています。しかし、これらをコアに現実的にレトロフィットすることは難しいことも知っています。
機能を拡張するときに役立つ可能性がある特定の手法は、既存の機能が変更されていないことを一時的かつローカルに確認することです。これは次のように行うことができます:
元の疑似コード:
def someFunction
do original stuff
return result
end
一時的なインプレーステストコード:
def someFunctionNew
new do stuff
return result
end
def someFunctionOld
do original stuff
return result
end
def someFunction
oldResult = someFunctionOld
newResult = someFunctionNew
check oldResult = newResult
return newResult
end
存在するすべてのシステムレベルのテストでこのバージョンを実行します。すべてが問題なければ、問題が発生していないことがわかり、古いコードの削除に進むことができます。古い結果と新しい結果の一致を確認する場合は、バグ修正などの意図的な変更のため、わかっているケースをキャプチャするために違いを分析するコードを追加することもできますshouldは異なることに注意してください。
"ほとんどの場合、変更はチーム全体で発表されることはないため、ほとんどの場合予期しないバグが発生します"
通信に問題がありますか? (他の人がすでに指摘していることに加えて、厳密なテストが必要であることを確認して)適切なコミュニケーションがあることを確認することについてはどうですか?人々が彼らが書いているインターフェースが次のリリースで変更されることを知っていること、そしてそれらの変更はどうなるのでしょうか?
そして、開発中にできるだけ早く(空の実装で)少なくともダミーのインターフェースへのアクセスを提供して、彼らが独自のコードを書き始めることができるようにします。
それがなければ、ユニットテストは、最終段階でシステムの一部の間に何か問題があることを指摘することを除いて、あまり機能しません。あなたはそれを知りたいが、それを早く、非常に早く知りたい、そしてチームに互いに話し合い、努力を調整し、実際に他のチームがしている仕事に頻繁にアクセスできるようにしたいので(定期的なコミットではなく、大規模なコミットではない)数週間または数か月後に、配信の1〜2日前にコミットします)。
あなたのバグはコードに含まれていません。確かに、自分が書いているインターフェイスをいじっていることを知らなかった他のチームのコードには含まれていません。あなたのバグはあなたの開発プロセス、人々の間のコミュニケーションとコラボレーションの欠如にあります。別の部屋に座っているからといって、他の人から自分を隔離する必要があるわけではありません。