web-dev-qa-db-ja.com

入力されていないように見えるコードを安全に削除するにはどうすればよいですか?

不必要に見えるコードをいくつか見つけましたが、コンパイラーはそれに気付きません。このコードを削除してもリグレッションは発生しないことを確認する(またはできる限り確認する)ために何をしますか。

2つのアイデアが思い浮かびます。

  1. コードを実行する必要があるように見えるかどうかに基づいて、「単純に」控除を使用します。ただし、これは複雑で時間のかかる作業になる場合があり、実質的なビジネスリターンが得られないため、少しリスクが高くなります(評価がエラーになる傾向があります)。

  2. そのコードセクションにロギングを配置し、実際に入力される頻度を確認します。十分な実行が終わったら、コードを削除しても安全であるという合理的な確信が持てるはずです。

より良いアイデアや標準的なアプローチのようなものはありますか?

124
Brad Thomas

100%の単体テストカバレッジがある完璧なファンタジーの世界では、それを削除して単体テストを実行し、テストが赤くならない場合はコミットします。

しかし、残念なことに、毎朝目を覚まし、多くのコードに単体テストがないか、そこにある場合、可能なすべてのEdgeケースを実際にカバーすることが信頼できないという厳しい現実に直面する必要があります。だから私はリスク/報酬を検討し、それだけでは価値がないという結論に達します:

  • 報酬:コードは、将来的に保守が少し簡単になります。
  • リスク:考えていなかった不明瞭なEdgeケースでコードを中断し、予想以上にインシデントを引き起こし、コード品質のOCDを満たし、ビジネス価値を認識できない変更を加えなければならなかったため、それに対して責任がありません。それ自体がコーダーではない関係者に。
110
Philipp

このプロセスには2つの部分があります。 1つ目は、コードが実際に死んでいることを確認することです。 2つ目は、誤った場合のコストを把握し、適切に緩和することです。

ここでの多くの回答は、前半に対する優れた解決策を持っています。静的アナライザーのようなツールは、死んだコードを識別するのに最適です。 grepが友達になる場合もあります。私がよく行う1つの変わった手順は、コードの本来の目的が何であったかを特定することです。 「Xは製品の機能ではなくなったため、コードセグメントYは機能Xをサポートするように設計されている」と言うのは、「コードセグメントYの目的が見当たらない」と言うよりもはるかに簡単です。

後半は、コードを削除する必要があるかどうかについてグリッドロックを解除するための重要なステップです。答えを間違えることの意味を理解する必要があります。あなたが答えを間違えれば人々が死ぬつもりなら、注意を払ってください!たぶん、コードの乱れが時間の経過とともに発生することを受け入れ、代わりに自分で余分なものを書かないようにしてください。人々が死ぬつもりがない場合は、ユーザーがどれほど寛容かを自問してください。何かを壊して顧客関係を維持している場合、ホットフィックスを送信できますか?このような問題を見つけるために支払われるQ&Aチームはありますか?これらの種類の質問は、削除キーを押す前にどれだけ確実でなければならないかを理解するために不可欠です。

コメントの中で、rmunは、コードを削除する前にその本来の目的を理解するという概念の優れた表現を指摘しました。引用は現在 Chesterton’s Fence として知られています。コメントで直接引用するには大きすぎますが、ここで適切に引用するに値すると思います。

物事を改革することに関しては、物事を変形させることとは異なり、1つの単純明快な原則があります。おそらくパラドックスと呼ばれる原則。そのような場合、特定の機関または法律が存在します。簡単にするために、道路の向こう側にフェンスまたはゲートが設置されているとしましょう。より近代的なタイプのリフォーマーはそれを陽気に言ってこう言います。「私はこれの使用を知りません。それを片付けましょう。」よりインテリジェントなタイプのリフォーマーが答えるのに適しています。離れて考えてください。その後、戻ってきて、その使用法を知っていると私に言ったら、私はそれを破壊することを許可するかもしれません。

85
Cort Ammon

また、コード内の関数/クラス名をgrepにする傾向があります。これにより、コメントやドキュメントファイルで名前が言及されている場合や、たとえば、スクリプト。ソースツリーのファイルに対してgrepを実行し、結果をファイルに保存します。通常、結果は要約された情報を提供します:ファイル名/パス、行番号、および名前が検出された行。これは、(コードアナライザーとは対照的に)意味的な意味なしに関数/クラスが呼び出されるか言及される場所の手がかりを与えます。 )、およびファイル拡張子に関係なく。間違いなく究極のソリューションではなく、分析への素晴らしい追加です。

