コードを最初から書き直さないことについてのJoelSpolskyの記事を思い出します。彼の主張を要約すると、コードは錆びることはなく、多くのメンテナンスリリースの後できれいに見えないかもしれませんが、機能すれば機能します。エンドユーザーは、コードがどれほど美しいかを気にしません。
あなたはここで記事を読むことができます: あなたが決してしてはいけないこと
私は最近プロジェクトを引き継ぎました、そして彼らのコードを調べた後、それはかなりひどいです。以前に作成したプロトタイプをすぐに思いつき、本番環境では使用しないことを明示しました。しかしもちろん、人々は耳を傾けません。
コードはWebサイトとして構築されており、関心の分離、単体テスト、およびコードの重複はどこにもありません。 App_Codeで多数のクラスを数えない限り、データレイヤーも、実際のビジネスロジックもありません。
私は、既存のコードを維持し、バグ修正リリースといくつかのマイナーな機能リリースを行う必要がある一方で、テスト駆動開発を念頭に置き、関心の分離を明確にして、すぐに書き直しを開始することを利害関係者に推奨しました。 。 ASP.NETMVCルートを使用することを考えています。
もちろん、私の唯一の懸念は、最初から書き直すのにかかる時間の長さです。それは完全に複雑ではなく、メンバーシップなどを備えたミルWebアプリケーションのかなりの実行です。
同様の問題に遭遇した人はいますか?あなたが取った特定のステップはありますか?
更新:
だから..私は何をすることにしたのですか?私はマットのアプローチを採用し、多くの領域をリファクタリングすることにしました。
すべてのADO呼び出しを含む非常に単純なデータアクセス層を作成し、これらの呼び出しを実行するためのSqlHelperオブジェクトを作成しました。
よりクリーンなロギングを実装しました
ソリューション。これははるかに簡潔です。
私はもうこのプロジェクト[資金調達、政治、何とか何とか]に取り組んでいませんが、いくつかのプロジェクトがどれほど悪いものになるか、そして1人の開発者が物事をよりクリーンで読みやすく、時間の経過とともに小さな増分ステップでより良くフラットになります。
これらすべての問題が発生したからといって、引き続き問題が発生するわけではありません。たとえば、新しいデータレイヤーの恩恵を受ける可能性のある特定のバグ修正をシステムで行っていることに気付いた場合は、新しいデータレイヤーを作成します。サイト全体が使用されていないからといって、使用を開始できないわけではありません。バグ修正中に必要に応じてリファクタリングします。また、コードを変更する前に、コードが何をしているのかを正確に理解してください。
コードの重複に問題がありますか?重複したコードのバグを修正する必要があるときに、中央の場所にあるクラスまたはユーティリティライブラリにそれを引き出します。
そして、他のレスポンダーがすでに述べたように、今すぐテストを書き始めてください。コードが結合されているように聞こえる場合は難しいかもしれませんが、おそらくどこかから始めることができます。
動作するコードを書き直す正当な理由はありません。ただし、すでにバグを修正している場合は、コードの特定の部分を「より良い」設計で作り直すことができない理由はありません。
本 ソフトウェアエンジニアリングの事実と誤謬 はこの事実を述べています:「再利用されたコードの変更は特にエラーが発生しやすいです。コンポーネントの20から25パーセント以上が改訂される場合、それはより効率的であり、ゼロから書き直すのに効果的です。」数値は、この主題について実施されたいくつかの統計的研究から得られたものです。コードベースの品質によって数値が異なる場合があると思いますので、このステートメントを考慮して、最初から書き直す方が効率的で効果的だと思います。
Joelの記事は本当にそれをすべて述べています。
基本的に決して。
Joelが指摘しているように、最初からやりすぎると、多くのことを失うことになります。おそらくあなたが思っているよりもずっと時間がかかるでしょう、そして最終結果は何ですか?基本的に同じことをする何か。それで、それを行うためのビジネスケースは何ですか?
それは重要なポイントです。最初から何かを書くにはお金がかかります。そのお金をどのように回収しますか?多くのプログラマーは、コードが気に入らないという理由だけでこの点を無視します。正当化される場合もあれば、そうでない場合もあります。
私はそのようなアプリケーションを持っていました、そして書き直しは非常にやりがいがありました。ただし、「改善」の罠を回避するようにしてください。
すべてを書き直すと、新しい機能を追加したり、根性がなかった長年の問題を修正したりするのは非常に魅力的です。これはフィーチャークリープにつながる可能性があり、また書き換えに必要な時間を大幅に延長する可能性があります。
正確に何を変更し、何を書き換えるだけかを必ず決めてください-事前に。
私は、以前のコードのリバースエンジニアリングビジネスルールを含むコードを最初から書き直した小さな専任チームの一員でした。元のアプリケーションは、C++で記述されたWebサービス(定期的なクラッシュと深刻なメモリリークを伴う)とASP.Net 1.0 Webアプリケーションであり、代替はC#2.0asmxベースのWebサービスとAjaxを使用したASP.Net2.0Webアプリケーションでした。それはチームがしたことのいくつかを言い、経営陣に説明しました
プロジェクトは大成功でした。それは非常に安定していて、はるかに優れたパフォーマンスを示しました。その後、新しい機能を追加する方が簡単でした。したがって、適切なリソースと状況があれば、コードの書き換えは正常に実行できると思います。
私はその記事にいくぶん同意しません。ほとんどの場合、ジョエルは正しいですが、時々(まれにでも)書き直しが良い考えであることを示す反例があります。例えば。、
Joelの主張は、主に既存のバージョンでかなりよく書かれたコードに基づいており、後知恵で改善できると思います。どうしても、継承したコードが本当にひどい場合は、プッシュして書き直してください。恐ろしいものがいくつかあります。それがまったく許容でき、適度にうまく機能する場合は、新しいものをゆっくりと段階的に導入します。
唯一の準正当な理由が思い浮かびます:政治。
私はコードベースを最初から書き直さなければならず、それは政治と関係がありました。基本的に、コードベースを管理していた以前のコーダーは、恥ずかしすぎて、採用されたばかりの新しいチームにソースコードをリリースできませんでした。彼女は、コードに対するすべての批判は人としての彼女の批判であると感じ、その結果、彼女は強制されたときにのみコードを私たちの残りの人にリリースしました。彼女はソースリポジトリへの管理アクセス権を持つ唯一の人物であり、すべてのソースを解放するように求められるたびに、彼女はコードの知識をすべて終了して家に帰ると脅迫しています。
このコードベースは15年以上前のものであり、さまざまなスタイルのさまざまな人々からの畳み込みとゆがみがあります。それらのスタイルのどれも、少なくとも、彼女が私たちにリリースした小さな部分では、明らかにコメントや仕様を含んでいませんでした。
利用可能なコードの一部と期限だけで、私は完全な書き直しを余儀なくされました。その結果、深刻な遅延を引き起こしたと主張されたために怒鳴られましたが、私は頭を下げて、議論するのではなく、それをやり遂げました。
政治は大きな苦痛になる可能性があります。
私の答えは:最初から書き直すできるだけ頻繁に。
私はキャリアのほとんどを、マネージャーから「ロックスター」と見なされた若くて経験の浅いプログラマーによって書かれた「プログラム」と丁寧に呼ばれる糞の山を継承して過ごしてきました。これらは一般的に修正不可能であり、最初から書き直す場合に比べて、足を引きずり続けるために10倍の労力を費やすことになります。
しかし、ownの作業を定期的に書き直すことで、大きなメリットも得られました。すべての書き直しは、物事を異なる方法で、潜在的にはより良くするチャンスであり、古いバージョンの少なくとも一部の部分を再利用できるはずです。
そうは言っても、すべての書き直しが良い考えであるとは限りません。たとえば、WindowsVista。
私はまさにこの状況にありましたが、完全に書き直すのではなく、リファクタリングプロセスを通じて物事を変えるために取り組みました。私が遭遇した問題は、私が扱っていたコードの非常に複雑なことでした-すべてがif-casesと複雑な正規表現に基づいた恐ろしい特別なケース主導の開発の多くのページが約10年間の計画外の成長と拡張に重ねられました。
私の目的は、機能ごとにリファクタリングして、同じ入力に対して同じ出力を提供する一方で、ボンネットの下ではるかにクリーンかつスムーズに動作して、将来の成長を促進し、パフォーマンスを向上させることでした。一般的な解決策はクリーンで迅速でしたが、システムによって解析されているドキュメントのあいまいな特殊なケースが表示され始め、私の素敵なクリーンコードがちょうどの出力を生成するため、コードの修正ジョブはますます困難で複雑になりましたオリジナルの機能とは少し異なります(これはWebページであるため、空白の量が異なると、古いIEバージョン)であらゆる種類のレイアウトの問題が発生する可能性があります。
再加工されたコードが使用されたかどうかはわかりません-完全に統合される前に会社を辞めました-しかし、私はそれを疑っています。 1500個の「if」ステートメントと3行の正規表現で同じ仕事ができるのに、なぜ20行のコードを使用するのでしょうか。
完全な書き直しの1つの危険は、あなたの仕事が絶えず進行中であるということです。あなたは収益に貢献していないコストです。吸うコードはお金を稼いでいるコードです。
しかし、既存のコードを一度に1つずつ修正すると、マネーマシンがどのように機能するかを知っているのはあなたです。
ある時点で、あなたはあなたの損失を削減しなければなりません。このコードベースを継承したばかりの場合は、意図しない結果をもたらす変更を加える可能性があり、テストが不足しているため、それらを見つけることはほぼ不可能です。
少なくとも、すぐにテストを書き始めてください。
ゼロから完全に書き直すのではなく、単体テストを導入しながら、コードベースのリファクタリングを小さなステップで開始する必要があります。例えば
私はむしろ少しずつ物事を行いたいと思います。たとえば、それらの領域で作業するときにデータモデルを使用してデータベースへのバックエンドを作成し(つまり、最初にユーザーログイン、次にユーザー管理など)、既存のフロントを微調整します-新しいバックエンドを使用するためのエンド(インターフェイス駆動型なので、テストを追加することもできます)。これにより、関心の分離を追加しながら、最初から再開発しても複製できない可能性のある文書化されていない微調整や動作を既存のコードに保持できます。
しばらくすると、コードベースの約60%を移行して、新しいバックエンドを使用するようになります。作業は公式プロジェクトではなく、メンテナンスだけなので、他の40を実行するための開発時間について議論するのに適した立場になります。 %、そしてそれが行われると、既存のフロントエンドクラスのサイズと複雑さが大幅に削減されます。完全に移行されると、新しいビューを実装する時間があれば、新しいバックエンドモデルとコントローラーコンポーネントを再利用できるようになります。
これは2つのことに依存すると思います。
1)レガシーコードベースの基盤となる設計にどの程度の欠陥があるか
2)書き換えにかかる時間。
1)私が働いている会社は、ひどく設計されたコードベースを持っていましたが、一度に1ビットずつリファクタリングできなかったため、リファクタリングが非常に困難でした。主な問題は、個々のクラスや関数ではなく、全体的な設計にありました。したがって、リファクタリングのアプローチは非常に困難です。 (全体的な設計は良好でしたが、たとえば、個々の関数が300行の長さであり、分割する必要がある場合、リファクタリングは理にかなっています)。
2)多くのコードと非常に複雑なにもかかわらず、プロセスを実行します。私たちのエンジンはそれほど多くのことをしていませんでした。したがって、書き直しはそれほど長くはありませんでした。管理者は、数十万行のコードの機能を非常に短時間で再構築できることに気付いていない場合があります。
これをCTO(小さな会社)に説明しようとしましたが、それでも書き直しは危険だと思っていたので、私と同僚は約4週間でエンジンの基本機能を書き直しました。その後、CTOに提示し、最終的に納得しました。
さて、基本的な機能を構築するのに6か月かかるとしたら、議論はあまりありません。
技術仕様を書くことから始めます。コードがそれほどひどい場合は、実際の仕様も存在しないに違いありません。したがって、包括的で詳細な仕様を作成します。最初から書き直したい場合は、とにかく仕様を作成する必要があるため、時間は十分な投資です。機能に関するすべての詳細を含めるように注意してください。アプリの実際の動作を調査できるので、これは簡単なはずです。改善の提案を自由に含めてください。ただし、現在の動作のすべての詳細を必ずキャプチャしてください。
調査の一環として、システムの自動テストを作成して、予想される動作を調査および文書化することを検討してください。単体テストではなく、ブラックボックス/統合テストに焦点を合わせます(これが醜い場合、コードではおそらく許可されません)。
Whenこの仕様を使用している場合、アプリが実際には第一印象よりもはるかに複雑であることに気付く可能性があり、最初から書き直すことを再検討します。代わりに徐々にリファクタリングすることにした場合は、仕様とテストが大いに役立ちます。しかし、それでも先に進んで書き直すことにした場合は、これから作業するための優れた仕様と、作業が完了したときに通知する一連の統合テストがあります。
経済学には、次のような矛盾する声明もあります。
埋没費用を考慮しないでください
ウィキペディアによると、埋没費用( https://en.wikipedia.org/wiki/Sunk_cost ):
経済学およびビジネスの意思決定において、埋没費用はすでに発生した費用であり、回収することはできません。
埋没費用が政治的圧力または個人的なエゴと結びついている場合(それが避けられないか、すぐに制御できない場合でも、彼らが悪い決定をしたか、結果を適切に監視しなかったことを認めるマネージャーになりたいですか?) 約束の拡大( https://en.wikipedia.org/wiki/Escalation_of_commitment )と呼ばれる状況につながります。これは次のように定義されます。
個人またはグループが、何らかの決定、行動、および投資からますます否定的な結果に直面したときに、コースを変更するのではなく継続する行動のパターン。これは不合理ですが、以前に行われた決定および行動と一致しています。
これはコードにどのように適用されますか?
現在ソフトウェア開発者としてかなり長いキャリアを持っているので、私が見つけた共通のスレッドの1つは、挑戦的または醜いコードベースに直面したとき(2年前の私たちのものであっても)、私たちの最初の本能は投げたいということです古い、醜いコードを取り出して、最初から書き直します。使い慣れたコードベースの場合、これは通常、プロジェクトを開始したときよりもプロジェクトの落とし穴とビジネス要件に精通しているという事実から生まれているため、(おそらく無意識のうちに)機会を切望しています。私たちの過去の罪を完全に消すことによってそれらを直すこと。それがなじみのないコードベースである場合、元の開発者が直面する課題を単純化しすぎて、「全体像」のアーキテクチャレベルの考え方を優先して「細部」を覆い隠し、予算と時間枠を吹き飛ばす傾向があります。コードが本来解決することを意図していたビジネスケースの複雑な特徴についての理解の欠如。
次に、技術的負債の概念全体があります。これは、金融負債と同様に、コードベースが技術的に破産するまで発生します。バグのトラブルシューティング、消火、および過度に挑戦的な改善に、ますます多くの時間とリソースが投資されており、前進は費用がかかり、困難で、危険にさらされています。プロジェクトは、欠陥のために、そして生産の問題を修正するためにプロジェクト作業から引き離されるために、ますます長くかかります。数時間後、「インシデント」は、まれなブリップではなく、期待される動作になり始めます。一歩下がって将来の生産性(および生活の質)を向上させるために正しいことを始める代わりに、期限を守るために技術的負債をますます追加することを余儀なくされる立場にあります-技術的には取るのと同等です別のカードで最小限の支払いを行うために、クレジットカードでキャッシングサービスを利用します。
とはいえ、可能な限り書き直す必要があるという意味でも、動作するコードの書き直しを絶対に避けるべきでもありません。どちらの極端も潜在的に無駄であり、後者はコミットメントのエスカレーションにつながる傾向があります(すべてのコストではを意味し、たとえコストを完全に無視してもこれらのコストは、メリットを完全に上回ります)。発生する必要があるのは、コードを書き直すことと段階的な改善を行うことのコストと利点を客観的に評価することです。課題は、その決定を適切に行うための専門知識と客観性の両方を備えた人を見つけることです。私たちの開発者にとって、書き直しは、くだらないレガシーコードベースで作業するよりもはるかに面白くて魅力的である傾向があるため、一般的に書き直しに偏っています。書き直しは、知覚できる即時の利益がほとんどない未知のものを課すため、ビジネスマネジャーは反対方向に偏る傾向があります。その結果、通常、実際の決定が行われず、デフォルトで、ある状況で方向シフトが必要になるまで(または開発者がコードをひそかに書き直して、通常はスパンキングを取得するまで)、既存のコードに時間をダンプし続けます。
私は、醜いものの、いくらか救済可能なコードベースに取り組んできました。それらは確立された慣行や標準に従わず、パターンを使用せず、きれいではありませんでしたが、意図した機能を適度にうまく実行し、アプリケーションの予想される寿命の予想される将来のニーズを満たすように変更できるほど柔軟でした。魅力的ではありませんが、機会が生じたときに段階的な改善を行いながら、このコードを存続させることは完全に受け入れられました。それ以外の方法で行うと、見た目がきれいになる以外にほとんどメリットがありません。 これを書き直すべきかの質問が発生するほとんどのコードはこのカテゴリに分類され、チームのジュニア開発者にそれを説明していることに気づきます。 {ここにwhizzbangフレームワークを挿入}でYetAnotherLineOfBusinessAppを書き直すのはとても楽しいです。それは必要でも望ましいものでもありません。これを改善する方法がいくつかあります...
また、絶望的なコードベースにも取り組んできました。これらは、そもそもほとんど起動されなかったアプリケーションであり、通常は予定よりはるかに遅れており、機能が低下した状態でした。それらは、元の開発者以外の誰もがコードが最終的に何をするのかを理解する機会がないような方法で書かれました。私はこれを「読み取り専用」コードと呼びます。一度書き込まれると、変更を試みると、原因不明のシステムで判読できない障害が発生する可能性があり、obj_85
という巧妙な名前の変数に実際に何が起こっているかを現在の開発者に教育する以外の目的を果たさない、大規模なモノリシックコード構造のパニック状態の大規模な書き換えにつながります。実行が行1,209に達するまでに、DoEverythingAndMakeCoffee(...)
メソッドのどこかにあるif... else...
、switch
、およびforeach...
ステートメントの深さ7レベルにネストされています。このコードをリファクタリングしようとすると失敗します。あなたがたどるすべてのパスは、別の課題につながり、さらに多くのパス、そして分岐するパス、そして前のパスに戻ります。単一のクラスの2週間のヘッドダウンリファクタリングの後、おそらくより適切にカプセル化されていることに気付きます。新しいコードは古いコードとほぼ同じくらい奇抜で難読化されており、リファクタリングしたものの本来の意図が完全に不明確であったため、おそらくさらに多くのバグが含まれています。機能を完全に複製したかどうかはわかりません。コードベースの変換はほぼ不可能であり、変数の名前を変更したり、適切な型を使用したりすると、意図しない副作用が指数関数的に発生するため、進行状況はほとんどありません。
上記のようなコードベースを改善しようとすることは、無駄の練習です。通常、リファクタリングは80%の書き換えになり、最終的な結果は80%の改善にはほど遠いものになります。非常に一貫性のないものになってしまい、新しいコードには、レガシーコードとの相互運用性のために実装する必要のある多くの妥協点があります(新しいコードとの相互運用に必要なレガシーコードのため、その半分は不要でした)後でとにかくリファクタリングされます)。たどることができる道は2つだけです...アプリケーションが自重で崩壊する前にアプリケーションが非推奨になる(または別のプロジェクトに転送される)ことを期待しながら、「修正」と変更をハッキングすることによって技術的負債を増やし続けます。誰かがビジネス上の決定を下し、完全な書き直しを行うリスクを負います。私はこれらのオプションの両方が嫌いです。なぜなら、それは通常、重大な何かが失敗するか、プロジェクトが予定よりも大幅に遅れるまで待つことを意味し、その後、おそらく生きているはずのない何かを呼吸するために、次の3か月の夜と週末を過ごすことですそもそも。
それで、あなたはどのように決めるのですか?
次のような古い格言があります。
悪いコードのようなものはありません。あなたが望むことをするコードとそうでないコードだけがあります。
いつ書き直すかを知るための鍵はそこにあります。システムは現在あなたが望むことをしますか?答えが「はい」の場合、ゆっくりですが、着実な改善が最善の策です。答えが「いいえ」の場合、書き直しが必要です。
Joelのエッセイに戻って、彼は厄介なコードについて話しますが、信頼性が高く、期待される価値を提供するソフトウェアです。代わりに、主要なバグでいっぱいの信頼できないコードがあり、それがすべてのユースケースをカバーしていなかった場合。そこにあるはずだったものがまだ機能しないか、単に欠けているものがありました。この場合、そこから成長するすべての小さな毛はバグ修正ではなく、癌です。