優れた開発者は、Railsコードをいつどのようにリファクタリングするかを知っています

公開: 2022-03-11

大規模なリファクタリング:なぜあなたはそのようなことをするのですか?

壊れていない場合は、修正しないでください。

よく知られている言葉ですが、私たちが知っているように、人間の技術の進歩のほとんどは、壊れていないものを修正することを決めた人々によってなされました。 特にソフトウェア業界では、私たちが行っていることのほとんどは、壊れていないものを修正することであると主張することができます。

機能の修正、UIの改善、速度とメモリの効率の改善、機能の追加:これらはすべて、実行する価値があるかどうかを簡単に確認できるアクティビティです。経験豊富なRails開発者は、これらに時間を費やすことに賛成または反対します。 ただし、ほとんどの場合、標準のリファクタリング、特に大規模なコードのリファクタリングという灰色の領域に分類されるアクティビティがあります。

大規模リファクタリングという用語は、説明する価値があります。 用語が少し曖昧であるため、正確に「大規模」と見なすことができるものはケースごとに異なりますが、少数のクラスまたは複数のサブシステムに大きな影響を与えるものはすべて、そのインターフェイスは「大規模」であると考えています。 。」 一方、単一のクラスのインターフェースの背後に隠されたままのRailsリファクタリングは、間違いなく「小さい」でしょう。 もちろん、その間にはたくさんの灰色の領域があります。 最後に、あなたの腸を信頼してください、あなたがそれをすることを恐れるならば、それはおそらく「大きい」でしょう。

定義上、リファクタリングは目に見える機能を生成せず、クライアントに表示できるものも、成果物も生成しません。 せいぜい、速度とメモリ使用量がわずかに改善される可能性がありますが、それが主な目標ではありません。 主な目標はあなたが満足しているコードであると言う人もいるかもしれません。 しかし、コードベース全体に大きな影響を与えるような方法でコードを再配置しているため、すべての地獄が崩壊し、問題が発生する可能性があります。 もちろん、それは私たちが言及した恐怖が由来するところです。 コードベースに新しい人を紹介し、彼らが特別に編成されたコードについて質問した後、次のような回答をしたことがありますか。

ええ、これは当時は理にかなっていたレガシーコードですが、仕様が変更され、修正するにはコストがかかりすぎますか?

たぶん、あなたは彼らに非常に真剣な表情を与え、それをそのままにして、それに触れないように彼らに言ったのかもしれません。

Railsコードをリファクタリングする方法を計画するときは、開始するために複雑なチャートが必要になる場合があります。

「なぜそれをやりたいのか」という質問。 自然なものであり、それを行うのと同じくらい重要かもしれません...

「なぜそれをやりたいのか」という質問。 これは自然なことであり、リファクタリングに高価な時間を費やすことができるように説得しなければならない人が他にもいることが多いため、リファクタリングプロセスと同じくらい重要な場合があります。 それでは、それを実行したい場合と、得られるメリットについて考えてみましょう。

パフォーマンスの向上

保守性の観点からコードの現在の構成に満足していますが、それでもパフォーマンスに問題が発生しています。 現在の設定方法を最適化するのは非常に難しく、変更は非常に脆弱です。

ここで行うことは1つだけであり、それはそれを広範囲にプロファイルすることです。 ベンチマークを実行し、どれだけの利益が得られるかを見積もり、それが具体的な利益にどのように変換されるかを見積もります。 提案されたコードリファクタリングは価値がないことに気付く場合もあります。 また、ケースを裏付けるためのコールドハードデータがある場合もあります。

アーキテクチャの改善

アーキテクチャは大丈夫ですが、やや時代遅れかもしれません。あるいは、コードベースのその部分に触れるたびにうんざりするほどひどいのかもしれません。 正常に高速に動作しますが、新しい機能を追加するのは面倒です。 その苦痛には、リファクタリングのビジネス価値があります。 「苦痛」はまた、リファクタリングプロセスが新しい機能を追加するのに時間がかかることを意味します。

