テスト駆動開発(および一般的なアジャイル)のこの制限は実際に関連していますか?


30

テスト駆動開発(TDD)では、最適ではないソリューションから開始し、テストケースを追加してリファクタリングすることにより、より良いソリューションを繰り返し生成します。ステップは小さいと想定されています。つまり、新しいソリューションはそれぞれ前のソリューションの近くにあることになります。

これは、勾配降下法やローカル検索などの数学的なローカル最適化方法に似ています。このような方法のよく知られた制限は、グローバルな最適値、または許容可能なローカルな最適値を見つけることを保証しないことです。開始点が悪い解の大きな領域によってすべての許容可能な解から分離されている場合、そこに到達することは不可能であり、メソッドは失敗します。

より具体的に言うと、いくつかのテストケースを実装した後、次のテストケースではまったく異なるアプローチが必要になるというシナリオを考えています。前の作業を破棄して、最初からやり直す必要があります。

この考え方は、TDDだけでなく、小さなステップで進行するすべてのアジャイルメソッドに実際に適用できます。この提案されたTDDとローカル最適化の類推には重大な欠陥がありますか?


三角測量と呼ばれるTDDサブテクニックについて言及していますか?「許容できる解決策」とは、正しい解決策か、保守可能/エレガント/読み取り可能な解決策を意味しますか?
-guillaume31

6
これは本当の問題だと思います。それは私の意見であるため、答えを書きません。しかし、はい、TDDは設計手法として宣伝されているため、局所的な最大値に至るか、まったく解決策にならないという欠陥があります。一般に、TDDはアルゴリズム設計には適していません。:TDDの制限に関連する説明を参照してくださいTDDで数独を解くのピーター・ノーヴィグが実際に主題について知ることによって、実際のソリューションを提供しながら、サークルで実行されていると「TDDをやって」しながら、ロン・ジェフリーズは自分のお尻を作るています、
アンドレスF.

5
言い換えると、TDDは「既知の」問題で記述するクラスの量を最小限に抑えるために(願わくば)議論の余地のないステートメントを提供します。したがって、クリーンでシンプルなコードを生成しますが、実際に全体像を見て、ドメイン固有の知識を持っていることは、断片的なテストを記述し、記述しなければならないコードを「発見」するよりも便利です。
アンドレスF.

2
問題は存在しますが、TDDやアジャイルに限定されません。以前に作成されたソフトウェアの設計を変更する必要があることを意味する要件の変更は、常に発生します。
RemcoGerlich

@ guillaume31:必ずしも三角測量ではなく、ソースコードレベルで反復を使用する手法。受け入れ可能な解決策によるIすべてのテストに合格し、合理的に維持することができます平均1 ...
フランク・フグ

回答:


8

このような方法のよく知られた制限は、グローバルな最適値、または許容可能なローカルな最適値を見つけることを保証しないことです。

比較をより適切にするために、ある種の問題では、反復最適化アルゴリズムが適切なローカル最適値を生成する可能性が非常に高く、他の状況では失敗する可能性があります。

いくつかのテストケースを実装した後、次のテストケースではまったく異なるアプローチが必要になるというシナリオを考えています。前の作業を破棄して、最初からやり直す必要があります。

これが現実に起こりうる状況を想像することができます。間違ったアーキテクチャを選択した場合、既存のすべてのテストを最初から再作成する必要があります。オペレーティングシステムAのプログラミング言語Xで最初の20個のテストケースの実装を開始するとしましょう。残念ながら、要件21には、プログラム全体をオペレーティングシステムBで実行する必要があります。したがって、ほとんどの作業と言語Yでの再実装を破棄する必要があります(もちろん、コードを完全に破棄するのではなく、新しい言語とシステムに移植します)。

これは、TDDを使用する場合でも、事前に全体的な分析と設計を行うことをお勧めします。ただし、これは他の種類のアプローチにも当てはまるため、これは固有のTDD問題とは思わない。また、実際のプログラミングタスクの大部分では、標準アーキテクチャ(プログラミング言語X、オペレーティングシステムY、ハードウェアXYZ上のデータベースシステムZなど)を選択するだけで、TDDのような反復またはアジャイル手法を比較的確実に実行できます。行き止まりにはなりません。

ロバートハーヴェイを引用して:「ユニットテストからアーキテクチャを成長させることはできません。」またはpdr:「TDDは、私が最高の最終設計に到達するのを助けるだけでなく、より少ない試行でそこに到達するのに役立ちます。」

