web-dev-qa-db-ja.com

アプリケーションの接着剤を書き換える準備をする方法

(あなたの意見では)主にまともな形で書かれ、モジュール化されているなどのプロジェクトがあり、その機能の多くを維持したいとします。ただし、このプロジェクトの本質的な部分はうまく設計されておらず、小さな変更で簡単に修正することはできません。それを修正する唯一の方法は、多くのコードを取り除くことです。最初から必要とする(または最初から始める)ほど悪くはありませんが、非常に普及しています。

コードが20,000行の非空白、非単一中括弧、非コメントJava、C++、C#、PHPまたはRubyコード。約2000〜3000行を破棄または変更する必要があると推定します。JavaまたはC#のようなものの場合、パッケージ/名前空間の1つがおそらく最初から完全に書き直されます。これらのクラスへの参照の多くは変更されます。

ここが難しい部分です。変更する必要があるコードは、システム内のGLUEでした。これは問題を要約する非常に単純な図です:

enter image description here

これが私がこの質問につながった状況です: コミット間ですでに長時間待機している場合はどうすればよいですか?

ご覧のとおり、内部処理を行っていたのはコードの一部だけではなく、システムの他の部分と通信していた部分でもあります。 APIと内部データ構造の両方が不十分でした。両方のAPIセットを段階的な変更のために動作させ続けることは、途方もなく冗長でした。

もちろん、私は何かをする前に分岐しましたが、私が行ったすべての変更は、単に機能しない製品を生み出しました。多くの場合、コンパイルエラーが発生しました。 「そうです、これらの部品を一緒に配線し直しましたが、この他の新しいインターフェースを実装したり、この重要な機能を記述したりする必要がある」などと考えたため、これはコミットを継続的に延期することにつながりました。これにより、何もチェックインしないという主要な罪が発生しました。この状況が再び発生した場合、繰り返したくない間違いです。これは、外部のAPIとサードパーティのモジュールに準拠する必要があることを除いて、すべてのAPIが内部にあるという事実によって緩和されましたが、これらのセクションのラッパーが変更されていました。

明白な代替ソリューションの1つは、変更ごとに製品全体が壊れるという事実を受け入れることですが、ブランチにチェックインしているので問題ありません。特に私がこのコードに取り組み、これらのAPIを使用する唯一の開発者であるためです。だから多分それは正しいことです。または、正しいことは、一部の引数がダミーであるか最終的に削除される場合でも、実際に機能を変更せずに(可能であれば)最初にAPIを新しいものにリファクタリングすることであり、その後、増分変更がはるかに自己含まれています。それとも、完全に良い方法があるのでしょうか?

5
durron597

簡単に言えば: 海を沸騰させないでください

言い換えると、一度にすべてを実行しようとするのではなく、管理しやすい小さなチャンクを分割します。 1つのブランチ。定期的なチェックインなし。

私はいつも好きでした Joel on Softwareのこの記事 。それは、コードベースに対して少しずつクリーンにするために行うことができる小さな変更の種類についての非常に良い具体例を提供します。

概念的に同様の変更のチャンクを決定し、そのためのブランチを作成します。たとえば、すべてのSQLコードをクラスに移動する場合があります。そのブランチでは、ワークフローは次のようになります。

  • 1つのSQLステートメントを削除する
  • テストを実行する
  • コードをチェックインする
  • 繰り返す

変更のチャンクが完了したら、それをマスターブランチにマージし、新しいチャンクを開始します。

これは、テストがあることを前提としています。そうでない場合は、開始する前に作成する必要があります(単体テスト、回帰テスト、統合テストを含む)。繰り返しますが、一度にすべてではありません。テストがない関数をリファクタリングしたい場合は、テストを記述し、コードをリファクタリングして、合格するまでテストを実行します。これがまさにこれらのテストの目的です。それらを利用していることを確認してください。

8
Darrick Herwehe