そして、得られるべき利益があります。 提案された大規模なリファクタリングがある場合とない場合のいくつかのサンプル機能のコスト/メリットを見積もります。 同様の違いが、システムの開発中、現在および将来にわたってシステムのその部分に影響を与える今後の機能のほとんどに適用されることを説明します。 彼らはしばしばソフトウェア開発にあるのであなたの見積もりは間違っているかもしれませんが、それらの比率はおそらく球場にあるでしょう。

それを最新のものにする

時々、コードは最初はうまく書かれています。 あなたはそれに非常に満足しています。 高速で、メモリ効率が高く、保守が容易で、仕様に適合しています。 最初は。 しかし、その後、仕様が変更されたり、ビジネス目標が変化したり、エンドユーザーについて最初の仮定を無効にする新しいことを学んだりします。 コードはまだうまく機能し、あなたはまだそれについてかなり満足していますが、最終製品のコンテキストでそれを見ると何かが厄介です。 物事がわずかに間違ったサブシステムに配置されているか、プロパティが間違ったクラスに配置されているか、一部の名前が意味をなさなくなっている可能性があります。 彼らは現在、ビジネス用語ではまったく異なる名前の役割を果たしています。 ただし、関連する作業は他の例と同じ規模になるため、大規模なRailsのリファクタリングを正当化することは依然として非常に困難ですが、メリットはそれほど具体的ではありません。 あなたがそれについて考えるとき、それを維持することさえそれほど難しいことではありません。 いくつかのことは実際には別のものであることを覚えておく必要があります。 Aは実際にはBを意味し、AのプロパティYは実際にはCに関連していることを覚えておく必要があります。

そして、ここに本当のメリットがあります。 神経心理学の分野では、私たちの短期記憶または作業記憶がわずか7 +/- 2の要素を保持できることを示唆する多くの実験があり、そのうちの1つがスターンバーグ実験です。 私たちが主題を研究するとき、私たちは基本的な要素から始めます、そして最初に、私たちがより高いレベルの概念について考えるとき、私たちはそれらの定義について考えなければなりません。 たとえば、「saltedSHA256password」という単純な用語について考えてみます。 最初に、「salted」と「SHA256」のワーキングメモリ定義、さらには「ハッシュ関数」の定義を保持する必要があります。 しかし、この用語を完全に理解すると、直感的に理解できるため、1つのメモリスロットしか占有しません。 これが、高レベルの概念について推論できるようにするために、低レベルの概念を完全に理解する必要がある理由の1つです。 私たちのプロジェクトに固有の用語や定義についても同じことが言えます。 しかし、コードについて議論するたびに本当の意味への翻訳を覚えておく必要がある場合、その翻訳はそれらの貴重なワーキングメモリスロットのもう1つを占めています。 それは認知的負荷を生み出し、私たちのコードのロジックを通して推論することを難しくします。 同様に、推論が難しい場合は、重要なポイントを見落とし、バグを導入する可能性が高くなります。

そして、より明白な副作用を忘れないようにしましょう。 クライアントや正しいビジネス用語に精通している人と変更について話し合うときは、混乱する可能性があります。 チームに参加する新しい人々は、ビジネス用語とコード内の対応する用語の両方に精通している必要があります。

これらの理由は非常に説得力があり、多くの場合、リファクタリングのコストを正当化すると思います。 それでも、注意してください。リファクタリングの時期と方法を決定するために最善の判断を下さなければならないエッジケースがたくさんある可能性があります。

最終的に、大規模なリファクタリングは、私たちの多くが新しいプロジェクトの開始を楽しんでいるのと同じ理由で優れています。 あなたはその空白のソースファイルを見ると、勇敢な新しい世界があなたの心の中を渦巻くようになります。 今回は正しく実行します。コードはエレガントで、美しくレイアウトされているだけでなく、高速で堅牢で簡単に拡張できます。そして最も重要なことは、毎日作業するのが楽しいことです。 小規模および大規模のリファクタリングにより、その感覚を取り戻し、古いコードベースに新しい命を吹き込み、その技術的負債を返済することができます。