実際にあなたが書いたもの

開始点がすべての許容可能な解決策から悪い解決策の大きな領域によって分離されている場合、そこに到達することは不可能であり、メソッドは失敗します。

真実になる可能性があります-間違ったアーキテクチャを選択すると、そこから必要なソリューションに到達しない可能性があります。

一方、事前に全体的な計画を立てて適切なアーキテクチャを選択する場合、TDDを使用することは、「グローバルな最大」(または少なくとも十分な最大)数サイクル。


20

TDDには極大の問題はないと思います。あなたが正しく気づいたように、あなたが書くコードはかもしれませんが、それがリファクタリング(機能を変更せずにコードを書き換える)が整っている理由です。基本的に、テストの増加に伴い、必要に応じてオブジェクトモデルの重要な部分を書き換えることができますが、テストのおかげで動作は変更されません。テストは、システムに関する不変の真理を示します。したがって、局所的および絶対的最大値の両方で有効である必要があります。

TDDに関連する問題に興味がある場合は、よく考える3つの異なる問題に言及できます。

  1. 完全問題:システムを完全に記述するために必要などのように多くのテスト?「例によるコーディング」はシステムを記述する完全な方法ですか?

  2. 硬化問題:へのインタフェースをテスト何は、不変のインタフェースを持っている必要があります。テストは不変の真理を表します、覚えておいてください。残念ながら、これらの真実は、ほとんどの場合、外部に面するオブジェクトについてのみ、私たちが書くコードのほとんどについては知られていません。

  3. テスト損傷問題:アサーションがテスト可能にするために、我々は次善のコード(たとえば、あまりパフォーマンスを)記述する必要があります。コードができる限り良好になるようにテストを作成するにはどうすればよいですか?


コメントに対処するために編集:ここでは、リファクタリングによって「ダブル」関数の極大値を抽出する例を示します。

テスト1:入力が0の場合、ゼロを返します

実装:

function double(x) {
  return 0; // simplest possible code that passes tests
}

リファクタリング:不要

テスト2:入力が1の場合、2を返す

実装:

function double(x) {
  return x==0?0:2; // local maximum
}

リファクタリング:不要

テスト3:入力が2の場合、4を返す

実装:

function double(x) {
  return x==0?0:x==2?4:2; // needs refactoring
}

リファクタリング:

function double(x) {
  return x*2; // new maximum
}

1
しかし、私が経験したことは、最初の設計はいくつかの単純な場合にしか機能せず、後でより一般的なソリューションが必要だと気づいたことです。より一般的なソリューションを開発するにはより多くのテストが必要でしたが、特殊なケースの元のテストは再び機能しません。より一般的なソリューションを開発する際に(一時的に)それらのテストを削除し、準備ができたらそれらを追加し直すことは許容できると思いました。
5gon12eder

3
私は、リファクタリングがコードを一般化する(もちろん、人為的な「デザインパターン」空間の外にある)方法であるとか、局所的な最大値を逃れる方法だとは思いません。リファクタリングはコードを整理しますが、より良い解決策を見つけるのに役立ちません。
アンドレスF.

2
@Sklivvz理解しましたが、私はあなたが投稿したようなおもちゃの例の外ではそのように機能するとは思わない。また、関数の名前が「double」であることがわかりました。ある意味であなたはすでに答えを知っていました。TDDは、多かれ少なかれ答えを知っているが、「きれいに」書きたいときに役立ちます。アルゴリズムを発見したり、本当に複雑なコードを書いたりするのには役立ちません。これが、ロンジェフリーズが数独をこの方法で解決できなかった理由です。TDDを使用して、不慣れなアルゴリズムを実装することはできません。
アンドレスF.

1
@VaughnCatoわかりました、今私はあなたを信頼するか、懐疑的であるかのどちらかです(これは失礼なことなので、やめましょう)。私の経験では、あなたが言うようには機能しません。TDDから進化した合理的に複雑なアルゴリズムを見たことはありません。たぶん私の経験はあまりにも制限されています:)
アンドレスF.