43
piwi
  • 死んでいるように見えるコードを特定します(静的分析など)。
  • 死んだとされるコードのすべての呼び出しのログメッセージを追加します。関数/メソッドを使用すると簡単です。定数のような静的メンバーの場合は、より扱いにくくなります。コードを非推奨としてマークするだけで、ランタイムが自動的にメッセージを記録する場合があります。それ以外の場合はコードをそのままにします。
    • デッドモジュールがロードされたときにメッセージをログに記録します。ほとんどの言語には、ロード時に静的初期化を実行する方法があり、ピギーバックすることができます。
    • ログメッセージに適切なスタックトレースが含まれていることを確認してください。これにより、デッドコードと呼ばれるwhatを理解できます。
  • 変更されたコードをすべてのテストスイートで実行します。また、テストスイートは、真夜中、四半期の変わり目、年の変わり目などの特別な時間についてもテストする必要があります。ログを見て、何が死んでいるかの理解を更新します。単体テストはデッドコードを具体的にテストする場合がありますが、他の単体テストや統合テストはそれに触れないことに注意してください。
  • 変更されたコードを本番環境で数週間実行します。月に一度のETL cronジョブなど、定期的なプロセスがすべてこの期間中に実行されるようにしてください。
  • ログを見てください。ログに記録されたものは実際には死んでいません。残りのデッドコードに対する呼び出しグラフの推移的な閉包はalso呼び出されなかったとしても、潜在的にデッドではありません。それを分析します。たぶん、いくつかのブランチは安全に死んでいます(例えば、現在消滅しているサービスのAPIで作業しています)一方で、まだブランチはありません。たぶん、モジュール/クラス全体が、その中で定義されている定数に対してのみロードされ、簡単にそれを使用できなくなります。
  • 残ったものはすべて安全に死んでおり、取り除くことができます。
30
9000

前述の既存の回答に加えて、複数のバージョンにわたってコードを繰り返し削除することもできます。

最初のバージョンでは、コードブロックが機能している状態で非推奨の警告を出すことができました。それ以降のバージョンでは、コードブロックを削除することはできますが、エラーメッセージを残してユーザーに機能を廃止し、使用できなくすることができます。最終バージョンでは、コードブロックとメッセージを削除します。

これは、エンドユーザーへの警告なしに予期しない機能を特定するのに役立ちます。最良のシナリオでは、コードは実際には何もせず、不要なコードが削除される前にいくつかのバージョンに渡って保持されるだけです。

16
user3196468

多くの人が、コードが未使用であることを証明できない場合は、コードをそのままにしておくことが「安全な」ことだと提案しています。

しかし、コードは資産ではなく、責任です。

なぜそれが重要でそのテストを指摘できるのかを説明できない限り、「より安全な」代替手段はそれを削除することでしょう。

それでもわからない場合は、少なくともゾンビコードを実行するためのテストを必ず追加してください。

7
Adrian McCarthy

機能トグルを使用して、問題のコードを完全に無視するようにソフトウェアの実行パスを変更できます。

このようにして、使用していないコードとトグルをオフにして変更を安全にデプロイできます。コードに関連するいくつかの重大な障害に気付いた場合は、トグルをオンに戻して、コードへの可能なパスを調査してください。

このアプローチにより、長期間にわたって問題が発生しなかった場合や、展開せずにライブでオンに戻すことができる場合に自信が持てます。ただし、さらに適切な方法は、問題の領域の周りに追加のロギングとテストカバレッジを適用することです。これにより、使用されているかどうかのより多くの証拠が提供されます。

トグルの詳細については、こちらをご覧ください: https://martinfowler.com/articles/feature-toggles.html

6
Sash

静的分析はもちろん...そして素晴らしいのは、新しいツールが必要ないことです。コンパイラには、必要なすべての静的分析ツールが含まれています。

メソッドの名前を変更して(たとえば、DoSomethingDoSomethingXに変更して)、ビルドを実行します。

ビルドが成功した場合、メソッドは明らかに何も使用されていません。削除しても安全です。

ビルドが失敗した場合は、それを呼び出すコード行を分離し、呼び出しを囲むifステートメントを確認して、呼び出しをトリガーする方法を決定します。トリガーとなる可能性のあるデータユースケースが見つからない場合は、安全に削除できます。

