大きなファイルのリファクタリングを処理する最良の方法は何ですか?


41

現在、残念ながらソフトウェア品質ガイドラインが常に守られているわけではないファイルを含む、より大きなプロジェクトに取り組んでいます。これには、明らかに複数の異なる機能を含む大きなファイル(2000〜4000行を読む)が含まれます。

次に、これらの大きなファイルを複数の小さなファイルにリファクタリングします。問題は、彼らがとても大きいので、異なるブランチの複数の人々(私を含む)がこれらのファイルで作業していることです。ですから、これらのリファクタリングを他の人々の変更とマージすることが難しくなるので、私は実際に開発とリファクタリングから分岐することはできません。

もちろん、全員がマージしてファイルを開発し、ファイルを「フリーズ」し(つまり、誰もファイルを編集できないようにする)、リファクタリングしてから「フリーズ解除」する必要があります。しかし、リファクタリングが完了するまでこれらのファイルに対する作業を基本的にすべて停止する必要があるため、これもあまり良くありません。

だから、リファクタリングする方法がありますか、他の誰かに作業を停止することを要求しないでください(長い間)、または開発のために機能ブランチをマージして戻しますか?


6
これは使用するプログラミング言語にも依存すると思います。
ロバートアンドジェジュク

8
「小さな増分」チェックインが好きです。誰かがレポジトリのコピーを最新に保っていない場合を除き、この方法により、すべてのユーザーのマージの競合が最小限に抑えられます。
マットラッフェル

5
テストはどのように見えますか?大きな(そしておそらく重要な)コードをリファクタリングする場合は、リファクタリングする前にテストスイートが本当に良好な状態にあることを確認してください。これにより、小さなファイルで正しいことを確認しやすくなります。
corsiKa

1
これにはさまざまなアプローチがありますが、最適なアプローチは状況によって異なります。
スティーブン

3
私はプロジェクトに参加しました。最大のファイルは10k行の長さで、特にクラス自体が6k行の長さで、誰もがそれに触れることを恐れています。つまり、あなたの質問は素晴らしいということです。この単一のクラスがマウスのスクロールホイールのロックを解除する正当な理由であるというジョークを発明しました。
ElmoVanKielmo

回答:


41

これは社会的な問題ではなく技術的な問題であることを正しく理解しています。過度のマージ競合を回避したい場合、チームはこれらの競合を回避する方法で協力する必要があります。

これはGitの大きな問題の一部であり、分岐は非常に簡単ですが、マージには依然として多くの労力がかかる可能性があります。開発チームは多くのブランチを立ち上げる傾向があり、そのコンテキストを理解せずにGit Flowをエミュレートしようとしている可能性があるため、それらのマージが難しいことに驚いています。

高速で簡単なマージの一般的なルールは、大きな機能の違いが累積するのを防ぐことです。特に、機能ブランチは非常に短命でなければなりません(数か月ではなく、数時間または数日)。変更を迅速に統合できる開発チームは、マージの競合が少なくなります。一部のコードがまだ製品の準備が整っていない場合は、機能フラグを使用して統合することはできますが、機能を無効にすることは可能です。コードがmasterブランチに統合されるとすぐに、実行しようとしている種類のリファクタリングがアクセス可能になります。

差し迫った問題には多すぎるかもしれません。ただし、リファクタリングを実行できるように、このファイルに影響する変更を週の終わりまでマージするように同僚に依頼することは実行可能です。待機時間が長くなると、マージの競合自体を処理する必要があります。それは不可能ではなく、回避可能な作業です。

また、依存コードの大規模な分割を防ぎ、API互換の変更のみを行うこともできます。たとえば、いくつかの機能を別のモジュールに抽出する場合:

  1. 機能を別のモジュールに抽出します。
  2. 古い関数を変更して、呼び出しを新しいAPIに転送します。
  3. 時間が経つにつれて、新しいAPIへのポート依存コード。
  4. 最後に、古い関数を削除できます。
  5. (機能の次の束のために繰り返します)