最後に、リファクタリングが特定の新機能の実装を容易にする計画によって推進されている場合に最適です。 その場合、リファクタリングはより集中的になり、リファクタリングに費やされた時間の多くは、機能自体のより迅速な実装によってすぐに取り戻されます。

準備

触れる可能性のあるコードベースのすべての領域で、テストカバレッジが非常に良好であることを確認してください。 十分にカバーされていない特定の部分を見つけた場合は、最初にテストカバレッジを上げるために時間を費やしてください。 テストがまったくない場合は、最初にそれらのテストの作成に時間を費やす必要があります。 適切なテストスイートを作成できない場合は、受け入れテストに集中してできるだけ多くのテストを作成し、リファクタリング中に単体テストを作成するようにしてください。 理論的には、適切なテストカバレッジがなくてもコードリファクタリングを実行できますが、多くの手動テストを実行する必要があり、頻繁に実行する必要があります。 時間がかかり、エラーが発生しやすくなります。 最終的に、テストカバレッジが十分でない場合、大規模なRailsリファクタリングを実行するコストが非常に高くなる可能性があるため、残念ながら、まったく実行しないことを検討する必要があります。 私の意見では、それは十分に強調されていない自動テストの利点です。 自動テストを使用すると、リファクタリングを頻繁に、さらに重要なことに、より大胆に行うことができます。

テストカバレッジが良好であることを確認したら、変更のマッピングを開始します。 最初は、コーディングを行うべきではありません。 関連するすべての変更を大まかにマップし、コードベースを介してすべての結果を追跡し、それらすべてに関する知識を頭の中にロードする必要があります。 あなたの目標は、何かを変更する理由と、それがコードベースで果たす役割を正確に理解することです。 変更が必要なように見える、または何かが壊れたという理由だけで物事を変更することに遭遇した場合、これで問題が解決するように思われると、死んだ路地にたどり着く可能性があります。 新しいコードは機能しているように見えますが、正しくありません。今では、行ったすべての変更を思い出すことができません。 この時点で、大規模なコードリファクタリングで行った作業を放棄する必要があるかもしれません。そして、本質的にあなたはあなたの時間を無駄にしました。 したがって、時間をかけてコードを調べて、これから行う各変更の影響を理解してください。 最終的には見事に報われるでしょう。

リファクタリングプロセスの支援が必要になります。 あなたは何か他のものを好むかもしれませんが、私は単純な白紙とペンが好きです。 まず、紙の左上に作成したい最初の変更を書き込みます。 次に、変更の影響を受けるすべての場所を探し始め、最初の変更の下にそれらを書き留めます。 ここであなたの判断を使うことが重要です。 最終的には紙のメモや図があなた自身のためにあるので、あなたの記憶に最も適したスタイルを選んでください。 その下に箇条書きのマークが付いた短いコードスニペットと、それに直接(完全な矢印)または間接的に(破線の矢印)依存していることを示す他のそのようなメモにつながる多くの矢印を書きます。 また、コードベースで気付いた特定のことを思い出させるために、矢印に省略記号で注釈を付けます。 計画された変更を実行している間、これらのメモに戻るのは数日だけであることを忘れないでください。非常に短くて不可解なリマインダーを使用しても、スペースの使用量が少なく、紙に簡単にレイアウトできます。 。 Railsのリファクタリングの数か月後に机を掃除していたときに、それらの紙の1つを見つけました。 それは完全にぎこちないものでした、私はそれが狂った誰かによって書かれたかもしれないことを除いて、その紙の何が意味するのか全くわかりませんでした。 しかし、私が問題に取り組んでいる間、一枚の紙が不可欠であったことを私は知っています。 また、すべての変更を書き出す必要があるとは思わないでください。 それらをグループ化し、別の方法で詳細を追跡できます。 たとえば、メインの論文では、「Abのすべての出現箇所をCdに名前変更する」必要があることに注意してください。その後、いくつかの異なる方法で詳細を追跡できます。 それらをすべて別の紙に書き出すか、すべての出現箇所をもう一度グローバル検索するように計画するか、選択したエディターで変更を開く必要があるすべてのソースファイルをそのままにしておくことができます。変更のマッピングが完了したら、それらに戻るように心に留めておきます。

