web-dev-qa-db-ja.com

ソフトウェアプロジェクトの偶発的な複雑さを管理する方法

マレーゲルマンがリチャードファインマンがどのように多くの困難な問題を解決したかを尋ねられたとき、ゲルマンはファインマンがアルゴリズムを持っていると答えました:

  1. 問題を書き留めます。
  2. 本気で考えてください。
  3. ソリューションを書き留めます。

ゲルマンはファインマンが別の種類の問題解決者であり、彼の方法を研究することから得られる洞察がなかったことを説明しようとしていました。中規模/大規模のソフトウェアプロジェクトの複雑さの管理についても、同じように感じています。優れた人々は本質的にそれが得意であり、何とかしてさまざまな抽象化を階層化およびスタックして、無関係な情報を導入することなく全体を管理可能にします。

それでは、ファインマンアルゴリズムが偶発的な複雑さを管理する唯一の方法ですか、それともソフトウェアエンジニアが偶発的な複雑さを抑えるために一貫して適用できる実際の方法はありますか?

76
davidk01

良い動きを見つけたら、より良い動きを探してください。
-27年の世界チェスチャンピオン、エマニュエルラスカー

私の経験では、偶然の複雑さの最大の要因は、たまたま最初のドラフトが機能しているという理由だけで、最初のドラフトを使い続けることです。これは英語の作曲クラスから学ぶことができるものです。彼らは、教師のフィードバックを取り入れて、課題のいくつかの下書きを時間通りに構築します。プログラミングクラスは、なぜかそうではありません。

次善のコードを認識、明確化、修正する具体的かつ客観的な方法が満載の本があります: クリーンコードレガシーコードで効果的に機能する 、その他多数。多くのプログラマーはこれらの技法に精通していますが、それらを適用するために常に時間をかけるとは限りません。それらは偶発的な複雑さを完全に軽減することができ、試してみることを習慣にしていないだけです

問題の一部は、他の人のコードの中間的な複雑さが、初期段階でピアレビューを受けていない限り、あまり見られないことです。クリーンなコードは、実際には通常いくつかの下書きが含まれている場合でも、簡単に記述できるように見えます。最初に頭に浮かぶ最善の方法を書き、それがもたらす不必要な複雑さに気づき、次に「より良い動きを探して」それらの複雑さを取り除くためにリファクタリングします。次に、あなたはそれを見つけることができなくなるまで「より良い動きを探しています」を続けます。

ただし、コードが解約されるまではコードをレビュー用に公開しないため、外部的にはファインマンのようなプロセスであった可能性があります。あなたはそのようにすべてを1つのチャンクで実行することはできないと思う傾向があるので、気にしないでください。どちらか、または可能であれば、以前に何度も同様のコードを記述した経験があり、中間段階なしでパターンを見ることができるからです。どちらにしても、下書きは避けられません。

105
Karl Bielefeldt

「ソフトウェアアーキテクチャスキルを教えることはできません」は、広く誤解されています。

多くの人がそれを信じる理由を理解するのは簡単です(それが得意な人は、神秘的に特別であると信じたがり、そうでないのは自分のせいではないと信じたくない人です)。それにもかかわらず、間違っています。このスキルは、他のソフトウェアスキルよりもやや実践的です(たとえば、ループの理解、ポインタの処理など)。

大規模なシステムを構築することは、優れたミュージシャンや演説家になることと同じように、繰り返しの練習や経験から学ぶことができると私は固く信じています。ほとんどの開業医のリーチ。

複雑さに対処することは、数回試行して失敗することによって主に習得するスキルです。それは、コミュニティが大規模なプログラミングのために発見した多くの一般的なガイドライン(レイヤーを使用し、頭の後ろで複製を戦う、0/1 /無限に忠実に準拠する...)が明らかに正しくなく、彼らが実際に大きな何かをプログラムするまで初心者。数か月後に問題を引き起こした重複によって実際に噛まれるまでは、そのような原則の重要性を「理解」することはできません。

47
Kilian Foth

Andy Huntによる実用的な考え方 はこの問題に対処します。それはドレイファスモデルを指し、さまざまなスキルの習熟には5つの段階があります。初心者(ステージ1)は、何かを正しく行うために正確な指示を必要とします。逆に、エキスパート(ステージ5)は、特定の問題に一般的なパターンを適用できます。本を引用して