2
@Sklivvz「適切なテストを記述できる限り」がまさにポイントです。私に質問をお願いするように聞こえます。私が言っていることは、あなたはしばしばできないということです。アルゴリズムやソルバーについて考えることは、最初にテスト書くことで簡単になりません。最初に全体像を見る必要があります。シナリオを試すことはもちろん必要ですが、TDDはシナリオを書くことではないことに注意してください。TDDは設計をテスト駆動することです!最初にテストを記述して、数独ソルバー(または別のゲームの新しいソルバー)の設計を推進することはできません。逸話的な証拠として(これは十分ではありません):ジェフリーズはできませんでした。
アンドレスF.

13

あなたが数学用語で説明しているのは、私たちが自分自身を隅に描くと呼ぶものです。この発生はTDDに限定されることはほとんどありません。ウォーターフォールでは、そこに到達するためだけにグローバルな最大値を確認し、次の丘の上にもっと良いアイデアがあることを理解することを望んで、数ヶ月間の要件を収集し、注ぐことができます。

違いは、この時点で完璧であるとは決して思わなかったアジャイル環境にあるため、古いアイデアを捨てて新しいアイデアに移行する準備が整いました。

TDDに固有の機能として、TDDで機能を追加する際にこれが発生しないようにするテクニックがあります。それが変換優先の前提です。TDDにリファクタリングする正式な方法がある場合、これは機能を追加する正式な方法です。


13

では彼の答え、@Sklivvzは説得力の問題が存在していないと主張しています。

私はそれが問題ではないことを主張したい:反復的な方法論全般とアジャイル、特にTDDの基本的な前提(および存在理由)は、グローバルな最適化だけでなく、ローカルな最適化でもないということです」知られています。言い換えると、それが問題であったとしても、とにかくそれを繰り返し行う方法はありません。基本的な前提を受け入れると仮定します。


8

TDDとアジャイルのプラクティスは、最適なソリューションの作成を約束できますか?(または、「良い」ソリューションでさえ?)

ではない正確に。しかし、それは彼らの目的ではありません。

これらの方法は、ある状態から別の状態への「安全な通過」を提供するだけであり、変更には時間がかかり、困難で、リスクが高いことを認めています。そして、両方のプラクティスのポイントは、アプリケーションとコードが実行可能であり、要件をより迅速かつ定期的に満たすことが実証されていることを保証することです。

... [TDD]は、要件を満たすことが証明されていないソフトウェアの追加を可能にするソフトウェア開発に反対しています ... KDDは、2003年にTDDが推奨することを述べた技術を開発または「再発見」したとされています自信をデザインし、刺激します。ウィキペディア

TDDは、コードの各「チャンク」が要件を満たすことを保証することに焦点を当てています。特に、貧弱なコーディングによって要件を推進するのではなく、コードが既存の要件を確実に満たすのに役立ちます。しかし、実装が何らかの形で「最適」であることを約束するものではありません。

アジャイルプロセスに関して:

