マーティンファウラーの有名なブログ投稿 Is Design Dead? を読んでいたとき、印象的な印象の1つは、アジャイル方法論とエクストリームプログラミングでは、設計だけでなくプログラミングも進化的であるという事実から、常にリファクタリングが必要な箇所があります。
プログラマーのレベルがよく、設計の影響を理解していて重大なミスを犯さない場合、コードは進化し続ける可能性があります。しかし、通常のコンテキストでは、このコンテキストでの地上の現実は何ですか?
ある重要な開発が製品に入る通常の日で、要件に重大な変更が発生した場合、それがどれだけ望んでも基本的な設計の側面を変更できないという制約ではありませんか? (コードの大部分を捨てずに)。設計と要件のさらなる改善の可能性について、行き止まりに達する可能性はかなりありませんか?
ここでは非アジャイルの実践は推奨していませんが、アジャイル、反復、または進化的開発手法を実践している人々から、経験。
そのような行き止まりに到達したことがありますか?どのようにしてそれを回避または回避できましたか?それとも、設計が進化してもクリーンで柔軟なままであることを保証するための対策はありますか?
私はあなたが投稿した記事のリンクを読んだだけです。Fowlerはいくつかの非常に優れた点を述べ、彼が言った多くのことを言わなければなりません。
IMO、まともな設計を行う場合は、行き止まりの状況と見なされる状況に陥ってはなりません。私は常にソフトウェアを ビルディングブロック で構成されていると見なしてきました。私はまだいくつかの事前設計を信じていますが、主な目的は製品全体を設計することではなく、全体的なアーキテクチャ/方向性を提供して、チームが全員が取り組んでいる共通の全体像を視覚化できるようにすることです。立方体と三角形のピースがたくさんある場合、ピースを一緒に叩き始める前に、城がどのように組み立てられるかをスケッチしておくと役に立ちます。
私はOO landから来たので、私にとって各ブロックはクラスであり、そのブロックの表面領域はパブリックインターフェイスです(外部または派生クラスから見えるもの)。 SOLID原則、各ブロックが非常にシンプルで直感的なパブリックインターフェイスを備えていることを確認します。私の例えに戻ると、コードが単純な形状のみを作成することを確認する必要があります。クラスを作成するときはいつでも、非常に複雑(多くの関数、多くの変数)であり、要件が変更されたときに再利用が難しい形状を作成します。
進化的設計の最大のリスク/課題はコーディングの時間に設計の決定を任せ、個々の開発者がそれらの決定を下すことを期待することであるという点で、ファウラーに同意します。適切なフィードバックメカニズムがないと、システムが故障する可能性があります。新しい機能が要求されるときはいつでも、拡張する必要のある関数を見つけ、その中に何らかの条件を設定し、その関数の内部にコードの束全体を追加するのは非常に魅力的です。そして、時にはこれで十分かもしれませんが、これも(IMO)行き止まりのコンポーネントにつながる最も一般的な方法の1つです。これは進化的なデザインとは何の関係もありません。これがいわゆる「ノーデザイン」です。
時間をかけて戻って言うと、ちょっと待ってください。このクラスにはすでに15のメンバー変数があります。そのうちの6つを抽出して、独自の自己完結型クラスに入れると、ソフトウェアは非常に軽量になります-重量、柔軟性、再利用可能なビルディングブロック。確かにPMが来て製品の要件の半分を変更した場合は、ブロックの一部を取り出して棚に戻し、新しいブロックをいくつか作成する必要があります(城を構築するときと同じように、すべてを使用することはできません)あなたのシリンダー)。しかし、その時点では、それはビジネスを行う上での一部にすぎません。要件が変更され、コードを柔軟かつモジュール式に保つことで、新しいビジネスの方向性に合わせて製品を変更できるはずです。
設計に対するこの革新的なアプローチは、あらゆるレベルのエンジニアのスキルで機能すると信じています。個人的に、私はソフトウェアを非常に長い間使用しており、チームがアジャイル方法論に移行する前に、ほとんどのQAを使用して、開発用PCからいくつかの主要コンポーネントをほとんど直接顧客に出荷する責任がありました。同時に、これらのコンポーネントは常に柔軟性と保守性を維持しています。
私は、ソフトウェアの設計は比較的上手だと思います。同時に、100ページのデザインドキュメントを作成してコーダーに渡して、それが機能することを期待するように依頼された場合、おそらく私は紙袋からデザインすることができませんでした。作業を開始するときに、UMLライクな(非常に簡略化された、完全な言語ではない)ダイアグラムをスケッチすることが時々ありましたが、コーディングを開始すると、必要に応じてリファクタリングし、最終的なコードが最初に描いたものとはまったく異なります。細かいところまで1か月か2か月考えても、他の誰かが自分のダイアグラムを作成して、コーディング中にデザインを変更せずに堅固なソフトウェアを思いつくことができるとは想像できません。
スペクトルの反対側では、現在私のチーム(現在はアジャイルであり、それを完全にサポートしています)に、過去15年間Cを行っただけの埋め込み土地から私たちに加わった2人の男性がいます。私は明らかにいくつかの最初の計画とクラスのレイアウトを手伝いましたが、SOLIDの適用と設計原則について議論する定期的なコードレビューとブレーンストーミングセッションもフォローアップしました。彼らはいくつかのスパゲッティを生み出しました少しうんざりさせたコードですが、わずかなナッジで、すでに生成されたものをリファクタリングし始めました。面白いのは、そのうちの1人が数日後に私に戻ってきて言ったのです。そのコードを移動すると、これは非常に読みやすく理解できるように見えます。行き止まりは回避されます。私が作ろうとしているポイントは、OO 、より多くの経験を持つメンターがいる限り、「進化的デザイン」は「デザインなし」と同じではないことを思い出させるために。彼の「より複雑な」クラスの一部でさえ、各クラスはそうではないのでそれほど怖くないそれだけの責任がある(つまり、それほど多くのコードではない)ので、 1つのクラス「行き止まり」で、それをチャックして、同じパブリックインターフェイスを持つ代替クラスを作成します(これまでのところ、これまでに書いたものでこの偶発性の必要性を見たことがなく、週に2回コードレビューを行っています)。
最後のメモとして、私は設計ドキュメントも確信しています(少なくとも現在のチームのビジネス状況に関して)が、私たちの設計ドキュメントの主な目標は Organizational Memory であるため、実際のドキュメントはコードが生成され、リファクタリングされた後に記述されます。コーディングする前に、通常、ナプキン/ mspaint/visioでクラスをスケッチする迅速な(時にはそれほど迅速ではない)設計フェーズがあり、このフェーズが青写真ではなく、進むべき道を作り出し、コーディングを開始すると、意味のないものは変更する必要があります。これらのリマインダーがあっても、新しい人はコードが元のデザインにどれほど不自然に感じても、コードを元のデザインに戻そうとする傾向があります。これは通常、コードレビューで表示されます。
ダン、私はたくさん書いた。申し訳ありません。
「設計の行き止まり」現象は、アジャイル手法とは直交していると思います。つまり、ウォーターフォールを実行し、(悪い)デザインに多くの時間を費やすことができるということです。次に、多くの時間を費やして実装し、行き止まりに自分を見つけます。
どちらかといえば、アジャイル手法すべきは、設計の選択が間違っていたことを早期に発見するのに役立ちます。この理由は、バックログでは最初に最も顧客価値の高い項目を実行する必要があり、ソフトウェアの有用な増分を提供することに集中する必要があるためです。あなたのデザインがあなたに高い価値と有用性を提供することを可能にするならば、それはすでに何かのために良いです:-)対照的に、このデザインが提供できないことが長年にわからないかもしれない滝のような状況で悪いデザインを持っているかもしれませんあらゆる価値と有用性-あなたが持っているのは、それが優れたデザインであるという幻想だけです。彼らが言うように、証拠はプリンにあります。
逆に言えば、アジャイル手法であっても、反復から反復へと決定を進めるシステムの設計について実行可能なビジョンを持つことが重要です。 Ken Schwabberは、ひどい開発者のチームがいても、一貫して悪いソフトウェアを繰り返し作成するようなものだと言ったと思います。アジャイルとは、多くの時間を前もって費やさないことを意味します。要件の実装を開始する前に、(とも学習または想像できることは限られているためです。変化する)。ただし、事前の作業(研究など)を行わなければならない状況があり、それを行わなければならない場合があります。
行き止まりをどのように回避しますか?
私は主に将来の要件を予測することによって言います。これは、類似のプロジェクト/製品の経験と知識で得られるものです。この期待は、現在のシステムについて多くの「もしも」の質問を自分自身に問うため、良い設計を整えるのに役立つものでもあります。私にとってこれは重要な要素です。 OOのようなテクニックは、自分が何をしているのかすでにわかっている場合に役立ちます。
行き止まりがある場合はどうしますか?
「行き止まり」は、斬新なものの開発中にあなたがヒットする他の技術ブロックと同じです。最初に気づくのは、完全にバックトラックすることを強制する真の「行き止まり」は実際にはないということです。少なくとも、この時点までに学んだことが、前進を可能にするものであり、努力は無駄になりませんでした。行き止まりにぶつかると、問題になります。問題は、新しい(または古い)要件を満たすために何を変更する必要があるか、およびこの変更を行うために最適化する方法です。あなたが今しなければならないすべてはこの問題を解決することです。これはソフトウェアではなく、たとえば、変更がはるかに簡単であるため、飛行機のデザイン。問題を特定し、修正します==リファクタリング==ソフトウェアエンジニアリング。時には多くの作業が含まれます...
スクラムを使用する場合、この変更は当然ユーザーストーリーに基づいて行われる必要があります(ユーザーはこの変更から何を取得しますか?)。プロセスは、現在のデザイン(おっと)で簡単に対応できない1つのストーリーから始まり、このストーリーを分解する方法について製品の所有者と話し合います。この変更を通じて、アジャイル原則を適用し続けます。
私の頭に浮かぶいくつかの有名な大きな要件の変更は、OSの世界からのものです。
あなたがこれらをどのように見ても、それらは多くの仕事です。元のデザインでは、ほぼ間違いなく、このような事態が発生する可能性が考慮されていませんでした(つまり、移植性は大きな要件ではありませんでした)。設計がOO=であったかどうかも、おそらく大きな要因ではありません。優れた設計では、プラットフォーム固有の部分がいくらか隔離され、作業が容易になります。
プロジェクトを永続的にリファクタリングし、UMLクラス図も使用しています。つまり、パッケージごとに1つ以上のクラス図を作成します。各図はパッケージのルートに保存されます。各UML分類子には、関連するJava Idにマップされる独自のIDがあります。つまり、ダイアグラムを開くと、最新のコードリファクタリングの変更に自動的に更新されます。直接変更することもできます。グラフィカルレベルのクラスダイアグラムとすべてのプロジェクトはすぐにリファクタリングされます。これはかなりうまく機能しますが、人間に取って代わることは決してありません。UMLクラスダイアグラムも、コードのグラフィカルビューにすぎません。コードとモデルを混在させないことが非常に重要ですEMFリファクタリングが完了するとすぐにモデル情報も失われるため、Eclipseが実行しています。これは役に立たないため、モデル駆動開発コードジェネレーターを使用しません。モデルがコードを駆動するのは好きではありませんが、私のコードは私のUMLクラス図を駆動します。
私のプロジェクト構造のすべての詳細を表す100を超えるクラス図と、至る所にあるメモでいっぱいになると、本当に役に立ちます。通常、開発者は他の図を学習または使用する時間がないため、プロジェクトのクラス図のみを作成します。クラス図も自動的に更新されるのでとても良いです。クラス図は、パッケージを逆にしてメモを追加するだけで、コードの後に作成できます。高速で常に正確で、100%反復的です。
モデル生成コードであるモデル駆動型開発と、通常はコードから更新されたUMLクラス図を使用してUMLをグラフィカルプレゼンテーションとして使用することを混同しないでください。 UML同期コードのみが、複数の反復の場合に私にとって真の価値があります。
長くて申し訳ありませんが、プロジェクトのグラフィカルなビューとしてのみ使用する場合は、UMLクラス図に2回目のチャンスを与える必要があると思います。これは、UMLがプロジェクト全体をカバーし、プロジェクト全体を表す大きなクラス図で構成される単一のモデルを持つことを意味します。何百ものビューを持つプロジェクト内に、何百もの小さなビューと各ビューのモデルがあるのはばかげているでしょう:-)
デザインや方向の変更などが原因で、コードや他のコードが行き止まりになりました。他にも多くの人がこの問題に遭遇するのを見てきました。大きな間違い(少なくとも私には間違いのように思われます)は、作業中のコードを破棄し、すべてを最初から再実装するという当面の望みです。
私はうまくいくと思われた同じ方法でそれぞれのケースに取り組みました:
費用:
利点:
約1〜2か月前、現在のプロジェクトは、SCRUM開発スタイルで、いくつかの悪い設計上の決定(および1か所に多くの設計の欠如)のために少し行き詰まりました。
私たちの解決策(そして私がSCRUMの標準的な解決策だと私が信じていること)は、スプリント全体(〜2週間)をリファクタリングだけに費やすことでした。この間、新しい機能は追加されませんでしたが、現在のコードベースについて考えることができ、私たちがやっていることに対してはるかに優れたシステムを設計することができました。
私たちは今、そのハードルを乗り越え、新しい機能を再び追加しています。
設計変更のコストを制限するための鍵は、コードをDRYにできる限り維持することです。これにより、ほとんどのアプリケーションコードが非常に高いレベルになり、ほとんどのコードが意図を直接表現します。 、およびメカニズムをほとんど指定していません。これを行うと、設計の決定はコード内で可能な限り最小の式になり、設計の変更は最小限のコストになります。
設計の行き止まりを回避するための鍵は、設計を変更する必要があるときはできるだけ早く認識し、それから変更することです。最大の問題は、設計を継続的に進化させることではなく、refusingによって設計を進化させ、それが大きな問題になるまで続きます。
例として、Netflixにはプロファイル機能があり、異なる家族のメンバーが同じプランに請求できますが、別々のキューがあります。数年前、彼らはその機能をキャンセルする必要があると発表しました。ユーザーのたった10%しかそれを使用していなかったためです。騒動の後、彼らは弾丸を噛み、それらの顧客を維持するために高価な再設計を行いました。
彼らが最初にその機能を追加したときに次善の設計を認識した何人かのエンジニアがいたと思います。彼らがそれを当時変更したとしたら、それはそれほど大きな問題ではなかっただろう。
「最初のものを捨てる計画を立てる」のようなことを言ったのはフレッド・ブルックスではなかったか?行き過ぎた感じはしないでください。行き止まりのデザインは、すべてのデザインを前もって実行しようとするプロジェクトでもポップアップ表示されます。再設計はすべてのタイプの開発で発生します。それは、それが最初から実行できない設計だった(最後の20%が "悪魔が細部にある"とつぶれてしまう)か、顧客が焦点を変更したためです。警報ベルの本当の必要はありません、あまり心配しないでください。