多くの場合、専門家が彼らの行動を細かいレベルで説明することは困難です。彼らの反応の多くは非常によく実践されているので、彼らは意識的な行動になります。彼らの広大な経験は、脳の非言語的、意識的領域によって採掘されているため、私たちが観察したり、明確に表現したりするのは困難です。

専門家が彼らのことをするとき、それは私たちの残りの部分にほとんど魔法のように見えます-奇妙な呪文、どこからともなく現れているように見える洞察、そして私たちの残りの部分さえ確信が持てないときに正しい答えを知る一見不思議な能力質問について。もちろんそれは魔法ではありませんが、専門家が世界を認識する方法、問題の解決方法、使用するメンタルモデルなどはすべて、非専門家とは著しく異なります。

さまざまな問題を確認する(結果として回避する)この一般的なルールは、特に偶発的な複雑さの問題に適用できます。特定のルールセットを用意するだけでは、この問題を回避できません。これらのルールでカバーされない状況が常にあります。問題を予測したり、解決策を特定したりするには、経験を積む必要があります。経験は教えることのできないものであり、それは常に試行錯誤し、失敗し、成功し、間違いから学ぶことによってのみ得ることができます。

この質問 Workplaceからの関連があり、このコンテキストでIMHOを読むのは興味深いでしょう。

23
superM

詳細は明記しませんが、「偶然の複雑さ」は、「本質的な」複雑度と比較して、問題に固有ではない複雑度として定義されます。 「テイミング」に必要なテクニックは、どこから始めるかによって異なります。以下は、すでに不要な複雑さを獲得しているシステムを主に参照しています。

私は、多数の数年にわたる大規模なプロジェクトでの経験があります。「偶発的」コンポーネントが「必須」アスペクトを大幅に上回り、そうでないコンポーネントも大幅に上回っていました。

実際、ファインマンアルゴリズムはある程度適用されますが、「本当のことを考える」が成文化できない魔法のみを意味するという意味ではありません。

私は取られる必要がある2つのアプローチがあることを見つけます。それらを両方持ってください–それらは代替ではありません。 1つは少しずつ対処することで、もう1つは大規模な再作業を行うことです。確かに、「問題を書き留めてください」。これは、システムの監査の形を取る可能性があります。コードモジュール、その状態(臭い、自動テストのレベル、それを理解すると主張するスタッフの数)、全体的なアーキテクチャー(「問題がある」場合でも1つあります) )、要件の状態など.

対処する必要がある問題が1つもないのは、「偶然の」複雑さの性質です。だからあなたはトリアージする必要があります。システムを維持し、その開発を進める能力に関して、それはどこを傷つけますか?たぶんいくつかのコードは本当に臭いですが、最優先事項ではなく、修正を待つことができます。一方、リファクタリングに費やされた時間を迅速に返すコードが存在する場合があります。

より優れたアーキテクチャとなる計画を定義し、新しい作業がその計画に確実に準拠するようにしてください。これは段階的なアプローチです。

また、問題のコストを明確にし、それを使用してリファクタリングを正当化するビジネスケースを構築します。ここで重要なことは、適切に設計されたシステムははるかに堅牢でテスト可能であるため、変更を実装するための時間(コストとスケジュール)がはるかに短くなるということです。これは真の価値があります。

大幅な手直しは「本当のことを考える」のカテゴリで行われます。正しく理解する必要があります。これは、 "ファインマン"(まあ、1のほんの一部でも構いません)があると大きな効果が出る場所です。アーキテクチャの改善につながらない大きな手直しは、惨事になる可能性があります。これについては、システム全体の書き換えが悪名高い。

どのアプローチでも、「偶発的」と「必須」を区別する方法を暗黙的に知っています。つまり、システムとその目的を本当に理解している優れたアーキテクト(またはアーキテクトのチーム)が必要です。

そうは言っても、私にとって重要なのは自動テストです。それが十分にある場合、システムは制御されています。そうでない場合。 。 。

4
Keith

すべてを可能な限りシンプルにする必要がありますが、シンプルにすることはできません。
—アルバートアインシュタインに起因