コードの削除が本当に心配な場合は、コードを保持することを検討してください。ただし、コードに ObsoleteAttribute (または使用言語の同等のもの)を付けてください。 1つのバージョンの場合と同様にリリースし、問題が発生していない状態でコードを削除します。

6
John Wu
  • コードアナライザーを使用して、これがデッドコードかどうかを確認します。
  • 私は自分のテストをチェックし、コードに到達しようとしました。テストケース内のコードに到達できない場合、デッドコードである可能性があります
  • 代わりにコードを削除して例外をスローします。 1つのリリースでは例外がアクティブになり、2番目のリリースでは例外を削除できます。安全のために、顧客が例外に気づいたときに、元のコードをアクティブにできるように緊急フラグ(Javaではシステムプロパティ)を設定します。したがって、例外を無効にして、元のコードを運用環境でアクティブ化できます。
4
Markus Lausberg

到達できないコードの削除

原則的に静的に型付けされた言語では、コードが実際に到達可能かどうかを常に知る必要があります。それを削除し、コンパイルします。エラーがなければ到達できませんでした。

残念ながら、すべての言語が静的に型付けされているわけではなく、すべての静的に型付けされた言語が原則となっているわけではありません。うまくいかない可能性があるのは、(1)リフレクションと(2)無原則なオーバーロードです。

動的言語、または精査中のコードの一部が実行時にリフレクションを介してアクセスされる可能性がある十分に強力なリフレクションを備えた言語を使用する場合、コンパイラーに依存することはできません。そのような言語には、Python、RubyまたはJavaが含まれます。

無原則のオーバーロードを伴う言語を使用している場合、オーバーロードを削除するだけで、単にオーバーロードの解決をサイレントにanotherオーバーロードに切り替えることができます。一部のそのような言語では、コードの使用に関連するコンパイル時の警告/エラーをプログラムすることができます。それ以外の場合、コンパイラに依存することはできません。そのような言語には、Java(@Deprecatedを使用)またはC++([[deprecated]]または= deleteを使用)が含まれます。

したがって、厳密な言語で作業することが非常に運が良ければ(錆が頭に浮かびます)、コンパイラーを信頼することによって、実際に自分の足で撃たれる可能性があります。残念ながら、テストスイートは一般的に不完全であるため、あまり役に立ちません。

次のセクションの頭出し...


潜在的に使用されていないコードの削除

多くの場合、コードは実際に参照されますが、実際にはそれを参照するコードの分岐は決して行われないsuspect

この場合、言語に関係なく、コードは明らかに到達可能であり、実行時の計測のみを使用できます。

過去には、このようなコードを削除するために3段階のアプローチを使用して成功しました。

  1. 実行されないと思われる各ブランチで、警告をログに記録します。
  2. 1サイクル後、特定のコードを入力すると、例外をスローするか、エラーを返します。
  3. 別のサイクルの後、コードを削除します。

サイクルとは何ですか?コードの使用のサイクルです。たとえば、金融アプリケーションの場合、月ごとの短いサイクル(月末に給与が支払われる)と年ごとの長いサイクルが予想されます。この場合、少なくとも1年待って、年末の在庫が他の方法では決して使用されないコードパスを使用する可能性があるため、警告が出されないことを確認する必要があります。

うまくいけば、ほとんどのアプリケーションではサイクルが短くなります。

TODOのコメントに日付を入れ、いつ次のステップに進むかをアドバイスすることをお勧めします。そしてあなたのカレンダーのリマインダー。

3
Matthieu M.

プロダクションからコードを削除することは、家の掃除のようなものです。屋根裏部屋から物を捨てる瞬間、あなたの妻は翌日、1923年に亡くなった隣人から彼女の偉大な祖母の3番目の甥の帰郷の贈り物を捨てたとしてあなたを殺します。

真剣に、誰もがすでに言及しているさまざまなツールを使用したおおまかな分析の後、およびすでに述べた段階的な非推奨アプローチを使用した後、実際の削除が行われると会社から消えてしまう可能性があることに注意してください。コードを残し、その実行をログに記録し、その実行のアラートが確実にユーザー(またはその後継者と割り当て者)に確実に通知されるようにすることが重要です。

このアプローチに従わないと、妻に殺される可能性が高くなります。 (すべての記念品のように)コードを保持する場合、ムーアの法則があなたの助けとなり、「私の靴の中の岩」のように感じられるコードによって使用されるジャンクディスク領域のコストによって、年とあなたは複数の水クーラーのゴシップや廊下で奇妙な顔の中心になる危険はありません。

