web-dev-qa-db-ja.com

既存の複雑なコードベースを理解する

重複の可能性:
不慣れな、構造的に健全でないコードに機能を追加する最も効果的な方法は何ですか?

これまで、私が取り組んできたのはJava最初から構築するプロジェクト(ほとんどがコースプロジェクトと趣味のもの)ですが、今では約46000の巨大なコードベースに遭遇しました。約200のクラスにまたがる行に加えて、約10の依存ライブラリがあります。

問題は、私が他の誰かのコードを使用したことがないだけでなく、そのような巨大なコードベースを使用したことがないということです。

コードベースを完全に理解し、既存のデザインの改善を提案するように求められます。今、ちょっと行き詰まっています。 コードの一部から始めて、コードの他の部分に到達するまでに、前の部分の動作の詳細を失っています。

私は本当にあなたがこの問題に対処するためにあなたの提案と経験を使うことができました。コードをよりよく理解するためにクラスの詳細をどのように進め/文書化するのですか?

編集:これは大学レベルの研究プロジェクトです。学生は数年かけてそれを開発してきましたが、悪い点として、これを書いた学生はすでに卒業しています。既存のドキュメンテーション(UMLのものではなく、Javadocの形式のみ)は、アプリケーションの設計を理解する上で役立ちますが、役に立ちません。

37
Ankit

コードを操作することは、それを学ぶための最良の方法です。巨大なコードベースを学ぶための「明白な」方法はありませんが、できることはいくつかあります。

コードを変更して小さな機能を追加し、その方法でコードベースをリファクタリングおよび学習することができます。コードベースの小さなローカライズされたセクションに焦点を合わせ、少しずつ学習してみてください。

単体テストがある場合は、それらを調査して、コードがどのように機能するかをよりよく理解することができます。テストがない場合、テストを記述することは、コードの一部を理解するための素晴らしい方法です。当然のことながら、ユニットテストは1つのユニットのみをテストすることになっているため、そのユニットのみに集中して、他の依存関係を模擬することができます。これにより、コードベースの知識が増えるまで、一度に1つのコードユニットに集中して学習することができます。

もう1つの優れた手法は、デバッガーでコードを実行し、ユースケースの実行全体をステップ実行することです。これにより、システムがどのように応答し、いくつかの機能を実行するかをよく理解できます。

これが一晩で起こることを期待しないでください。コードベースの複雑さを理解するには少し時間がかかりますが、ユニットテストを作成/理解することは、それらを学ぶ素晴らしい方法です。

25
Oleksi

私はたくさんのコードレビューをしなければならず、それには大きくて複雑なコードベースが含まれています。

可能な場合は、コンポーネントの マインドマップ を構築しようとしています。

私がプロジェクトで作業または貢献する必要がある場合、私が行う非常に効果的なことは、最初は明確ではなかった領域にコメントを追加(または変更)することです。

最後に、プロジェクトの簡単な「アーキテクチャ」概要を、将来のコードレビュー担当者のために構築する必要があります。このステップでは、マインドマップが非常に役立ちます。

18
user2567
  • 大学一年の時も似たような立場でした。その経験に基づいて、あなたはあなたが噛むことができるよりもあなたが少し多いと思います。

以下はあなたの人生を楽にするかもしれませんが、あなたは多くの仕事をする必要があります。

  • 問題領域を完全に理解します。ユースケース図などでビジネスおよびモデルアプリケーションの機能について説明します。あなたにはそれを行うための無制限の時間とリソースがあると思います。

  • 問題をより小さなタスクに分解する必要があります。自分で目標を設定し、目標に向かって取り組みます。例えば。認証プロセスを理解したい。それを実行すると思われるコードを見つけ、それから作業を開始してください。システム全体でハングアップする場合は、私の意見ではどこにも到達できないでしょう。

  • 以前にこれに取り組んだことのある人の連絡先を取得します。彼らがまだ同じ会社にいる場合は、システムに慣れてから一緒に作業してください。

  • コードを調べて、コードの内容を文書化すると、非常に長い時間がかかります。あなたがそれを買う余裕があるとは思えません。

  • 新しい環境にシステムを展開します。うまくいけば、それは壊れて、何が起こっているのかを確認するために多くのコードをデバッグする必要があります。

  • ユースケースがあると想定して、コードベースを介してデバッグを開始します。

  • ビジネスでコードベースの確認を求められる理由を明確にしてください。確かにそれは高価で無意味な運動ではないので、彼らには正当な理由があるはずです。これは、どこを見ればよいかがわかるでしょう。

  • コードベースを分析し、それが密結合であるかどうか、重複コードがどれだけあるかを通知するツールがあります。これは便利かもしれませんが、プロジェクトの開始時ではありません。

  • 単体テストがあるかどうかを確認します。それらの実行を開始して、システムが実行できる機能を確認します。