作業ソフトウェアは進捗状況の主な指標です...各反復の終わりに、利害関係者と顧客の代表者は進捗状況を確認し、投資収益率を最適化するために優先順位を再評価します(Wikipedia

敏ility性は最適なソリューションを探していません。ROIを最適化することを目的とした、実用的なソリューションです。それはよりも早く実用的なソリューションを約束します; 「最適な」ものではありません。

しかし、その質問は間違っているので、OKです。

ソフトウェア開発のOptimumsは、あいまいで移動するターゲットです。通常、要件は流動的であり、上司の上司でいっぱいの会議室で、あなたの恥ずかしいことにのみ現れる秘密に満ちています。そして、ソリューションのアーキテクチャとコーディングの「本質的な良さ」は、同僚と管理上の大君の意見の分かれた主観的な意見によって評価されます。誰も実際に良いソフトウェアについて何も知らないかもしれません。

最低限で、TDDとアジャイルプラクティスは困難を認め、二つのものを最適化しようとしている客観的かつ測定:作業V-ない作業。そしてスーナーV以降。。

また、客観的な指標として「仕事」と「早い」があったとしても、それらを最適化する能力は、主にチームのスキルと経験に左右されます。


努力が最適なソリューションを生み出すときにあなた理解できることには、次のようなものがあります。

等..

それらのそれぞれが実際に最適なソリューションを生成するかどうかは、尋ねるべきもう1つの大きな質問です。


1
確かに、TDDやその他のソフトウェア開発方法の目標は、グローバルな最適化という意味での最適なソリューションであるとは書いていません。私の唯一の懸念は、ソースコードレベルでの小さな回の反復に基づく方法論は、多くの場合、すべての任意の許容可能な(良いのに十分な)解決策を見つけられないかもしれないということです
フランク・フグ

@Frank私の答えは、ローカルとグローバルの両方の最適をカバーすることを意図しています。そして、どちらの場合でも答えは「いいえ、それはこれらの戦略の目的ではなく、ROIを改善し、リスクを軽減するように設計されています」です。...またはそのようなもの。そして、それは、Jörgの答えが得たものの一部です。「最適」は動いている標的です。...さらに一歩先に進めます。ターゲットを動かすだけでなく、完全に客観的または測定可能なわけではありません。
-svidgen

@FrankPuffer補遺に値するかもしれません。しかし、基本的なポイントは、これら2つのことは、それらがまったく設計されていない、または達成することを意図していない何かを達成するかどうかを尋ねているということです。さらに、測定や検証さえできない何かを達成できるかどうかを尋ねています。
svidgen

@FrankPuffer Bah。私は答えを更新して、それをもっと良くしようとしました。良くしたか悪くしたかはわかりません!...しかし、私はSE.SEを降りて仕事に戻る必要があります。
svidgen

この答えは大丈夫ですが、(他のいくつかの答えと同様に)私が抱えている問題は、「リスクの軽減とROIの改善」が常に最良の目標ではないということです。実際、それらはそれ自体が真の目標ではありません。動作するために何かが必要な場合、リスクを軽減してもそれは削減されません。TDDのように比較的無指向の小さなステップが機能しない場合があります。リスクは最小限に抑えられますが、最終的には有用な場所に到達できません。
アンドレス

4

これまで誰も追加していないことの1つは、あなたが説明している「TDD開発」は非常に抽象的で非現実的であることです。アルゴリズムを最適化する数学的アプリケーションでのようなものかもしれませんが、ほとんどのコーダーが取り組んでいるビジネスアプリケーションではあまり起こりません。

現実の世界では、テストは基本的にビジネスルールを実行および検証しています。

たとえば、顧客が妻と2人の子供を持つ30歳の非喫煙者である場合、プレミアムカテゴリは「x」などです。

プレミアム計算エンジンは、非常に長い間修正されるまで繰り返し変更されることはありません。ほとんどの場合、アプリケーションが稼働している間はそうではありません。

実際に作成したのはセーフティネットであるため、顧客の特定のカテゴリに新しい計算方法が追加されたときに、古いルールが突然破られて間違った答えを出すことはありません。デバッグの最初のステップが、バグを修正するコードを記述する前にエラーを再現するテスト(または一連のテスト)を作成することである場合、セーフティネットはさらに便利です。その後、1年後に、誰かが誤って元のバグを再作成した場合、コードがチェックインされる前に単体テストが中断します。しかし、それはあなたの仕事の大部分ではないはずです。


1
最初に、あなたの答えを読んだとき、私は「はい、それが核心点だ」と思いました。しかし、質問を再考した後、私はそれが必ずしもそれほど抽象的または非現実的ではないと思いました。完全に間違ったアーキテクチャを盲目的に選択した場合、TDDは1000回の反復後ではなく、それを解決しません。
Doc Brown

@Doc Brown Agreed、それはその問題を解決しません。ただし、すべての前提条件とビジネスルールを実行する一連のテストが提供されるため、アーキテクチャを繰り返し改善できます。修正のために一から書き直す必要があるほどひどいアーキテクチャは非常にまれです(願っています)。その極端な場合でも、ビジネスルールの単体テストが良い出発点になるでしょう。
mcottle

「間違ったアーキテクチャ」と言うとき、既存のテストスイートを捨てる必要がある場合を念頭に置いています。私の答えを読みましたか?
Doc Brown

@DocBrown-はい。「間違ったアーキテクチャ」とは「テストスイート全体を変更する」ことを意味する場合は、それを言った方がいいかもしれません。アーキテクチャを変更しても、すべてのテストがビジネスルールベースの場合、すべてのテストを破棄する必要はありません。おそらく、作成する新しいインターフェイスをサポートするためにそれらすべてを変更し、一部を完全に書き換える必要がありますが、ビジネスルールは技術的な変更に取って代わられることはないため、テストは残ります。ユニットテストへの投資は、アーキテクチャを完全に有効にする可能性が低い可能性があるため、無効にすべきではありません
-mcottle

確かに、新しいプログラミング言語ですべてのテストを書き換える必要がある場合でも、すべてを捨てる必要はなく、少なくとも1つは既存のロジックを移植できます。そして、主要な現実世界のプロジェクトについては100%同意します。問題の仮定は非常に非現実的です。
Doc Brown

3

邪魔になるとは思わない。ほとんどのチームには、たとえホワイトボードに書いても、最適なソリューションを思い付くことができる人はいません。TDD /アジャイルは邪魔になりません。

多くのプロジェクトは最適なソリューションを必要とせず、必要なものは、この分野で必要な時間、エネルギー、フォーカスが作成されます。他のすべてのものと同様に、最初に構築する傾向があります。その後、高速にします。パフォーマンスがそれほど重要な場合は、何らかのプロトタイプを使用してこれを実行し、多くの反復を通じて得られた知恵で全体を再構築できます。

いくつかのテストケースを実装した後、次のテストケースではまったく異なるアプローチが必要になるというシナリオを考えています。前の作業を破棄して、最初からやり直す必要があります。

これは発生する可能性がありますが、発生する可能性が高いのは、アプリケーションの複雑な部分を変更する恐れです。テストを受けていないことは、この領域に大きな恐怖感をもたらす可能性があります。TDDと一連のテストの利点の1つは、変更する必要があるという概念でこのシステムを構築したことです。このモノリシックに最適化されたソリューションを最初から考え出すと、変更するのが非常に困難になる可能性があります。

また、これを最適化不足の懸念のコンテキストに入れてください。パフォーマンスに過度に集中しているため、必要のないものを最適化し、柔軟性のないソリューションを作成することに時間を費やすことはできません。


0

「ローカル最適」のような数学的概念をソフトウェア設計に適用するのは欺くことができます。このような用語を使用すると、ソフトウェア開発は実際よりもはるかに定量的で科学的に聞こえます。コードに「最適」が存在したとしても、それを測定する方法はなく、したがって到達したかどうかを知る方法もありません。

アジャイルな動きは、ソフトウェア開発を数学的手法で計画および予測できるという信念に対する実際の反応でした。良くも悪くも、ソフトウェア開発は科学というよりも技術のようなものです。


しかし、それは反応が強すぎましたか?厳密な先行計画が扱いにくく費用がかかることが判明した多くの場合に確かに役立ちます。ただし、ソフトウェアの問題の中に、数学的な問題として事前に設計されたものに取り組む必要あります。それらをTDDすることはできません。PhotoshopのUIと全体的なデザインをTDDできますが、そのアルゴリズムをTDDすることはできません。これらは、典型的なTDDの例で「sum」、「double」、「pow」を導き出すような些細な例ではありません[1])。おそらく、いくつかのテストシナリオを記述して新しいイメージフィルターをいじることはできません。あなたは絶対に座って式を書いて理解しなければなりません。
アンドレスF.