この複数ステップのプロセスにより、多くのマージの競合を回避できます。特に、抽出した機能を他の誰かが変更している場合にのみ競合が発生します。このアプローチのコストは、すべてを一度に変更するよりもはるかに遅く、一時的に2つの重複したAPIがあることです。これは、何か緊急事態がこのリファクタリングを中断し、重複が忘れられるか優先度が下げられるまで、それほど悪くありません。

しかし、最終的には、どのソリューションでもチームと調整する必要があります。


1
@Laiv残念なことに、これは非常に一般的なアドバイスですが、継続的インテグレーションのようなアジャイルっぽいスペースからのアイデアには明らかにメリットがあります。連携して作業するチーム(および頻繁に作業を統合するチーム)は、互いに連携して作業するだけのチームよりも、大きな横断的な変更を行う方が簡単です。これは必ずしもSDLC全体に関するものではなく、チーム内のコラボレーションに関するものです。いくつかのアプローチにより、一緒に作業することがより実現可能になります(オープン/クローズドプリンシパル、マイクロサービスを考えてください)が、OPのチームはまだありません。
アモン

22
機能ブランチのライフタイムを短くする必要があるとは言いませんが、長い間親ブランチから分岐しないようにしてください。親ブランチから機能ブランチへの変更を定期的にマージすることは、機能ブランチがより長く続く必要がある場合に機能します。それでも、必要以上に機能ブランチを保持しないことをお勧めします。
ダンライオンズ

1
@Laiv私の経験では、リファクタリング後の設計について事前にチームと話し合うことは理にかなっていますが、通常は1人でコードを変更する方が簡単です。それ以外の場合は、ものをマージする必要があるという問題に戻ります。4kの行は多くのように聞こえますが、extract-classのようなターゲット絞ったリファクタリング用ではありません。(私が読んだなら、ここでMartin Fowlerのリファクタリングの本を一生懸命読みます。)しかし、4k行、「これをどのように改善できるかを見てみましょう」などのターゲット絞ったリファクタリングにのみ適してます。
アモン

1
@DanLyons原則としてあなたは正しいです:それはマージの努力の一部を広げることができます。実際には、Gitのマージは、マージされるブランチの最新の共通の祖先コミットに大きく依存します。master→featureをマージしても、masterの新しい共通の祖先は得られませんが、feature→masterをマージすると得られます。master→featureのマージが繰り返されると、同じ競合を何度も解決しなければならない場合があります(ただし、これを自動化するにはgit rerereを参照してください)。マスターの先端が新しい共通の祖先になるため、リベースは厳密に優れていますが、履歴の書き換えには他の問題があります。
アモン

1
答えは私にとっては大丈夫です。ただし、gitがブランチを簡単にしすぎており、そのためdevsが頻繁にブランチしすぎているという例外があります。SVNの時間と、CVSでさえ、分岐が困難(または少なくとも面倒)で、関連するすべての問題で、可能であれば一般的に回避できるほどの時間をよく覚えています。gitでは、分散システムであるため、多くのブランチを持つことは、実際には(つまり、各dev上で)多数の別々のリポジトリを持つことと何の違いもありません。解決策は別の場所にあり、分岐しやすいことは問題ではありません。(そして、はい、私はそれが脇にあることがわかります...しかしそれでも)。
AnoE

30

より小さなステップでリファクタリングを行います。大きなファイルに名前があるとしましょうFoo

  1. 新しい空のファイルを追加し、Bar「トランク」にコミットします。

  2. Fooに移動できるコードの小さな部分を見つけますBar。移動を適用し、トランクから更新し、コードをビルドしてテストし、「トランク」にコミットします。

  3. ステップ2を繰り返しまでFooBar同じサイズを持っている(またはあなたが好むどんなサイズ)