覚えておくべきこと:

  • 簡単なことではないので、打ちのめさないでください。

  • わからないことがたくさんありますので、それらにこだわって前進しないでください。

  • これは通常かなり動機づけられないので、進捗状況をメモすることが重要です。私は通常、これを目の前のホワイトボードに置いています。

  • 大きな数字に固執しないでください。はい、あなたは自己防衛でそれを使うことができ、200のクラスはおそらくかなりの数の責任に等しいですが、これはあなたの人生を簡単にしませんので、解決策を探しましょう:)

10
CodeART

ソフトウェア開発の世界へようこそ。ほとんどの場合、次のことを行います。

  • 既存の大規模なコードベースで作業します。
  • 限られたドキュメントになります。
  • オリジナルのクリエイターはもういなくなっています。
  • システムがエンドツーエンドであることは誰にもわかりません。

ほとんどの場合、あなたは投げ込まれ、それを理解することが期待されます。確かに、道に沿って人々が助けてくれるはずですが、結局のところ、物事を理解するのはあなた次第です。これは物事の意味は人によって違い、本当に違いを生むスキルだと思います。

ここで他の人がそれを行う方法の私の経験/観察から、私はうまくいくと思います:

  • 最初にシステムの骨組みを見つけ、次に詳細の入力を開始する方法でそれに取り組みます。
  • コードのある場所から別の場所に移動する方法を示す呼び出しツリーを書き留めます。多くの場合、これはシステム全体ではなく特定の領域の1つにすぎません。
  • コードの理解に役立つツールの使用。 Slickeditの検索機能と参照機能を利用しています。また、Eclipseにはこのようなツールがいくつかあると思います。
  • システムの動作に関する実験。多くの場合、フローを理解するのに役立つように、さまざまなものをテストする高レベルのトレース/ printfsを使用します。

最後に、最も役立つのはRepetitionです。あなたが物事を通過する回数が多いほど、何が何であるかを知ることがより簡単になります。業界では、誰かがその知識を効果的であると考えるまでに数か月かかるとよく言われます。これは学習曲線のためです。

最後に、私自身の経験では、大きな新しいコードベースを理解するにはいくつかの段階があるように感じます。

  • ステージ1:ベアリングを取得する。この段階では、物事がどこにあるかを学び、慣れることがすべてです。小さな変更を成功させることができると期待するべきだと私は言うでしょう。
  • ステージ2:物事がどこにあるかを理解し、運が良ければ中規模の変更を行うことができます。この時点では、物事を成し遂げる正しいにはかなりの助けが必要になると思いますが、物事を成し遂げることができます。
  • ステージ3:コードを非常によく理解しており、複雑で大きな変更を加えることができる。このレベルで最も一般的な問題は、システムレベルの理解が不完全なためにバグが発生することです。それでもあなたは非常に効果的です。
  • ステージ4:完全に理解し、システムを自由に曲げることができます。実際にここに来る人はほとんどいないと思います。
6
barrem23

ドキュメントにこのようなものが存在しない限り、コードの静的な構造と動的な動作の感触を得るために、UMLクラスとシーケンス図を描くのが好きです。これらの図は正式である必要はありませんが、「これはまた何をしているのですか?」瞬間。

それらがすでに存在する場合は、単体テストを見て、すべてがどのように統合されるかを確認することも好きです。何もない場合は、それらを記述することも、コードベースにアクセスするための良い方法です。

編集:私はすべてのコードの図を描画するのではなく、現在取り組んでいる部分と複雑な部分(複雑な継承階層など)だけを描画します。

5
ftr

データ構造-に焦点を当てます。これがゴムが道路にぶつかる場所です。

データ定義(データベーススキーマ、ファイル形式など)は、アプリケーションロジックよりも(コードの)行数がはるかに少なくなりますが、アプリケーションが何をしているかを実際に理解するには非常に重要です。データ構造と関係を学びます。その後、プログラムの残りの部分に頭を巻き付ける方がはるかに簡単です。

