web-dev-qa-db-ja.com

1つのメソッドシグネチャを変更したところ、25,000以上のエラーが発生しました。今何?

私は最近、非常に大きなアプリケーション(1500万ロケーション)に取り組んでいる新しい仕事を始めました。私の以前の仕事では同様に大きなアプリケーションがありましたが、(良いか悪いかは問わず)OSGiを使用しました。つまり、アプリケーションは独立して変更、コンパイル、およびデプロイできる多数のマイクロサービスに分割されました。新しいアプリケーションは、1つの大きなコードベースにすぎず、いくつかの.dllがあります。

上司から依頼されたので、このクラスのインターフェースを変更する必要があります。彼らは当初、あまり一般化されなかったいくつかの仮定でそれを書きました、そして、それは非常に密に結合されているので、しばらくの間、リファクタリングの問題を避けてきました。インターフェイスを変更したところ、25000以上のエラーが発生しました。エラーの一部は、「XYZPriceCalculator」のような重要な発音の名前を持つクラスにありますreaaallyは壊れません。しかし、すべてのエラーが解決されるまで、アプリケーションが起動しているかどうかを確認できません。ユニットテストの多くは、そのインターフェイスを直接参照するか、そのインターフェイスを参照する基本クラスに結合されているため、それらを修正するだけでもかなり大きな作業になります。加えて、これらすべての要素がどのように組み合わされるのか本当にわかりません。そのため、それを開始できたとしても、何かが壊れた場合にどのように見えるかわかりません。

私は最後の仕事でこのような問題に実際に直面したことはありません。私は何をしますか?

169
user788497

25000エラーは、基本的には「触れないでください」という意味です。元に戻します。目的のインターフェイスを持つ新しいクラスを作成し、クラスのコンシューマーをゆっくりと新しいクラスに移動します。言語によっては、古いクラスに非推奨のマークを付けることができます。これにより、あらゆる種類のコンパイラ警告が発生する可能性がありますが、実際にはビルドは中断されません。

残念ながら、これらは古いコードベースで発生します。徐々に物事を改善することを除いて、あなたがそれについてできることは多くありません。新しいクラスを作成するときは、それらを適切にテストし、SOLIDの原則を使用して作成してください。そうすることで、将来変更しやすくなります。

350
Stephen

リファクタリングで分割して征服する

多くの場合、ソフトウェアにまったく影響を与えない方法でほとんどの小さなステップを実行できるので、必要な変更を小さなステップに分割すると役立ちます。リファクタリングツールは、このようなタスクに非常に役立ちます。

割る

最初に、達成したい変更を合計した、可能な最小の変更(変更されたLoCの観点からではなく、論理的な変更の観点から)を特定します。 特に、純粋なリファクタリングであり、ツールで実行できるステップを分離するようにしてください。

征服する

あなたのような複雑なケースでは、一度に1つの小さなリファクタリングを実行し、その後問題を解決することが理にかなっている可能性があります。これにより、すべての継続的インテグレーションシステムが変更を検証でき、テストチームも外観を整えることができます。これにより、実行した手順が検証されます。

特定のリファクタリングを実行するには、変更するメソッドの呼び出しサイトが25,000ある場合のツールサポートが絶対に必要です。 たぶんsearch-and-replaceも機能しますが、そのような重大なケースでは、私はそれが怖いでしょう。

たとえば、C#では、メソッドの Resharper から signatureの変更 を使用できます。変更が十分に簡単な場合。新しいパラメーターを追加すると、コールエラーが発生する呼び出しサイトで使用する値を指定できます

その後、すぐにエラーのないコードベースになり、すべての単体テストを実行できます。これはリファクタリングのみであったため、合格します。

メソッドのシグネチャが適切に見えたら、Resharperが新しく導入されたパラメーターの引数として追加した値に移動して置き換えることができます。これはもうリファクタリングではありませんが、エラーのないコードベースがあり、変更する各行の後にテストを実行できます。

時々それはうまくいかない

これは、多数の呼び出しサイトがある場合など、非常に便利な方法です。これで作業用ソフトウェアができたので、小さなリファクタリング手順を実行してシグネチャを少しだけ変更してから、別のリファクタリングを実行できる場合があります。

残念ながら、署名の変更が複雑すぎて細かい変更に分解できない場合は機能しません。しかし、これはまれです。問題をより小さな問題に分割すると、通常が可能であることが示されます

79
theDmi

上司と一緒にタスクを明確にして、プロのソフトウェア開発者としての問題とニーズを理解できるようにします。

チームの一員である場合は、リード開発者を探し、彼に助言を求めます。

幸運を。

36
mmehl

触れないでください。何もしないでください。

代わりに、椅子に座って「Heeeeelp !!!!!」と叫んでください。できるだけ大声で。

まあ、その通りではありませんが、先輩にアドバイスを求めてください。エラーが25,000ある場合、エラーを修正するのではなく、エラーの原因を修正します。そして、上級の同僚はあなたにアドバイスすることができるはずですhow上司が望む変更を25,000エラーなしで行うことができます。これにはさまざまな方法がありますが、どの方法が適切かは、特定の状況によって異なります。

そして上司があなたの先輩に同じ変更をするように言ったかもしれません、そして彼らは「ノー」と言いました彼らは何が起こるか知っていたからです。それがあなたに仕事が与えられた理由です。

28
gnasher729

埋め込まれたAPIを単純に変更することはできません。 reallyを変更する必要がある場合は、それらを廃止またはドキュメント化して(言語で許可されている方法であれば何でも)、代わりに使用するAPIを文書化します。その後、古いAPIは徐々に段階的に廃止されます。リファクタリングの時間予算によっては、おそらく非常にゆっくりと廃止されます。