そうすれば、次回チームメイトがトランクからブランチを更新するときに、「小さな部分」で変更を取得し、それらを1つずつマージできます。他の誰かがその間にトランクを更新したため、ステップ2でマージの競合が発生した場合も同様です。

これにより、マージの競合や手動で解決する必要はなくなりませんが、各競合をコードの小さな領域に制限するため、管理しやすくなります。

そしてもちろん-チームでリファクタリングを伝えます。あなたが何をしているのかを仲間に知らせてください。そうすれば、特定のファイルのマージの競合を予想しなければならない理由がわかります。


2
これは、GITSで特に便利ですrerereオプションが有効になって
D.ベンKnoble

@ D.BenKnoble:その追加に感謝します。私は認めなければなりません、私はgitの専門家ではありません(しかし、説明されている問題はgitに特化したものではなく、分岐を許可するすべてのVCSに当てはまり、私の答えはこれらのシステムのほとんどに当てはまります)
Doc Brown

用語に基づいて考えました。実際、gitでは、この種のマージは1回だけ実行されます(プルしてマージするだけの場合)。しかし、開発者の好みに応じて、いつでもプルしてチェリーピックするか、個々のコミットをマージするか、リベースできます。時間がかかりますが、自動マージが失敗する可能性が高い場合は確実に実行可能です。
D.ベンノーブル

18

ファイルをアトミック操作として分割することを考えていますが、中間的な変更を加えることができます。ファイルは時間の経過とともに徐々に大きくなり、時間の経過とともに徐々に小さくなります。

長い間変更する必要のない部分を選択し(git blameこれで解決できます)、最初にそれを分割します。その変更を全員のブランチにマージし、次に分割する最も簡単な部分を選択します。たぶん、1つの部分を分割するだけでも大きなステップなので、最初に大きなファイル内でいくつかの再配置を行う必要があります。

人々が頻繁にマージして開発に戻っていない場合は、それらをマージした後、その機会に変更したばかりの部分を分割することを奨励する必要があります。または、プルリクエストのレビューの一環として、分割を行うよう依頼します。

アイデアはゆっくりと目標に向かって進むことです。進行が遅いように感じますが、突然、コードがはるかに優れていることに気付くでしょう。オーシャンライナーを回すには長い時間がかかります。


ファイルが大きく始まっている可能性があります。サイズのファイルをすばやく作成できます。1日または1週間で1000のLoCを作成できる人を知っています。また、OPは自動化されたテストについて言及していませんでした。
チャックコットリル

9

この問題の通常の解決策とは異なる解決策を提案します。

これをチームコードイベントとして使用します。できる人全員にコードをチェックインしてもらい、ファイルをまだ使用している他の人を助けてください。関係者全員がコードをチェックインしたら、プロジェクターを備えた会議室を見つけて、協力して新しいファイルに移動します。

これに特定の時間を設定することで、1週間分の議論に終わりが見えないようにすることができます。代わりに、これは必要なものがすべて見られるまで、毎週1〜2時間のイベントになることもあります。ファイルをリファクタリングするのに1〜2時間しか必要ないかもしれません。たぶん、試すまでわかりません。

これには、リファクタリングで全員が同じページにいるという利点があります(しゃれを意図していません)が、ミスを避けたり、必要に応じて維持できるメソッドのグループ化について他の人から入力を得るのにも役立ちます。

このような方法で行うと、組み込みのコードレビューがあると考えることができます。これにより、適切な量の開発者がコードをチェックインし、レビューの準備ができたらすぐにコードを承認できます。あなたはまだあなたが見逃したものについてコードをチェックすることを望むかもしれませんが、それはレビュープロセスがより短いことを確認するための長い道のりになります。

これは、すべての状況、チーム、または企業で機能するとは限りません。作業は、これを簡単に実現する方法で配布されないためです。また、開発時間の誤用として(誤って)解釈されることもあります。このグループコードには、マネージャーとリファクタリング自体からの賛同が必要です。