PS:コメントに応じて明確にするために..元の質問は、「どうやって安全に削除する..」です。もちろん、バージョンコントロールは私の回答で想定されています。ごみを掘り下げて貴重なものを探すのと同じように。意味のある組織がコードを捨てることはなく、バージョン管理はすべての開発者の厳格な部分の一部であるべきです。

問題は、不要なコードに関するものです。実行パスの100%がコードに到達しないことを保証できない限り、コードの一部が不要であることを知ることはできません。そして、これは、この保証がほぼ不可能であるほど大きなソフトウェアであると想定されています。そして、質問を間違って読まない限り、会話のこのスレッド全体が関連するのは、削除されたコードが呼び出される可能性がある本番稼働中のケースであり、したがって、ランタイム/本番の問題があります。バージョン管理は本番環境の失敗により遅れをとらないので、「バージョン管理」に関するコメントは質問や私の元のポイントとは関係ありません。つまり、本当に必要な場合は、非常に長い時間枠で適切に非推奨にします。余分なコードは、ディスクスペースの肥大化に比較的わずかなコストを追加するだけなので、心配しないでください。

私見、コメントは不必要で、削除の候補です。

2
LMSingh

多分コンパイラdoes気づくでしょう、それは大騒ぎしないだけです。

サイズに合わせて、コンパイラを完全に最適化してビルドを実行します。次に、疑わしいコードを削除して、ビルドを再度実行します。

バイナリを比較します。それらが同一である場合、コンパイラはコードに気づき、黙ってコードを削除しました。ソースから安全に削除できます。

バイナリが異なる場合...それは決定的ではありません。それは他の変更である可能性があります。一部のコンパイラーは、コンパイルの日付と時刻をバイナリーに組み込みます(そして、おそらくそれを離れて構成することができます!)

1

私は最近、 "deleteFoo"と呼ばれるメソッドを使用して、この正確な状況に遭遇しました。メソッドベースの宣言以外にコードベース全体で見つかった文字列はありませんでしたが、メソッドの上部にログ行を賢く書きました。

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

コードが使用されたことがわかりました!いくつかのAJAXメソッドは "method:delete、elem = Foo"を呼び出し、それが連結されて call_user_func_array() で呼び出されます。

疑わしい場合はログに記録してください!ログがいっぱいになっていないのに十分な時間が経過した場合は、コードの削除を検討してください。ただし、コードを削除した場合でも、ログを残し、日付をコメントに残しておくと、Gitでコミットを見つけなければならない人にとって、コミットを見つけやすくなります。

1
dotancohen

それを囲んでいるコードが何をしているのかわかっている場合は、それをテストするが壊れているかどうかを確認します。これは、作業しているコードの一部をリファクタリングする最も簡単で最も費用効果の高い方法であり、とりあえず作業しているコードの変更を開発するという問題としてとにかく実行する必要があります。

一方、コードの一部をリファクタリングしている場合それが何をしているかを知らない場合削除しないでください)。最初にそれを理解し、安全であると判断した場合はリファクタリングします(そして、リファクタリングが時間の適切な使用であると判断できる場合のみ)。

さて、「死んだ」コードが実際にanythingに接続されていない状況があるかもしれません(私は自分で実行したことを知っています)。 。請負業者が開発したコードのライブラリなど、アプリの配信直後に廃止されたため実際にはどこにも実装されなかった(はい、そうです。具体的な例を考えていますが、どうして知りましたか?).

私は個人的にこのコードをすべて削除しましたが、これは巨大なリスクであるため、軽く実行することはお勧めしません-これはコードの量によって異なります変更は潜在的に影響を与える可能性があります(常に何かを見落としている可能性があるため)非常に注意深く、これを削除するかどうかを判断するために積極的な単体テストを実行する必要があります古代のレガシーコードはアプリケーションを壊します。

さて、そういうことはすべて言ったので、そのようなコードを試して削除することは、時間や正気に値する(== --- ==)価値がないでしょう。それがアプリケーションで深刻な問題にならない限り(たとえば、アプリケーションの膨張のためにセキュリティスキャンを完了できない...なぜそうなのか、私はまだ例を考えているのです)、到達しない限りお勧めしませんコードが本当にインパクトのある方法で腐敗し始めたそのような状況。