データレイアウトを知ることで、コードの読み取り中に方向感覚が得られます。それは、森の中をナビゲートしているときに地図とコンパスを持っているようなものです。

「フローチャートを表示してテーブルを非表示にしてください。そうすれば、引き続き神秘化されます。テーブルを表示してください。通常、フローチャートは必要ありません。それは明らかです。」-Fred Brooks、The Mythical Man-Month

PS。データは永遠に存続します。プログラム(およびコンピューター)は一時的なものです。

3
Maglob

私は1年以上にわたって複雑なコードベースに取り組んできました。私の洞察があなたを助けることができるかどうか見てください:

あなたの洞察は正しく、コードの別の部分に到達するまでに、前の部分を忘れています。それは決して終わることのないサイクルになることができます。ここで取り上げるべき重要な教訓は、すべての部品が適切に機能しないと製品は機能しないということです。ひとつの部品が故障しても、製品は動作しません。別の角度から見てください。ある部分を劇的に改善しても、製品の機能が向上しない可能性があります。それが、ここでの主な目標です。

最初は、開発者にならないでください。テスターに​​なってください。

部分的に理解しようとしないでください。すべてのパーツが一緒になっているときの製品全体とその動作を理解します。実稼働環境(つまり、非開発環境-デバッグポイントなし)から、製品をテストします。次に、すべてのテスターと同じように、直面する問題をバグトラッカーに記録します。重大度と優先度を割り当てます。このソフトウェアはかなり前から存在していたので、すでにバグトラッカーが作成されているかどうかを確認します。すでにある場合は、ラッキーです。それらに追加して、時間をかけて既存の各ファイルを確認してください。このサイクルの最後に、ユーザーの視点(間違いなく見逃してはなりません)とQAの視点から製品を理解します。当然のことながら、コード行によってバグが修正されることにも気づくかもしれません。それをコード化した人たちは、当時は本当に必要がなかったため、修正しませんでした。

2番目のステップ:デザイナーケープを着用

製品をいくつかの部分に分割します(文字通り、またはユーザーの便宜に従ってではなく、それらがどのように連携するかに従って)。今までの仕事かもしれませんし、既存の知識が役立つかもしれません。次に、それらが互いに、および10個の依存ライブラリとどのように連携するかを理解してください。次に、追跡されたバグごとに、コードのエンティティを識別するメモを書きます(例:この変更には、クラスX、Y、Zなどの変更が含まれます)。おそらく、このステップの終わりまでに、現在のアーキテクチャの問題点と改善できる点についてのFEWヒントが得られます。

次に、現在のアーキテクチャ/デザインで十分かどうかを判断し、ソフトウェアの改善を続けることができますOR製品がより優れたデザインまたは既存のデザインの変更を必要とする場合。

カードの家

また、複雑な製品には多くのコードが含まれているため、いくつかのことを取り上げて調整または改善する立場にない場合があります。これは、システム全体が絡み合ってクラスの1つに変更を加えることは、カードの家の1枚のカードの位置を変更することと同じであり、どちらの端が壊れるかわからないためです。私の経験では、これは本当です。私は一部を選択し、コードを改善しましたが、コードの他の部分との契約を認識していなかったため、コードを放棄して間違いを犯しました。ですから、部品を理解しようとするのではなく、それが全体であることを理解してください。

懸念を優先する

改善しようとしていることを覚えておく必要があります。

製品をより高速にしたいですか?

もちろんそうです。しかし、それは懸念の主なものでしょうか?遅いですか? 「はい」の場合、パフォーマンス基準を作成し、ボトルネックを特定して、それらの部分を改善します。もう一度テストしてください。

使いやすさを向上させたいですか?

それから、それはほとんどAPI/UI側です。

セキュリティを強化しますか?

そして、それはあなたが探究すべき境界です。

私は3つの例しか提供していませんが、他にも探すべきことがたくさんあります。

最新かつ最良のドキュメント

私は、最新かつ最良のドキュメントがコード自体であるという投稿の1つでここを読みました。今日かなりの量のドキュメントを作成しても、それはしばらくしてからの歴史です。したがって、コードは最新のドキュメントです。そのため、コードを閲覧するときはいつでも、コメントに理解を書き込んでください。コードベースを渡すときは、コメントだけに依存しないように注意してください!

3
Sundeep

this に関連しています。しかし、Lines of codeコンポーネントが追加されています。ちなみに、私の経験では、46000行のコードはmediumサイズのアプリケーションです。

1
NWS