偶然の複雑さに対処するための個人的なアルゴリズムをスケッチしましょう。

  1. ユーザーストーリーまたはユースケースを記述します。製品の所有者に確認してください。
  2. 機能がないために失敗する統合テストを記述します。チームにそのようなことがある場合は、QAまたはリードエンジニアに確認してください。
  3. 統合テストに合格できるいくつかのクラスの単体テストを記述します。
  4. ユニットテストに合格したクラスのminimal実装を記述します。
  5. 他の開発者とユニットテストと実装を確認します。ステップ3に進みます。

全体のデザインマジックはステップ3にあります。どのようにこれらのクラスを設定しますか?これは次の質問と同じになります。問題の解決策がある前に問題の解決策があるとどう思いますか?

驚くべきことに、あなたが解決策を持っていると想像するだけは、問題解決(Abelsonによって「希望的思考」と呼ばれる)について書く人々の主な推奨事項のようですおよびSussmanの コンピュータプログラムの構造と解釈 およびPolyaの それを解決する方法 )の「後退」

一方、誰もが「想像上の解決策の味」を持っているわけではありません。あなただけがエレガントだと思う解決策もあれば、より幅広い聴衆が理解できる解決策もあります。そのため、他の開発者とコードをピアレビューする必要があります。パフォーマンスを調整するためではなく、理解されたソリューションに同意するためです。通常、これは再設計につながり、いくつかの反復の後、はるかに優れたコードになります。

minimal実装を記述してテストに合格し、多くの人が理解できるテストを記述したい場合は、還元不可能な複雑さが残ります。

3
logc

偶然の複雑さ

元の質問(言い換え)は次のとおりでした。

アーキテクトはソフトウェアプロジェクトの偶発的な複雑さをどのように管理しますか?

偶発的複雑さは、プロジェクトを指揮する者が1つだけのテクノロジーを追加することを選択した場合に発生し、プロジェクトの元の設計者の全体的な戦略は意図していなかったプロジェクトに持ち込みます。このため、戦略の選択の背後にある理由を記録することが重要です。

誤った複雑さは、その戦略からの意図的な離脱が明らかに必要になるような時まで、彼らの元の戦略に固執するリーダーシップによって妨げられる可能性があります。

不要な複雑さの回避

質問の本文に基づいて、次のように言い換えます。

アーキテクトはソフトウェアプロジェクトの複雑さをどのように管理しますか?

この言い換えは、ファインマンアルゴリズムが導入された質問の本文にさらに当てはまり、問題に直面したときに最良の建築家がゲシュタルトを持ち、そこからソリューションを巧みに構築することを提案するコンテキストを提供します。私たちの残りはこれを学ぶことを望んでいないことを。理解のゲシュタルトを持つことは、主題の知性と、その範囲内にある可能性のある建築オプションの機能を学ぶ意欲に依存します。

プロジェクトを計画するプロセスでは、組織の学習を使用してプロジェクトの要件のリストを作成し、考えられるすべてのオプションのリストを作成してから、オプションと要件を照合します。専門家のゲシュタルトは、彼がこれをすばやく、そしておそらくほとんど明らかな作業なしで、それを彼に容易にやって来るように見せることを可能にします。

彼の準備のために彼に来ると私はあなたに提出します。エキスパートのゲシュタルトを使用するには、すべてのオプションに精通している必要があります。また、プロジェクトが提供する必要があると判断された予測される将来のニーズを可能にする簡単なソリューションを提供する見通しと、変化するニーズに適応する柔軟性が必要です。プロジェクト。ファインマンの準備は、理論および応用数学と物理学の両方におけるさまざまなアプローチを深く理解していたことでした。彼は生まれつき好奇心が強く、自分の周りの自然界について発見したことを理解するのに十分な明るさ​​でした。

エキスパートテクノロジーアーキテクトも同様の好奇心を持ち、ファンダメンタルズを深く理解するだけでなく、さまざまなテクノロジーに幅広く触れています。彼(または彼女)は、ドメイン全体で成功している戦略( Principles of Unix Programming など)と特定のドメインに適用される戦略( designなど)パターン および スタイルガイド )。彼はすべてのリソースに精通しているわけではありませんが、リソースの場所を知っています。