22
Kevin Krumwiede

評価

この変更が必要かどうか、または新しいメソッドがaddできるかどうかを評価し、他のメソッドを非推奨にします。

進む

変更が必要な場合;次に、移行計画が必要です。

最初のステップは、新しいメソッドを導入し、古いメソッドに引数をマッサージして、新しいメソッドを呼び出せるようにすることです。これには、いくつかのハードコーディングが必要になる場合があります。それはいいです。

これはコミットポイントです。すべてのテストがパス、コミット、プッシュすることを確認してください。

移行

忙しい作業により、古いメソッドのすべての呼び出し元が新しいメソッドに移行されています。ありがたいことに、それはフォワーダーのおかげで徐々に行うことができます。

先に進んでください。ツールを使用して支援することをためらわないでください(sedが最も基本的ですが、他にもあります)。

古いメソッドに非推奨のマークを付けます(新しいメソッドに切り替えるヒントがあります)。これは、何かを忘れたかどうかを見つけるのに役立ち、これに取り組んでいるときに同僚が古いメソッドの呼び出しを導入した場合に役立ちます。

これはコミットポイント(またはおそらく複数のコミットポイント)です。すべてのテストがパス、コミット、プッシュであることを確認してください。

削除

しばらくしてから(おそらく1日程度)、古いメソッドを削除してください。

10
Matthieu M.

メソッドシグネチャへの変更が単なる名前の変更である場合、簡単な解決策は、ツールを使用して、問題のメソッドを参照する25,000クラスの変更を自動化することです。

コードを手動で編集しただけで、すべてのエラーが発生したと思います。 Java(OSGiへの参照を参照))に精通していることも想定しているため、たとえばEclipseで(使用しているプログラミング環境はわかりませんが、他の環境にも同様のリファクタリングツールがあります) 「リファクタリング->名前の変更」を使用して、メソッドへのすべての参照を更新できます。これにより、エラーが発生しなくなります。

単に名前を変更する(パラメーターの数またはタイプを変更する)以外の方法でメソッドシグネチャを変更する場合は、「リファクタリング->メソッドシグネチャの変更」を使用できます。ただし、他の回答が示唆するように、より注意深くする必要がある可能性があります。また、変更の種類に関係なく、ビジーなコードベースでこれらの変更をすべてコミットするのは、かなりの作業になる可能性があります。

8
MikkelRJ

ここに私の貢献。

私は最近、非常に大きなアプリケーション(1500万行のコード)で作業する新しい仕事を始めました。

あなたはおそらくプロジェクトとその「機能」に精通していません。 1行のコードを入力する前に、プロジェクトに精通していることが重要です。 変更をロールバックして、コードを分析することから始めます。 (少なくとも影響を受けるもの)

既存のソリューションを理解することで、どこに参入するかについてより良い見通しが得られます。 ソリューションとその重要性をコンテキスト化します。

@Gregが指摘したように、既存のコードをテストして、有効な参照と比較できます(回帰テスト)。 ソリューションは、同じ結果を生成できる必要があります既存のソリューションよりも優れています。この段階では、結果が正しいかどうかは気にしないでくださいです。最初の目標は、バグを修正することではなく、リファクタリングすることです。既存のソリューションが「2 + 2 = 42」を示している場合、ソリューションもそうする必要があります。例外がスローされない場合でも、例外は発生しません。 nullを返す場合は、nullも返す必要があります。等々。そうしないと、25,000行のコードが危険にさらされます。

これはレトロ互換のためです。

どうして?今のところ、それは成功したリファクタリングのあなたのユニークな保証です。

また、ユニットテストの多くは、そのインターフェイスを直接参照するか、そのインターフェイスを参照する基本クラスに結合されています。

レトロ互換性を保証する方法が緊急に必要とされているので、ここであなたの最初の課題です。単体テスト用のコンポーネントを分離します。

これらの25k行のコードは、既存のコードの可能な結果を​​想定して作成されたことに留意してください。契約のこの部分を破らなければ、最終的な解決策の半分に達します。もしそうなら、まあ:力があなたと一緒になるかもしれない

新しい「契約」を設計して実装したら、古い契約を置き換えます。それを非難するか、取り除いてください。

バグのリファクタリングと修正は別のタスクであるため、バグをそのままにすることをお勧めします。それらを一緒に進めようとすると、両方で失敗する可能性があります。バグを見つけたと思うかもしれませんが、それらは「機能」である可能性があります。したがって、それらをそのままにしておきます(1分間)。

25k行のコードは、1つのタスクのみに集中するのに十分な問題だと思われます。

最初のタスクが完了したら。これらのバグ/機能を上司に公開します。

最後に、@ Stephenが言ったように:

ゆっくりと物事を改善することを除いて、それについてできることは多くありません。新しいクラスを作成するとき、正しくテストすることを確認し、SOLIDの原則を使用してそれらを作成します。これにより、将来変更しやすくなります。

6
Laiv

テストしてください

他の誰もが影響をほとんど与えないようにリファクタリングする方法を推奨しています。しかし、それだけ多くのエラーがあるため、たとえわずか10行のコードでリファクタリングに成功したとしても(おそらく可能です)、25,000のコードフローに影響を与えています、たとえそれらを書き直す必要がなかったとしても。

したがって、次に行うことは、回帰テストスイートが飛行色で合格することを確認することです。そして、もしあなたがそれを持っていないなら、それを作るでしょう。包括的なリグレッションテストスイートをモノリシックプロジェクトに追加するのは退屈に聞こえますが、リリース候補の信頼性を高め、スイートが適切に自動化されている場合はリリースを迅速に行うには良い方法です。

5
Greg