このアイデアを上司に売り込むには、コードレビュービットと、最初からどこにいるのかを知っている全員に言及してください。開発者が新しいファイルのホストを検索する時間を失うのを防ぐことは避ける価値があります。また、開発者が物事が終わった場所や「完全に欠けている」場所についてPOを取得しないようにすることは、通常良いことです。(メルトダウンが少ないほど良い、IMO。)

この方法で1つのファイルをリファクタリングすると、それが成功して有用であれば、より多くのリファクタリングの承認をより簡単に取得できる場合があります。

ただし、リファクタリングを行うことに決めた場合は、幸運を祈ります!


これは、チームの連携を実現するための非常に優れた方法を捉える素晴らしい提案であり、それを機能させるために不可欠です。さらに、ブランチの一部をmaster最初にマージできない場合、少なくともそれらのブランチへのマージを処理できるように部屋に全員がいることになります。
コリンヤング

コードモブを提案した+1
ジョンレイナー

1
これは、問題の社会的側面に正確に対応しています。
チャックコットリル

4

この問題を修正するには、共有リソース(コード自体)を変更しようとしているため、他のチームからの賛同が必要です。そうは言っても、人を混乱させることなく巨大なモノリシックファイルを「移行」する方法があると思います。

また、個々のファイルのサイズに加えて、巨大なファイルの数が手に負えないほど増えてない限り、一度にすべての巨大なファイルをターゲットにしないことをお勧めします。

このような大きなファイルをリファクタリングすると、予期しない問題が頻繁に発生します。最初のステップは、大きなファイルが現在masterまたは開発ブランチにあるものを超えて追加機能蓄積するのを止めることです。

これを行う最良の方法は、デフォルトで大きなファイルへの特定の追加をブロックするコミットフックを使用することだと思いますが、コミットメッセージ内の魔法のコメントなどで無効にすることができます@bigfileok。痛みはないが追跡可能な方法でポリシーを無効にできることが重要です。理想的には、コミットフックをローカルで実行でき、エラーメッセージ自体でこの特定のエラーをオーバーライドする方法を示す必要があります。また、これは私の好みですが、認識されていない魔法のコメントまたはコミットメッセージで実際に発生しなかったエラーを抑制する魔法のコメントは、コミット時の警告またはエラーである必要がありますので、必要かどうか。

コミットフックは、新しいクラスをチェックしたり、他の静的分析(アドホックかどうか)を行ったりする可能性があります。また、現在のファイルよりも10%大きい行数または文字数を選択して、大きなファイルが新しい制限を超えて拡大できないと言うこともできます。また、行数が多すぎたり、文字数が多すぎたり、w / eが増えたりして、大きなファイルを成長させる個々のコミットを拒否することもできます。

大きなファイルが新しい機能の蓄積を停止すると、一度に1つずつリファクタリングすることができます(同時に、コミットフックによって強制されるしきい値を減らして、再度成長するのを防ぎます)。

最終的に、大きなファイルはコミットフックを完全に削除できるほど小さくなります。


-3

ホームタイムになるまで待ちます。ファイルを分割し、コミットしてマスターにマージします。

他の人は、他の変更と同様に、朝に変更を機能ブランチにプルする必要があります。


3
それでも、彼らは彼らの変化けれども...と私のリファクタリングをマージしなければならない意味します
ホフ


1
まあ、実際にそれらがすべてこれらのファイルを変更している場合、とにかくマージに対処する必要があります。
ライヴ

9
これには、「驚き、私はすべてのものを壊しました」という問題があります。OPは、これを行う前に賛同と承認を得る必要があり、「進行中」のファイルを他の誰も持っていないスケジュールされた時間にそれを行うと役立ちます。
computercarguy

6
クトゥルフの愛のためにこれをしないでください。チームで働くことができる最悪の方法についてです。
モニカとの軽さレース、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.