ソリューションの構築

このレベルの知識、理解、および知恵は、経験と教育から引き出すことができますが、偶発的で不要な複雑さを回避する方法で連携するゲシュタルト戦略的ソリューションをまとめるために、知性と精神活動が必要です。専門家はこれらの基礎をまとめる必要があります。これらは、最初に用語を作成したときにドラッカーが予見したナレッジワーカーでした。

特定の最後の質問に戻ります。

偶発的な複雑さを和らげる具体的な方法は、次の種類のソースにあります。

Unixプログラミングの原則に従うと、うまく機能し、共通のインターフェースで堅牢な単純なモジュール式プログラムを作成できます。以下の設計パターンは、必要以上に複雑ではない複雑なアルゴリズムを構築するのに役立ちます。以下のスタイルガイドは、コードが読みやすく、保守可能で、コードが記述されている言語に最適であることを保証します。専門家は、これらのリソースにある多くの原則を内部化し、それらをまとまりのあるシームレスな方法でまとめることができます。

2
Aaron Hall

これは数年前は難しい質問だったかもしれませんが、最近のIMOでは、偶発的な複雑さを解消することはもはや難しくありません。

ケントベクセイド氏自身について、ある時点で:「私は優れたプログラマーではありません。私は優れた習慣を持つ優れたプログラマーです。」

IMO:彼は自分を建築家ではなくプログラマと見なし、彼の焦点は知識ではなく習慣に焦点を当てています。

ファインマンの難しい問題を解決する方法は、それを行う唯一の方法です。説明は必ずしも簡単に理解できるとは限らないので、分析します。ファインマンの頭は知識だけでなく、その知識を応用するスキルにも満ちていました。それを使用する知識とスキルの両方を持っている場合、難しい問題を解決することは難しくも簡単でもありません。それが唯一可能な結果です。

奇妙な複雑さを含まない、完全に非魔法の方法でクリーンなコードを記述します。これは、Feynmanが行ったこととほとんど同じです。必要なすべての知識を取得し、単に隠しておくだけでなく、それを動作させることに慣れるためのトレーニングを行います頭の片隅にクリーンなコードを書いてください。

現在、多くのプログラマーは、クリーンなコードを作成するために必要なすべての知識にさえ気付いていません。若いプログラマーはアルゴリズムとデータ構造に関する知識を捨てる傾向があり、ほとんどの古いプログラマーはそれを忘れがちです。または大きなO表記と複雑さの分析。年配のプログラマーはパターンやコードの匂いを却下する傾向があります-またはそれらが存在することさえ知らない。どんな世代のほとんどのプログラマーも、たとえパターンについて知っていても、使用するタイミングとドライバーのパーツを正確に覚えていることはありません。 SOLIDの原則に照らしてコードを常に評価している世代のプログラマはほとんどいません。多くのプログラマは、あらゆる場所で抽象化のすべての可能なレベルを混合しています。当分の間、私は仲間のプログラマを1人は知りません、彼のコードをリファクタリングブックでFowlerによって説明されたステンチに対して絶えず評価するために。一部のプロジェクトはいくつかのメトリックツールを使用しますが、最も使用されるメトリックはある種の複雑さですが、他の2つのメトリック(結合と凝集)はクリーンなコードにとって非常に重要であっても、ほとんど無視されます。ほとんどすべての人が無視するもう1つの側面は、認知的負荷です。単体テストをドキュメントとして扱うプログラマはほとんどいません。さらに、単体テストの作成や名前の付け方が難しいことを認識しているプログラマはまだいません、それは通常悪い因数分解を示します。わずかな少数派は、コードモデルとビジネスドメインモデルをできる限り互いに近づけるためのドメイン駆動設計のマントラを認識しています。将来問題を食べる。コードをクリーンにしたい場合は、常にこれらすべてを考慮する必要があります。そして、今思い出せないことはまだまだたくさんあります。

クリーンなコードを書きたいですか?魔法は必要ありません。必要なことをすべて学び、それを使用してコードのクリーン度を評価し、満足するまでリファクタリングしてください。そして、学習を続けてください。ソフトウェアはまだ若い分野であり、新しい洞察と知識は速いペースで獲得されます。

0
user625488