web-dev-qa-db-ja.com

無関係なバグを修正するときに、ソフトウェアにバグを引き起こさないようにするにはどうすればよいですか?

私はソフトウェアインターンであり、修正するバグとソフトウェアに追加する機能が割り当てられています。機能を追加すると、すべてうまくいきます。私の問題はバグを修正することです。私は非常に大きなコードベース(数百万行に及ぶ)と不十分なドキュメント(コメント、大量のマジックナンバー、異なるクラス間の密結合など)に取り組んでいます。提供される唯一のドキュメントは、8ページ程度のWord文書です。しかし、私はまだバグ修正でより良い仕事ができるように感じています。

私が遭遇した状況の例を挙げましょう。

  • 特定のタイプのオブジェクト(コンピュータグラフィックス)のエクステント計算に関して修正するバグが割り当てられました
  • バグの問題と、その原因を見つけました。プログラムがデータ構造(連続配列)に3Dデカルトポイントを表すメモリを挿入したため、一見するとそれはありません(したがって、このポイントはエクステント計算で使用されます)。
  • バグは確かにこれが原因でした。ただし、別のコード(別のクラス)は、ポインター演算を使用してこのポイントを取得し、それを別の機能に使用して、ソフトウェアの特定の機能が有効になっているときにマウスをこのポイントの近くにスナップしました。しかし、ポイントを削除したので、割り当てられたバグを修正しましたが、ソフトウェアに別のバグを引き起こしました。

このようなことが起こらないようにするにはどうすればよいですか?どうすれば改善できますか?私が見逃しているプロセスはありますか?

49
Jason

あなたはこれらの種類の欠陥に対して単独で責任を負うことはできません。あなたは人間であり、全体として大きなシステムについて考えることは不可能です。

「リグレッションバグ」(システムの変更時に意図せずに作成されたバグ)を防ぐには、次のようにします。

  • 自動回帰テストの包括的なスイートを開発します。うまくいけば、あなたのシステムはテストの大規模なスイートを持っています。システムでバグが発見されるたびに、バグを再現する自動テストを作成する必要があります。次に、バグを修正します。システムが後退した場合、「後退テスト」が中断し、開発者に通知されます。
  • コードを検査します。バグを修正するときはいつでも、変更した関数の使用法を調べて、導入した重大な変更を特定することをお勧めします
  • もっとテストを書いてください。コードを検査するときに、自動テストでカバーされていないコードを特定した場合は、これを追加する良い機会です。
  • システムに慣れ、ソフトウェアを長時間使用します。練習は完璧を作る。開発者がまったく新しいシステムにバグを持ち込まないことや、それらがインターンである場合は誰も期待しません。私たちはすべてそれをやった。
73
Samuel

これを完全に排除することはできません。影響を減らし、可能性を減らすためのいくつかの手順を次に示します。

  1. 変更または修正する機能またはバグの単体テストと回帰テストを追加します。プロジェクトにテストフレームワークやハーネスを設定していない場合は、プロセスの一部となるように設定します。これは時間の経過とともに物事を改善します。
  2. コードがより疎結合になるようにコード設計を改善します。つまり、DRYを犠牲にしても、SRPに従うことになります。特に、OOPシステムでは、コードとデータが同じクラスにあり、データは常にプライベートである必要があります。これが意味をなさない場合、問題のドメインとあるクラスのデータ構造の変更が別のクラスの動作に影響を与えることは可能ではありません。
  3. 提案された修正について同僚に相談してください。これはコードレビューの形をとることがありますが、回帰をキャッチできない場合があります。コードレビューは通常、バグをキャッチするためのものではありません。当然の結果として、チームの他のメンバーが行っている変更について知っていることを確認し、システムの他の部分がコードに依存している可能性があるものについてフィードバックを提供する時間を与えます。あなたがチームの上級者であっても、これを行ってください。

お役に立てれば。

10
RibaldEddie

他の答えには多くの良い提案があります。私はそれらを付け加えます:あなたの組織はおそらく管理レベルで問題を抱えています。

  • 経営陣は、借金の処理よりも新機能の作業を優先する場合があります。あなたが言及したバグは、過去の貧弱な慣行が今日バグのないソフトウェアを開発することをより高価にしている証拠です。このようなデータポイントを収集して、対処されていない債務が原因で進行中の問題が発生していること、および新しい機能が追加されず、既存の債務が減少する「品質マイルストーン」をスケジュールすることが賢明であることを経営陣に通知します。

  • 経営陣はおそらく問題の範囲を知らないでしょう。実際のバグを見つける静的分析ツールは高価ですが、ユーザーデータを失うソフトウェアを出荷したため、市場で失敗するよりもはるかに安価です。優れた静的アナライザーは、何万もの未解決のバグがあること、それらがどこにあるのか、それらを修正する方法を教えてくれます。これにより、問題のサイズ、および問題の軽減速度を把握できます。

9
Eric Lippert

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

-G.K.チェスターソン