2
[1]実際、fibonacciTDDの例/チュートリアルとして使用されているのを見て、かなりうそをついています。私は、フィボナッチや同様のシリーズをTDDすることで「発見」したことがないだろうと思っています。誰もがすでに不正行為であるフィボナッチを知っていることから始まります。TDDでこれを発見しようとすると、OPが求めていた行き止まりに到達する可能性が高くなります:単にテストを追加してリファクタリングするだけでは、シリーズを一般化することはできません - 数学を適用する必要あります推論!
アンドレスF.

2つの発言:(1)これは欺くことができることは正しい。しかし、TDDは数学的最適化と同じだとは書いていません。アナロジーまたはモデルとして使用しました。モデルと本物の違いに気づいている限り、数学はほとんどすべてに適用できる(そしてそうすべき)と信じています。(2)科学(科学的研究)は通常、ソフトウェア開発よりも予測しにくいものです。また、ソフトウェアエンジニアリングは、クラフトよりも科学的な仕事に近いとさえ言えます。工芸品は通常、より多くのルーチン作業が必要です。
フランクパファー

@AndresF .: TDDは、考えたり設計したりする必要がないという意味ではありません。これは、実装を作成する前にテストを作成することを意味します。アルゴリズムでそれを行うことができます。
ジャックB

@FrankPuffer:OK、それでは、ソフトウェア開発において「ローカル最適」なのは、どのような測定可能な価値ですか?
ジャックB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.