最初の変更の結果を計画するとき、それが大規模であるという性質により、おそらくそれ自体がさらに結果をもたらす追加の変更を特定するでしょう。 それらについても分析を繰り返し、依存するすべての変更に注意します。 変更のサイズに応じて、同じ紙に書き出すか、新しい空白の紙を選ぶことができます。 変更をマッピングする際に試みる非常に重要なことは、分岐する変更を実際に停止できる境界を特定することです。 リファクタリングを、意味のある、丸められた、変更の最小のセットに制限する必要があります。 停止して残りをそのままにしておくことができるポイントを見つけた場合は、概念的に他の変更に関連している場合でも、リファクタリングする必要があると思われる場合でもそうしてください。 このラウンドのコードリファクタリングを終了し、徹底的にテストし、デプロイして、さらに戻ってきます。 変更のサイズを管理しやすくするために、これらのポイントを積極的に探す必要があります。 もちろん、いつものように、判断を下します。 かなり頻繁に、いくつかのプロキシクラスを追加してインターフェイスの変換を少し行うことで、リファクタリングプロセスを中断できるようになりました。 「自然な停止」(つまり、プロキシコードがほとんど必要ない)ポイントになるまでリファクタリングを少し進めるのと同じくらいの作業になることに気付いたとき、私はそれらの実装を開始しました。 その後、バックトラックし、最後の変更を元に戻し、リファクタリングしました。 未知の領域をマッピングするように聞こえる場合は、領域マップが2次元しかないことを除けば、そう感じているからです。

実行

リファクタリングの準備ができたら、計画を実行します。 集中力が高まっていることを確認し、気を散らすことのない環境を確保してください。 この時点でインターネット接続が完全にオフになることもあります。 問題は、準備が整っていれば、隣の紙に良いメモを書き、集中力を高めることです。 多くの場合、この方法で変更を非常に速く進めることができます。 理論的には、ほとんどの作業は準備中に事前に行われました。

実際にコードをリファクタリングしたら、非常に具体的なことを行い、悪いコードのように見える可能性のある奇妙なコードに注意してください。 おそらくそれらは悪いコードですが、実際には、本番環境のバグを調査しているときに発見された奇妙なコーナーケースを処理していることがよくあります。 時間の経過とともに、ほとんどのRailsコードは、奇妙なコーナーケースのバグを処理する「髪の毛」または「いぼ」を成長させます。たとえば、IE6に必要な奇妙な応答コードや、奇妙なタイミングのバグを処理する条件などです。 それらは全体像にとって重要ではありませんが、それでも重要な詳細です。 理想的には、最初にそれらをカバーしようとしない場合でも、それらはユニットテストで明示的にカバーされます。 私はかつて、中規模のアプリケーションをRails2からRails3に移植するという任務を負っていました。コードには非常に精通していましたが、少し面倒で、考慮すべき変更がたくさんあったため、再実装を選択しました。 実際には、それは賢明な動きとは言えないため、実際の再実装ではありませんでしたが、私は空白のRails 3アプリから始め、説明したプロセスを大まかに使用して、古いアプリの垂直スライスを新しいアプリにリファクタリングしました。 垂直スライスを終了するたびに、古いRailsコードを調べ、各行を調べて、新しいコードに対応するものがあることを再確認しました。 私は基本的に、古いコードの「髪の毛」をすべて選び、新しいコードベースに複製していました。 結局、新しいコードベースでは、すべてのコーナーケースに対処しました。

十分な頻度で手動テストを実行するようにしてください。 これにより、リファクタリングプロセスで自然な「中断」を探す必要が生じ、システムの一部をテストできるようになるだけでなく、プロセスで中断することを予期していなかったものを中断しなかったという確信が得られます。 。

急いでください

Railsコードのリファクタリングが完了したら、最後にもう一度すべての変更を確認してください。 差分全体を見て、それを調べます。 リファクタリングの開始時に、現在の知識がなかったために見逃した微妙なことに気付くことがよくあります。 これは、大規模なリファクタリングの優れた利点です。特に、最初に記述しなかった場合は、コード編成のより明確なイメージを得ることができます。