要するに、コードが何をするか知っているなら、まずそれをテストしてください。そうでない場合は、おそらくそのままにしておくことができます。 できないことを知っている場合変更を実装する前に、変更を積極的にテストする時間を費やしてください。

0
Zibbobz

grepまたは最初に使用可能なツールを使用すると、コードへの参照が見つかる場合があります。 (もともとは私の指示/アドバイスではありません。)

次に、コードを削除するリスクがあるかどうか、または私の提案が使用できるようになるかどうかを決定します。

コードの関数/ブロックを変更して、ログファイルに使用されたことを書き込むことができます。そのようなメッセージがログファイルに「すべて」記録されていない場合は、おそらくロギングコードを削除できます。

2つの問題:

  1. 他のユーザーからログを取得しています。おそらく、終了時にプログラムをクラッシュさせるグローバルフラグを設定して、ユーザーにエラーログを送信するように依頼することができます。

  2. 発信者の追跡一部の高級言語(Pythonなど)では、簡単にトレースバックを取得できます。しかし、コンパイルされた言語では、戻りアドレスをログに保存し、終了時にコアダンプを強制する必要があります(ポイント1)。
    Cでは、これは非常に単純なはずです。条件付きブロック(たとえば、#ifdef ... #endif)を使用して、機能することがわかっている(そして機能することをテストした)場合にのみ使用します。スタックから戻りアドレスを読み取るだけです(インラインアセンブリ言語が必要な場合と不要な場合があります)。

引数をログファイルに保存すると、役立つ場合とそうでない場合があります。

0
Oskar Skog

このケースでは常に業界標準であると想定していた私のアプローチは、奇妙なことに今のところ言及されていません。

2つ目の目を取得します。

「未使用コード」にはさまざまな種類があり、削除が適切な場合もあれば、それをリファクタリングする必要がある場合もあれば、可能な限り実行して戻ってこない場合もあります。あなたはそれがどれであるかを理解する必要があり、そうするためには秒とペアリングするのがベストです-経験豊富です! -開発者、コードベースに精通している人。これにより、不必要な間違いのリスクが大幅に減少し、コードベースを保守可能な状態に保つために必要な改善を行うことができます。これを行わないと、ある時点で、コードベースに精通している開発者が不足することになります。

ロギングアプローチも見ました。より正確には、ロギングコードを見てきました。10年間そこに残された、さらに多くのデッドコードです。ロギングアプローチは、機能全体の削除について話している場合はかなりまともですが、デッドコードの小さな部分を削除するだけの場合はそうではありません。

0
Peter

あなたの質問に対する簡単な答えは、あなたが理解していないコードを安全に削除することはできないということです。いくつかのリスクがあります。

その避けられないリスクを軽減するための最善の方法を判断する必要があります。他の人はロギングについて言及しています。ロギングはもちろん、それが使用されていることを証明できますが、使用されていないことは証明できません。デッドコードの主な問題は維持されることであるため、デッドコードの疑いがあるというコメントを追加して、メンテナンスを行わないようにすることができます。

コードがソース管理下にある場合は、いつコードが追加されたかを確認し、それがどのように呼び出されたかを判断できます。

最終的には、あなたはそれを理解するか、それを放っておくか、あるいはチャンスをつかむかのどちらかです。

0
jmoreno

問題のコードの開発者がまだ利用できる場合は、彼と一緒にコードの削除を確認してください。また、コード内のコメントを読み取ります

あなたは適切な説明、このコードが追加された理由、そしてそれがどの機能に役立つのかを得るでしょう。特定のユースケースを簡単にサポートできるほか、本当に必要でないかどうかを判断する専門知識がない場合もあります。極端な場合、Cプログラムからすべての free ステートメントを削除して、問題に気付かないことがあります(メモリ不足になるまで数分かかる場合があります)。

コードの作成者があなたの隣にいる場合、それは本当に悪い文化ですが、彼が悪いコードを書くだけで、あなたのコードはすべて完璧であるため、話す理由はありません。そしてもちろん、彼はコメントにランダムな単語を書くだけで、読書に時間を費やす必要はありません。

コードにコメントを記述する最も重要な理由の1つは、この方法で実装された理由を説明することです。解決策に同意しない場合もありますが、問題を考慮する必要があります。

著者との議論により、コードが削除された、または終了しなかったものの歴史的な残骸であることも明らかになる可能性があるため、削除しても安全です。

0
h22