お使いの製品がリリースされているかどうか、およびタッチする予定のAPIが、個別にリリースされている他のエンティティ(モジュール、プロジェクト、製品、ドキュメントなど)に何らかの形で公開されているかどうかを知ることが重要です。それに対する答えが「はい」の場合、移行フェーズでAPIの互換性を維持する必要があります。古いAPIを置き換えるのではなく、拡張してください。 2番目のステップでは、古いAPIを廃止し、影響を受けるエンティティに更新の機会を与えることができます。 If関連するすべてのエンティティが古いAPIの使用を停止していることを確認できます。その場合にのみ、APIを削除できます。 (外部の依存関係がない場合でも、同じ方法を使用するように書き換えることができる場合があります)。

そうは言っても、大規模な書き直しへの良いアプローチは、作業をできるだけ小さな独立したステップに分割することです。

複数のエンドで同時に作業するという罠に陥るのは簡単ですが、通常はそれが何らかの形でコミットを混乱させます-最良の場合、複数のブランチと進行中の機能を追跡する必要があり、いくつかでマージする必要がありますポイント;最悪の場合、すべての新機能が相互にインターリーブされて開発され、単一の機能に属するクリーンコミットを作成できなくなります。また、半機能または非コンパイル機能を保持し続けるため、コミットをまったく実行できなくなります。それはまた、あらゆる種類のあいまいな相互作用のバグを導入するのを「助け」、バグを導入する正確なポイントを見つけようとするときに多くのトラブルを引き起こします。

これは主に精神的な規律の練習であり、それに複数のアプローチがあります。まず、新しいAPIをスタブ化し、目的とAPIの詳細を文書化することから始めます。これが完了すると、安定したコミットポイントが得られます。その後、個々の機能と単体テストの実装に移行できます(途中で複数回コミットすることもできます)。最後に、新しいAPIを呼び出すモジュールを変更し、すべての「接着」作業を行います。この順序は、ビルドを中断することなくコミットできる停止点を提供するのに役立ちます。

繰り返しますが、これは1つの方法であり、唯一の方法ではありません。目的は、書き換えをモジュール化し、変更間の相互作用を最小限に抑えることです。

2
Sir Athos

私は複数のtargeted rewriteに関与しています。コードを書き始める前に、分析を行う必要があります。

  • うまく設計された部分と不十分に設計された部分との違いは何ですか?
  • 適切に設計されていないコードを再設計して、適切なコードと同じ設計構造に一致させることはできますか?
  • 不十分に設計されたコードへのすべてのタッチポイントを特定します。つまりどのメソッドが呼び出されるか、どのように呼び出されるか(つまり、オブジェクトインスタンスやシングルトンなど)
  • 安全にこれらのタッチポイントをいくつ削除できますか?
  • 悪いコードの露出を良いコードから分離するために使用できるインターフェースを特定します。

これらすべての生データから、最終結果がどのようになるかについて真剣に考えます。完了したとき、またはコードを1行もスクラップすることなく高品質の製品を提供できるように書き換えが完了したタイミングを知る必要があります。

  • 作業を、一度に1つずつ作業できるタスクに分解します。
  • 各タスクにかかる時間を見積もります。本当に適切な見積もりを出すことができない場合は、タスクをさらに細かく分解する必要があるでしょう。自動テストや単体テストの時間を含めます。
  • あなたが取る各タスクをあなたが考えるすべての時間を合計し、それを3倍にします。真剣に。そこにはwill現時点では予期できない方法で再設計した結果が隠されています。
  • 対象の書き換えだけにアクティビティを制限します。新しい機能を使用する時間も、コードの他の部分に取り組む時間もありません。
  • タスクを1つずつ実行します。次に進む前に、各タスクが完了してテストされていることを確認してください。

対象を絞った書き直しの経験から、結果として得られる製品は何度も改善され、保守が容易になったと言えますが、スケジュールが最大の問題でした。この作業を完了する時間が限られている場合、書き直しを行うと、プロジェクトは当初考えていたよりもかなり遅くなります。

2
Berin Loritsch