私は長年ソフトウェア開発者として働いています。より多くの開発者が製品の開発に関与するようになると、プロジェクトがより複雑で保守不能になるのは私の経験です。
開発の特定の段階にあるソフトウェアは、「ハッカー」と「ハッカー」を獲得する傾向があるようです。特に、アーキテクチャを定義するチームメンバーの誰もが会社で働いていない場合にそうです。
何かを変更しなければならない開発者がアーキテクチャの全体像を把握するのに苦労しているのは、私にとって苛立たしいことです。したがって、元のアーキテクチャーに反する方法で問題を修正または変更する傾向があります。その結果、コードはますます複雑になり、理解がさらに難しくなります。
何年にもわたってソースコードを実際に維持できるようにする方法について、何か役立つアドバイスはありますか?
コードの腐敗を防ぐ唯一の真の解決策は、適切にコーディングすることです!
うまくコーディングする方法は別の問題です。あなたが一人で働く優秀なプログラマーであっても、それは十分に難しいです。異機種混合のチームでは、さらに困難になります。アウトソーシング(サブ)プロジェクトで...祈るだけです。
通常の良い習慣が役立つかもしれません:
ユニットテスト はあなたの友達です。それらを実装すると、低結合が強制されます。また、プログラムの「ハック」部分を簡単に識別してリファクタリングできることも意味します。また、変更をすばやくテストして、既存の機能を損なうことがないことを確認できます。これにより、開発者はコードを複製するのではなく、既存のメソッドを変更して、物事を壊すことを恐れるようになります。
ユニットテストは、コードの追加のドキュメントとしても機能し、各部分の動作の概要を示します。広範な単体テストを使用すると、プログラマは、変更を加えて既存のクラス/メソッドを使用するためにプログラムのアーキテクチャ全体を知る必要はありません。
ナイスな副作用として、単体テストはバグの数を減らすことも期待しています。
ここの誰もがすぐに言及しますcode rot、そして私はこれを完全に理解して同意しますが、それでもここでより大きな全体像と大きな問題を見逃しています。コードの腐敗は発生するだけではありません。さらに、単体テストは優れていると述べられていますが、実際には問題を解決していません。単体テストの対象範囲は広く、コードにバグはほとんどありませんが、コードとデザインが腐っています。
あなたは、プロジェクトに取り組んでいる開発者が機能を実装することが困難であり、全体的なアーキテクチャの全体像を見落としているため、システムにハッキングを実装していると述べました。設計を実施して影響を与える技術的リーダーシップはどこにありますか?このプロセスのコードレビューはどこにありますか?
あなたは実際にコードの腐敗に苦しんでいませんが、あなたはteam rotに苦しんでいます。実際のところ、ソフトウェアの元の作成者がチームにいなくても問題はありません。既存のチームのテクニカルリードが基礎となる設計を完全かつ真に理解し、テクニカルリードとしての役割に長けていれば、これは問題ではありません。
私たちにできることはいくつかあります:
1人に全体的なアーキテクチャの責任を与えます。その人物を選択するときは、彼らがアーキテクチャを開発および維持するためのビジョンとスキルを持っていること、および他の開発者がアーキテクチャをフォローするのを支援する影響力と権限を持っていることを確認してください。その人は、経営陣から信頼され、同僚から尊敬される熟練した開発者である必要があります。
すべての開発者がアーキテクチャの所有権を取得する文化を作成します。すべての開発者は、アーキテクチャの整合性を開発および維持するプロセスに関与する必要があります。
アーキテクチャの決定が容易に伝達される環境を開発します。現在のプロジェクトのコンテキストだけでなく、一般的にも、デザインとアーキテクチャについて話すように人々に勧めてください。
コーディングのベストプラクティスにより、コードからアーキテクチャが見やすくなります。リファクタリング、コメントのコメント、ユニットテストの開発などに時間をかけてください。命名規則やクリーンなコーディングプラクティスのようなものは、アーキテクチャのコミュニケーションに役立つため、必要なチームとして独自の標準を開発し、それに従うために時間を費やすこと。
必要なすべてのドキュメントが明確、簡潔、最新でアクセス可能であることを確認してください。高レベルと低レベルの両方のアーキテクチャ図を公開し(それらを壁に固定すると役立つ場合があります)、公に保守できるようにします。
最後に(自然な完璧主義者として)アーキテクチャの整合性は価値ある願望であることを認識する必要がありますが、連携して機能する製品を実際に出荷できるチームを構築するなど、もっと重要なことがある場合もあります。
私がこの問題に取り組む方法は、ルートでそれを切り取ることです:
私の説明では、Microsoft/ 。NET の用語を使用しますが、すべてのプラットフォーム/ツールボックスに適用できます。
単体テストの作成中に、リファクタリングによって腐敗したコードをクリーンアップします。次の場合はいつでも、触れるすべてのコードに(この)設計負債を支払います。
次の方法により、テストファースト開発サイクルを大幅にスピードアップします。
次の方法で、コードをリファクタリングして(内部の凝集性が高いユニットの)低結合を使用します。
有機的成長は良好です。大きな先行設計は悪いです。
現在の設計に精通しているリーダーがいます。そうでない場合は、知識が得られるまでプロジェクトのコードを読んでください。
リファクタリングの本を読んでください。
簡単な答え:できません。
それが、あなたがsmallおよびsimpleソフトウェアを書くことを目指すべき理由です。簡単ではない。
一見複雑に見える問題について、できるだけ簡単で簡潔な方法でそれを定義できるほど十分に考えている場合にのみ可能です。
本当に大きく複雑な問題の解決策は、多くの場合、小さくて単純なモジュールを基に構築することで解決できます。
つまり、他の人が指摘したように、単純さと疎結合が重要な要素です。
それが不可能または実現可能でない場合は、おそらく調査を行っています(既知の単純なソリューションがない、または既知のソリューションがまったくない複雑な問題)。研究が保守可能な製品を直接生産することを期待しないでください。それが研究の目的ではありません。
1999年から継続的に開発が続けられている製品のコードベースに取り組んでいるので、ご想像のとおり、今のところはかなり複雑です。私たちのコードベースにおけるハッキングの最大の原因は、これまで ASP Classic から ASP.NET に移植する必要が何度もあったことです。ADO ADO.NETへ、ポストバックから Ajax へ、UIライブラリの切り替え、コーディング標準など.
全体として、コードベースを保守可能に保つための合理的な仕事をしました。これに貢献した主なことは次のとおりです。
1)定数リファクタリング-ハックされているか理解しにくいコードに触れる必要がある場合は、クリーンアップに余分な時間をかけることが期待されますそれを上にして、そうするためにスケジュールに余裕があります。ユニットテストを使用すると、回帰に対してより簡単にテストできるため、これをはるかに恐ろしくすることができます。
2)きれいな開発環境を維持する-使用されなくなったコードの削除には注意してください。バックアップコピー/作業コピー/実験コードを残さないでください。プロジェクトディレクトリに存在します。
3)プロジェクトの存続期間中の一貫したコーディング標準-それに直面して、コーディング標準に対する私たちの見解は、時間とともに進化します。新しい標準に準拠するために戻ってすべてのコードを改造する時間がない限り、プロジェクトの存続期間を通じて開始したコーディング標準を使用することをお勧めします。あなたが終わったのは素晴らしいことです ハンガリー語表記 今、そのレッスンを新しいプロジェクトに適用し、その新しいプロジェクトの途中で切り替えるだけではありません。
質問にプロジェクト管理のタグを付けたので、いくつかの非コードポイントを追加しようとしました:)
ターンオーバーの計画-メンテナンスフェーズに到達するまでに開発チーム全体が姿を消していると想定します-その価値のある開発者が自分のシステムを永久に維持することを望んでいません。時間があれば、すぐに資料の準備を始めましょう。
一貫性/均一性は十分に強調することができません。これは、「独りで行く」という文化を思いとどまらせ、疑問がある場合は、新しい開発者に尋ねることを奨励します。
チームの新しい開発者(レベルに関係なく)がすぐに立ち上がって実行できる可能性が高まるため、メインストリーム(使用するテクノロジ、設計パターン、および標準)を維持してください。
ドキュメント-特にアーキテクチャ-決定が下された理由とコーディング標準。また、リファレンス/メモ/ロードマップをビジネスドメインの文書化に含めてください。ドメインの経験がない開発者に対して、企業のビジネスが何をしているかを説明するのがいかに難しいかおわかりでしょう。
現在の開発チームだけでなく、将来のメンテナンス開発者についても、ルールを明確に説明します。これが、すべてのページに関連する設計およびコーディング標準のドキュメントへのハイパーリンクを配置することを意味する場合は、そうしてください。
アーキテクチャ、特にコードレイヤーが明確に区切られて分離されていることを確認します。これにより、WebフォームのUIを HTML5 - jQuery UI など。これにより、1年程度の寿命が長くなる可能性があります。
高度に調整可能なコードの1つのプロパティは、function Purityです。
純度とは、関数が同じ引数に対して同じ結果を返す必要があることを意味します。つまり、他の関数の副作用に依存すべきではありません。さらに、それ自体に副作用がない場合にも役立ちます。
この特性は、結合/凝集特性よりも簡単に確認できます。あなたはそれを達成するためにあなたの道を行く必要はありません、そして私は個人的にそれをより価値があると思います。
関数が純粋な場合、その型はそれ自体が非常に優れたドキュメントです。さらに、引数/戻り値に関するドキュメントの作成と読み取りは、グローバルな状態(他のスレッドO_Oによってアクセスされる可能性がある)について言及するよりもはるかに簡単です。
保守性を向上させるために純度を広範囲に使用する例として、 [〜#〜] ghc [〜#〜] を確認できます。これは約20年前の大規模なプロジェクトで、大規模なリファクタリングが行われており、新しい主要な機能がまだ導入されています。
最後に、「Keep it simple」ポイントはあまり好きではありません。複雑なものをモデリングしているときは、プログラムを単純に保つことはできません。単純なコンパイラを作成してみてください。生成されたコードは、おそらく非常に遅くなります。もちろん、個々の関数を単純にすることはできます(すべきです)が、プログラム全体が単純になるわけではありません。
これらの回答の多くは最初から大きなチームに焦点を当てているようなので、私はスタートアップを2人の開発チーム(設計者を含めれば3人)の一部と見なします。
言うまでもなく、シンプルなデザインとソリューションが最適ですが、給与を文字どおり支払う人がいる場合は、必ずしも最もエレガントでシンプルで保守可能なソリューションについて考える時間はありません。それを念頭に置いて、私の最初の大きなポイントは:
Documentationコメントではなく、コードはほとんど自己文書化する必要がありますが、設計ドキュメント、クラス階層と依存関係、アーキテクチャのパラダイムなどのようなものです。新規または既存のプログラマがコードベースを理解するのに役立つもの。また、「このクラスをこの機能の要素に追加する」など、最終的にポップアップする奇妙な疑似ライブラリーを文書化すると、ユーザーが機能を書き直すことができなくなるため、役立ちます。
ただし、厳しい時間制限がある場合でも、次の点に注意してください。
ハックを回避とクイックフィックス。クイックフィックスが実際のフィックスでない限り、根本的な問題を何かに理解し、それを修正することは常に優れています。 「これを2分以内に機能させるか、解雇される」というシナリオがない限り、後でコードを修正することはないので、今すぐ修正することをお勧めします。あなたが持っている次のタスクに移動します。
そして、私の個人的なお気に入りのヒントは、引用の詳細ですが、ソースは覚えていません。
"あなたの後に来る人が、あなたの住んでいる場所を知っている殺人精神病者であるかのようにコードを書いてください"
他の回答に加えて、レイヤーをお勧めします。それほど多くはありませんが、異なるタイプのコードを分離するには十分です。
ほとんどのアプリケーションでは内部APIモデルを使用しています。データベースに接続する内部APIがあります。次に [〜#〜] ui [〜#〜] レイヤー。アプリケーションの他の部分を中断したり壊したりせずに、さまざまな人が各レベルで作業できます。
別のアプローチは、すべての人に comp.risks とThe Daily WTFを読んでもらい、悪いデザインと悪いプログラミングの結果、彼らはThe Daily WTFに投稿された自分のコードを見るのを恐れます。
言及されていないが、私が重要だと思う1つの原則は、 オープン/クローズド原則 です。
開発およびテストされたコードは変更しないでください。そのようなコードは封印されています。代わりに、サブクラスを使用して既存のクラスを拡張するか、それらを使用してラッパー decorator クラスを記述するか、適切なパターンを使用します。ただし、作業コードは変更しないでください。
ちょうど私の2セント。
スカウトになる 。あなたが見つけたよりも常にコードをきれいにしておいてください。
壊れたウィンドウ を修正します。これらのコメントはすべて、バージョン3.0を使用している場合の「バージョン2.0での変更」です。
大きなハックがある場合は、チームとしてより良いソリューションを設計し、それを実行します。チームとしてハックを修正できない場合、システムを十分に理解できません。 「大人に助けを求めなさい。」周りの最年長の人々はこれを以前に見たかもしれません。システムの図を描画または抽出してみてください。相互作用図として特にハックなユースケースを描画または抽出してみてください。これはそれを修正しませんが、少なくともあなたはそれを見ることができます。
デザインを特定の方向に押しやった、どのような仮定が正しくなくなったのですか?その混乱の一部の背後に小さなリファクタリングが隠れている可能性があります。
システムがどのように機能するかを説明し(1つの使用例でも)、サブシステムを何度も何度も謝罪しなければならないことに気づいたら、それが問題です。どのような動作がシステムの他の部分を単純化しますか(システムの存在と比較して、実装がどのように見えても関係ありません)。書き直す古典的なサブシステムは、他のすべてのサブシステムをその動作セマンティクスと実装で汚染します。 「ああ、frooサブシステムに値をフィードする前に値をgrozする必要があります。次に、frooから出力を取得するときに、それらのungrozを実行します。ユーザーとストレージから読み取ったときに、すべての値をgrozする必要があります。そして、システムの残りの部分は間違っていますか?これは、2つ以上の異なるgrozificationがある場合に、より刺激的になります。
実際の問題が見えるように、警告を削除してチームとして1週間過ごします。
すべてのコードをコーディング標準に再フォーマットします。
バージョン管理システム がバグ追跡システムに関連付けられていることを確認してください。これは、将来の変更が適切で責任があることを意味し、なぜあなたはうまくいくことができます。
いくつかの考古学を行います。元の設計ドキュメントを見つけて確認します。彼らは、オフィスの隅、廃墟となったオフィススペース、または誰も開かないファイリングキャビネットの古いPCにいる可能性があります。
Wikiで設計ドキュメントを再公開します。これは知識を制度化するのに役立ちます。
リリースとビルドのチェックリストのような手順を記述します。これにより、人々は考える必要がなくなり、問題の解決に集中できます。可能な限りビルドを自動化します。
継続的積分 を試してください。ビルドが失敗するのが早ければ早いほど、プロジェクトがRailsに費やす時間は短くなります。
チームリーダーがこれらのことを行わない場合、それは会社にとって悪いことです。
すべての新しいコードが、測定されたカバレッジで適切な単体テストを確実に取得できるようにしてください。したがって、問題はさらに悪化することはありません。
単体テストされていない古いビットの一部を単体テストしてみてください。これは、変化の恐れを減らすのに役立ちます。
可能であれば、統合および回帰テストを自動化します。少なくともチェックリストがあります。パイロットは賢く、たくさんの支払いを受け、チェックリストを使用します。また、めったにめちゃくちゃになることもありません。
Steve McConnellによるCode Completeを読んでから、もう一度読んでください。それは、最初のプロジェクト設計から1行のコードとその間のすべてに至るまで、優れたソフトウェアを書く聖書のようなものです。私が最も気に入っているのは、何十年にもわたる確かなデータによってバックアップされていることです。これは、次善のコーディングスタイルだけではありません。
これを「ウィンチェスターミステリーハウスエフェクト」と呼んでいます。家のように、それは十分に単純に始まりましたが、長年にわたって、多くの異なる労働者は、誰もそれを本当に理解しなくなった全体的な計画なしに非常に多くの奇妙な機能を追加しました。なぜこの階段はどこにも行かず、なぜそのドアは一方向にしか開かないのですか?知るか?
この影響を制限する方法は、拡張を処理するのに十分な柔軟性がある場所で作られた優れたデザインから始めることです。これについては、すでにいくつかの提案が出されています。
しかし、多くの場合、損傷が既に行われている場所で仕事をすることになります。高価で潜在的にリスクのある再設計と再書き込みを実行せずに優れた設計を行うには遅すぎます。それらの状況では、ある程度それを受け入れながら、カオスを制限する方法を見つけることを試みるのが最善です。すべてが巨大で醜いシングルトンの「マネージャー」クラスを通過しなければならない、またはデータアクセスレイヤーがUIに密に結合されているという設計の感度を苛立たせるかもしれませんが、対処する方法を学んでください。そのフレームワーク内で防御的にコードを記述し、過去のプログラマーの「幽霊」が現れたときに予期しないことを期待してみてください。
私は、技術的ではない問題と(おそらく)実用的なアプローチを取りたいと思っています。
マネージャーが技術的な品質(管理可能なコード、単純なアーキテクチャ、信頼性の高いインフラストラクチャなど)を気にしない場合、プロジェクトを改善することが難しくなります。この場合、上記のマネージャーを教育し、努力を maintainability に「投資」して、 技術的負債 に取り組むよう説得する必要があります。
あなたがそれらの本にあるコード品質で夢を見るなら、あなたはこれについて心配している上司も必要です。
または、単に「フランケンシュタインプロジェクト」を飼いならしたい場合は、これらが私のヒントです。
私の経験では、プログラミングは創発的というよりはエントロピー的です(少なくとも一般的な命令型構造パラダイムでは)。人々が「ただ働く」ためのコードを書くとき、その組織を失う傾向があります。現在、コードの整理には時間がかかります。時々、コードを機能させるだけではありません。
機能の実装とバグ修正に加えて、コードのクリーンアップに時間をかけてください。
コードのリファクタリング と ユニットテスト は完全に問題ありません。しかし、この長期実行プロジェクトはハッキングに駆り立てられているため、経営陣は腐敗をきれいにするために足を踏み入れていません。チームはハックを導入する必要があります。誰かをトレーニングして問題/リクエストを分析するために十分なリソースを割り当てていないからです。
長期実行プロジェクトを維持することは、個々の開発者と同じくらいプロジェクトマネージャーの責任です。
人々はそれが好きだからハックを導入しません。彼らは状況によって強制されます。
多数の回答のどれも明白なことを強調していないことに私は驚いた:ソフトウェアを多数の小さな独立したライブラリで構成する。多くの小さなライブラリを使用すると、大きくて複雑なソフトウェアを構築できます。要件が変更された場合、コードベース全体を破棄したり、大きなホンキングコードベースを変更して、現在行っている以外のことを行う方法を調査したりする必要はありません。要件の変更後も関連するライブラリと、それらを組み合わせて新しい機能を実現する方法を決定するだけです。
ライブラリの使用を容易にするライブラリのプログラミング手法を使用します。たとえば、関数ポインタをサポートしている非オブジェクト指向言語は、実際にはオブジェクト指向プログラミング(OOP)をサポートしています。したがって、たとえばCでは、OOPを実行できます。
多くのプロジェクト間でこれらの小さな独立したライブラリを共有することを検討することもできます(gitサブモジュールはあなたの友達です)。
言うまでもなく、小さな独立した各ライブラリは、単体テストする必要があります。特定のライブラリが単体テストできない場合は、何か問題があります。
CまたはC++を使用していて、小さな.soファイルを多数持つという考えが嫌いな場合は、すべてのライブラリを1つの大きな.soファイルにリンクするか、静的リンクを行うことができます。 Javaについても同様です。soを.jarに変更するだけです。
シンプル:ほとんどのコードのメンテナンスコストをzeroに下げて、メンテナンス可能な数の可動部品を用意します。変更する必要がないコードには、メンテナンスコストはかかりません。私は、コードに本当にzeroメンテナンスコストがかかるようにすることをお勧めします。多くの小さくてうるさいリファクタリングの反復でコストを削減しようとしないでください。コストをかけるゼロすぐに。
わかりました、確かにそれは想像よりもずっと難しいです。しかし、始めるのは難しくありません。コードベースのチャンクを取り出してテストし、インターフェース設計が混乱している場合は、その上にニースインターフェースを構築し、同時に、(変更する理由がないなどの理由で)信頼性が高く、安定しているコードベースの部分の拡大を開始できます。信頼できず、不安定な部分を縮小する。維持するのが悪夢のように感じるコードベースでは、変更が必要な可動部分とそうでない部分を区別できないことがよくあります。これは、すべてが信頼できず、変更される傾向があるためです。
コードベースの構成を「安定」部分と「不安定」部分に分離することをお勧めします。安定部分は、再構築および変更するための巨大なPITAです(これは、それらが本当に「安定」セクションに属している場合は、変更して再構築してください)。
保守性を難しくするのは、コードベースのサイズではありません。維持する必要があるのはコードベースのサイズです。オペレーティングシステムのAPIを使用するときはいつでも、何百万行ものコードに依存しています。しかし、オペレーティングシステムのソースコードを維持する必要がないため、製品のメンテナンスコストには影響しません。私はコードを使用するだけで機能します。私が使用するだけで、維持する必要がないコードは、私の側で維持費を負担しません。