可能であれば、同僚にもレビューしてもらいます。 彼は、コードベースのその正確な部分に特に精通している必要はありませんが、プロジェクトとそのコードに一般的に精通している必要があります。 変化に新たな目を向けることは、大いに役立つ可能性があります。 他の開発者にそれらを見てもらうことが絶対にできない場合は、自分自身のふりをする必要があります。 ぐっすりとお休みいただき、心を込めてご確認ください。

QAが不足している場合は、その帽子も着用する必要があります。 繰り返しになりますが、休憩を取り、コードから離れてから、戻って手動テストを実行してください。 たくさんの工具を使って雑然とした電気配線キャビネットに入って、すべてを整理し、場合によっては材料を切断して再配線するのと同じことをしたので、通常よりも少し注意を払う必要があります。

最後に、計画されているすべての変更を考慮して、作業の成果を楽しんでください。これにより、よりクリーンで実装が容易になります。

いつやらないの?

プロジェクトコードを最新かつ高品質に保つために大規模なリファクタリングを定期的に実行することには多くの利点がありますが、それでも非常にコストのかかる操作です。 推奨されない場合もあります。

テストカバレッジが不十分です

前述のように、テストカバレッジが非常に悪い場合は大きな問題になる可能性があります。 独自の判断を使用してください。ただし、短期的には、新しい機能に取り組み、できるだけ多くのローカライズされた小規模なリファクタリングを実行しながら、カバレッジを上げることに集中する方がよい場合があります。 思い切ってコードベースの大部分をソートすることにした場合、これは非常に役立ちます。

リファクタリングは新機能によって推進されておらず、コードベースは長い間変更されていません

わざと「コードベースは変わらない」と言う代わりに過去形を使いました。 経験から判断すると(そして経験によって、私は何度も間違っていることを意味します)、コードベースの特定の部分をいつ変更する必要があるかについての予測に頼ることはほとんどできません。 ですから、次善の策を実行してください。過去に目を向け、過去が繰り返されると想定してください。 何かが長い間変更されていない場合は、おそらく今すぐ変更する必要はありません。 その変化がやってくるのを待って、何か他のことに取り組んでください。

あなたは時間に追われています

メンテナンスはプロジェクトのライフサイクルの中で最も費用のかかる部分であり、リファクタリングにより費用が安くなります。 将来のメンテナンスをより安くするために、技術的負債を減らすためにリファクタリングを使用することは絶対に必要です。 そうしないと、新しい機能を追加するのにますます費用がかかるという悪循環に陥る危険があります。 それがなぜ悪いのかが自明であることを願っています。

とは言うものの、大規模なリファクタリングは、所要時間に関しては非常に予測不可能であり、途中で行うべきではありません。 内部的または外部的な理由で時間に追われていて、その時間枠内に終了できるかどうかわからない場合は、リファクタリングを中止する必要があります。 圧力とストレス、特に時間誘導型は、集中力のレベルを低下させます。これは、大規模なリファクタリングに絶対に必要です。 チームからより多くの「バイイン」を取得して、その時間を確保し、時間のある期間のカレンダーを調べてください。 それが継続的な時間である必要はありません。 もちろん、他にも解決すべき問題がありますが、それらの休憩は1日か2日を超えてはなりません。 もしそうなら、あなたはあなた自身の計画を思い出さなければならないでしょう。なぜならあなたはあなたがコードベースについて学んだこととあなたが止めた場所を正確に忘れ始めるからです。

結論

いくつかの有用なガイドラインを提供し、特定の機会に大規模なリファクタリングを実行することの利点をあえて納得させていただきたいと思います。 トピックは非常に曖昧であり、もちろんここで述べられていることは明確な真実ではなく、詳細はプロジェクトごとに異なります。 私はアドバイスを提供しようとしましたが、それは私の意見では一般的に当てはまりますが、いつものように、あなたの特定のケースを考慮し、あなた自身の経験を使ってその特定の課題に適応します。 頑張ってリファクタリング!