いくつかの戦略:

  • 継承図を作成します。 これを行うツール がありますが、手で描くことも役に立ちます。

  • 200クラスのうち、おそらく1ダースほどが本当に重要であることに気付くでしょう。これらは、継承ツリーのルートに近いクラスになります。その他はテーマのバリエーションであり、スーパークラスが何をするかが分かれば、これらすべてのリーフクラスがどのように機能するかがかなりよくわかります。

  • メモをとります。ここではwikiを使用すると役立ちます。最終的には、ノートの一部を現在よりも優れたドキュメントに変えることができます。

  • 作成されたオブジェクトの図と、それらが互いにどのように関連しているかを描きます。

  • デバッガーを使用して、理解できないセクションをステップスルーします。

  • プロファイリングツールを使用して、コードで何が行われているのかをあまり気にすることなく、パフォーマンスの問題を見つけます。パフォーマンスメトリックを記録しながら、アプリケーションをそのペースで実行してください。アプリケーションがほとんどの時間を費やしている場所を知ることで、a)どのように機能するかについての洞察を得ることができ、b)すべての詳細を理解する必要なく改善点を提案することができます。

1
Caleb

tl; dr:ドキュメントの作成、テストの作成。理解を作りに変える。

完全版:

少し前まで、私も会社で初めて既存のプロジェクトに取り組む必要があり、迅速に対応する必要がありました。

私はすべてのプロジェクトが常に非常に深刻な問題に苦しんでいることを発見しました:不十分なドキュメントとテストこれはプロジェクトの成功という点で非常に悪いです。

  • バス係数 。プロジェクトに取り組む割り当てられた1人の主任開発者を除いて、スタッフが実際にどのように機能するかを理解している人はほとんどいません。

  • 参照できるアーキテクチャの一般的な図がないため、コミュニケーションは困難です。十分な情報に基づいたアーキテクチャ改善の決定は困難です。

  • 人に大変です。新人はプログラムを理解するのに苦労しています。

だから私のために働いたのは、理解メイキングに変えることです。

  • 新しいプロジェクトで私がしなければならない特定のタスクがある場合、私はシステムについて最も多くの情報を収集できるようにそれについて取り組みます。

  • タスクがなく、全体的な理解が必要な場合(これはあなたのケースのようです)、ドキュメントを作成します。

一般的な理解を得るためのドキュメントの作成

脳に複雑な抽象的なシステムを理解させるのは難しいことがわかりました。ただし、何かを作成している場合、同じ理解が容易に得られます(おそらく「フロー」の概念に関連している可能性があります)。

それは一種のハックです。ほとんどの人は高品質の製品を作ることを楽しみ、仕事に誇りを持っているので、ドキュメントを最終製品にすることができます。新しいツール(たとえば、Sphinxのようなドキュメントシステム)を学び、理解したことをターゲットユーザーに伝える方法の難しい問題を解決します。自分や他の人に見せるための最終製品ができます。人々は後で感謝します。これは大きな動機です。

メタドキュメントを忘れないでください。ドキュメントが古くなって他の人が更新を煩わせないため、ドキュメントが古くなるとイライラします。開発ワークフロー(バージョン管理、テスト、ドキュメント、コーディングスタイルガイドなど)を簡潔に文書化します。

テストを書く

特定のタスク(新機能やバグ修正)が手元にある場合は、最初にそのテストを記述します。

テストを書くには、テストスイートがどのように機能するかを理解し、それを実行する必要があります。これは通常明白ではないので、簡単に文書化します。テストスイートには、単体テストや統合テストなど、多くの独自の問題が混在していることが多いため、後で修正する方法についてのアイデアも書き留めておきます。テストスイートが機能しない場合や、すべてのテストが失敗する場合があるため、修正します。テストスイートがまったくない場合があります。これは、テストスイートのセットアップ方法を学ぶ機会です。

それから私はテストを書きます。大規模なタスクの場合は、まず高レベルの「統合」テストを記述します。ユニットテストを作成するときの最初のタスクは、必要な機能を実装するユニットを特定することです。これにより、アーキテクチャをかなり理解できます。その後、機能を実装するコードを書くのは簡単です。

テストを書くことは、機能を実装したり、修正するよりも大きな仕事になるかもしれません。しかし、それは後でやりがいがあります。しっかりとしたテストスイートができたら、あなた(または他の開発者)はランダムなことを改善してテストに合格するかどうかを確認することで多くを学ぶことができます。

1