ここで提起された他のすべてのアイデアと同様に、もう1つの重要なことは、バグが導入されたことを知ることです方法と理由。チームがGitまたは同様のソース管理システムを使用していると仮定すると、git blameやGitHubのBlameボタンなどのツールがこれに役立ちます。バグの原因となったコード行を特定したら、ソース管理ツールを使用して、いつ、誰が、どのような変更が加えられたかを調べます。

バグを修正するときはいつでも、組織のバグトラッカーにnarrativeと書いて、バグがどのようにして発生したかを考えます。 「ボブがテストのために96行目をコメントアウトし、誤ってコミット5ab314cでコミットしたように見え、Frobnicatorの展開を急いでいたためマージされなかったようです。その週に適切に内容を確認します。 "それでも、バグを解決している時点でgoodであることがわかります。これは、壊れたコードに正当な理由がないことを確信しているためです。現状のままで、自信を持って修正できます。しかし、git blameを掘り下げると、変更しようとしている「明らかに壊れた」コード行が、修正を意図したコミットで導入されたことがわかる他のいくつかのバグあなたが持っていたとは思わなかったが、突然、「修正」が何らかのダメージを与え、何か賢いことをする必要があることに気づいた。

もちろん、他の多くの回答がここに記しているように、前の開発者が古いバグを素朴に再導入した場合に失敗するテストを残しておけばいいでしょう-取り壊そうとしているフェンスがある種の自動警告あなたが見ることができない目的。ただし、コードベースの以前の開発者が作成したテストの数を制御することはできません。コードの履歴を掘り下げることは、利用可能な数少ないテクニックの1つです既存のバグを修正する時点で物事を壊すリスクを減らすため。

5
Mark Amery

私は壊れやすいレガシーアプリケーションにも取り組んでいます。変更の最大の目標は、「以前は機能していたものを壊さないこと」です。 (2番目の目標は、ユーザーがジョブを完了できる必要があることです。少し不格好になったり、回避策を実行する必要がある場合でも、どの時点でも完全に行き詰まることはありません。)

  1. Michael Feathers著の本「レガシーコードを効果的に使用する」があります。これは非常に便利ですが、自動テストの追加については100%だと警告します。技術的には常に可能です(そして、彼は多くのトリッキーな状況でそれを行う方法を説明しています)が、ビジネスの観点からは常に機能するとは限りません。この本は、物事がどのように悪化するかについて面白いサイドバーがいくつかあるので、役に立ちます。
  2. 変化点に触れるすべての退屈な手動検査。 fooとbarの2つの関数を変更した場合、fooとbarの単語について、コードベース全体で全文検索を実行します(すべてチェックアウトされていますか?)。 (注、grepなどの検索ツールのパワーユーザーになる必要があります。)この検索を簡略化しないでください... Javaファイルだけで検索しないでください、関数はフロントエンドJavaScriptで開始されたものからのリフレクションを使用して呼び出される可能性があるため...単なる例です。変更を呼び出すすべてのものが完全にテストされるまで、バックトラッキングを続けます。
  3. 検索手法を逆にして、変更点を呼び出さずに同じことを行うアプリケーション内の点を見つけてみてください。コピーして貼り付けたコードまたは双方向で同じことを検索し、変更する必要があるすべてのポイントを変更したことを確認します。 (その後、前のステップを繰り返します。)

恐ろしく聞こえるかもしれませんが、レガシーアプリケーションを維持するのが難しいこれらのアプリケーションは、重要なことを行っているため、誰かが本当に動作するように本当に必要としているため、通常、まだまだ存在し、更新されています。これからいくつかの満足を得るようにしてください。

1
user3067860

「(別のクラスの)別のコードは、ポインタ演算を使用してこのポイントを取得し、それを別の機能に使用して、ソフトウェアの特定の機能が有効になっているときにマウスをこのポイントの近くにスナップさせました。」

ええ、あなたは高メンテナンスのコードベースを手渡されました。この場合、明らかなインターフェースを持つオブジェクト間の通常の結合ではなく、いくつかの「マジック」が発生していたようです。もちろん、変更されるとすぐに機能が停止しました。

これには手品はありません。多くのソフトウェア開発手法は、変更の影響を特定できるようにするために、複雑さの管理に焦点を当てています-ただし、これらはここでは追跡されていません。これは、以前の開発者とプロジェクトを監督する先輩のせいです。

1
pjc50

コードをリファクタリングして、特定のメンバーへの直接アクセスを非表示にし、アクセサーを追加できます。これにより、メンバーへの直接アクセスに依存するすべてのコードが壊れるので、それを使用するすべての場所がわかります。

おまけとして、メンバーを再配置し、以前の場所をゴミで埋めます。これにより、ポインター演算を介してメンバーにアクセスするコードが壊れます。

開発者は、コードを早期にブレークし、頻繁にブレークする必要があります。 「99.99%以上の時間で機能する」は、「まったく機能しない」よりもバグ修正の方がはるかに悪い。 (もちろん、量産コードの場合、「早期に中断し、頻繁に中断する」ことは望ましくありません)

0
Calin Ceteras