web-dev-qa-db-ja.com

DRYはソフトウェアプロジェクト管理の敵ですか?

ソフトウェア開発の最も基本的で広く受け入れられている原則の1つは、DRY(繰り返さないでください)です。ほとんどのソフトウェアプロジェクトには、何らかの管理が必要であることも明らかです。

次に、管理が容易なタスク(推定、スケジュール、制御)について教えてください。正しい、反復的なタスク、正確にはDRYに従って回避されるべきタスク。

そのため、プロジェクト管理の観点からは、既存のコードを100回コピーしてタスクを解決し、必要に応じて各コピーにいくつかの小さな変更を加えるのは素晴らしいことです。いつでも、あなたは自分がどれだけの仕事をし、どれだけ残っているかを正確に知っています。すべてのマネージャーはあなたを愛します。

代わりにDRY原理を適用し、多かれ少なかれ重複するコードを排除する抽象化を見つけようとすると、状況は異なります。通常、多くの可能性があり、決定を下さなければならず、調査を行います、創造的です。より短い時間でより良い解決策を思い付くかもしれませんが、失敗する可能性もあります。ほとんどの場合、残された作業量を実際に言うことはできません。あなたはプロジェクトマネージャーの最悪の悪夢です。

もちろん大げさですが、明らかにジレンマがあります。私の質問は次のとおりです。開発者がDRYをやりすぎているかどうかを判断する基準は何ですか?どうすればよい妥協案を見つけることができますか?または、妥協点を見つけるだけでなく、このジレンマを完全に克服する方法はありますか?

注:この質問は、私の以前の質問と同じ考えに基づいています ソフトウェア開発におけるルーチン作業の量とその推定への影響 、しかし、ポイントを明確にして、繰り返し申し訳ありません:)

82
Frank Puffer

あなたは、プロジェクト管理の主な目的が正確な見積もりを出すことであると想定しているようです。これはそうではありません。プロジェクト管理の主な目的は、開発者と同じです。つまり、製品所有者に価値を提供することです。

自動化ではなく、遅い手動プロセスを多く使用する製品は、理論的には見積もる方が簡単かもしれません(疑わしいかもしれませんが)、それは顧客に金銭的価値を提供しないため、プロジェクト管理が悪いだけです。ジレンマはありません。

ソフトウェアプロジェクトの見積もりが難しいことはよく知られており、多数の書籍が作成され、それを管理するためのさまざまなプロセスが開発されています。

PM=のonly目的が正確な見積もりを生成することである場合、それは簡単です。見積もりを10倍にパディングして、開発者にゲームをプレイさせるゲームをプレイしても製品の保守性が低下しないので、これは実際にコピーペーストビジーワークを使用して時間を埋めるという提案よりも優れています。

しかし実際には、製品の所有者は有用な見積もりandを可能な限り迅速かつ安価に提供する高品質の製品を求めています。これらは、PMがナビゲートする必要がある実際の制約です。

いずれにせよ、繰り返しの手動作業は自動化より予測可能であるというあなたの仮定に異議を唱えます。すべての経験から、繰り返しの手作業はmoreエラーが発生しやすいことがわかります。そして、コピー貼り付けされたコードでバグが発見された場合はどうなりますか?突然、バグを修正するコストに繰り返しの量が掛けられ、不確実性が爆発的に増大します。

133
JacquesB

あなたは正しい-コピーと貼り付けはうまく機能し、DRYは、コピーされたテンプレートまたはコピーのいずれかを維持または展開する必要がないプログラムを作成することである場合、意味がありませんこれら2つのソフトウェアコンポーネントのライフサイクルがまったく異なる場合、共通のコードをリファクタリングすることによってコンポーネントを結合し、それ自体が開発中の共通のlibにすることで、実際に作業に予測できない影響を与える可能性があります。 1つのプログラムまたはプログラムシステム内のセクションでは、これらのすべての部分が通常同じライフサイクルを持ちます。これがDRYおよびプロジェクト管理にとっての意味することを以下に示します。

真剣に、そのようなプログラムは世の中にたくさんあります。たとえば、コンピュータゲーム業界は、最大で数か月または1年の短期間にわたって維持する必要のあるプログラムを数多く作成し、その時間が過ぎるとコピー貼り付けを行います。メンテナンス期間が経過した前のゲームの古いコードから新しいゲームのコードベースへの変換は完全に問題なく、処理速度が向上する可能性があります。

残念ながら、過去数年間に私が対処しなければならなかったほとんどのプログラムのライフサイクルは、それとは大きく異なります。私に届いた要件またはバグ修正リクエストの98%は、既存のプログラムの変更リクエストでした。そして、既存のソフトウェアの一部を変更する必要があるときはいつでも、「プロジェクト管理」または計画は、テストとデバッグの労力が非常に少ないときに最適に機能します。これは、1つの場所で何かを変更する場合は当てはまりませんが、コピーのためです。 -貼り付けたビジネスロジックは、コードベースの他の数十の場所を変更する必要があることを簡単に忘れてしまいます。これらの場所をすべて見つけたとしても、それらをすべて変更する(および変更をテストする)時間は、変更する場所が1つしかない場合と同じくらい長くなります。したがって、変更を正確に見積もることができても、必要なコストの数十倍のコストがかかると、プロジェクトの予算と簡単に衝突する可能性があります。

TLDR-オリジナルまたはコピーのバグ修正とメンテナンスの必要性や責任がないプログラムを開発するときはいつでも、遠慮なくコピーしてください。ただし、あなた、あなたのチーム、またはあなたの会社が責任を負う、または担当する可能性がある場合は、可能な限りDRYを適用してください。

補足として、「バグ修正とメンテナンス」の意味と、これが、特に1つの製品の内部で、実際の例を使用して計画の予測不可能性につながる方法を説明しましょう。実際に、おそらく100のインスタンスではなく、このようなことが実際に発生するのを見てきましたが、重複するインスタンスが1つしかない場合でも問題が発生する可能性があります。

タスク:アプリケーションに対して100の異なるレポートを作成します。各レポートは非​​常によく似ており、レポート間の要件の違い、ロジックの違いがありますが、全体としてはそれほど多くの違いはありません。

このタスクを取得した開発者が最初のタスクを作成し(3日かかると言います)、QAによるいくつかの変更またはマイナーなバグ修正と、顧客による検査が終了した後、問題なく実行されているようです。次に、全体をコピーして貼り付け、次のレポートを作成することで、次のレポートの作成を開始しました。新しいレポートごとに、平均で1日程度必要です。一見して非常に予測可能...

100のレポートが「準備完了」になった後、プログラムは実際の本番環境に移行し、QA中に見落とされていたいくつかの問題が発生します。パフォーマンスの問題があるか、レポートが定期的にクラッシュするか、他のものが意図したとおりに機能しない可能性があります。 DRYの原則が適用された場合、コードベースを1か所で変更することでこれらの問題の90%を解決できます。しかし、コピーと貼り付けのアプローチにより、問題は1回ではなく100回解決しました。また、あるレポートから別のレポートにすでに適用された変更により、開発者は最初のレポートの修正を他の99にすばやくコピーアンドペーストできません。100のレポートすべてを調べて、それらを読む必要があります、変更を変更されたレポートに変換し、テストし、おそらくそれを個別にデバッグします。PMの場合、これは非常に難しくなります。もちろん、「通常の」バグ修正に時間をかけることができます(3時間としましょう) )そしてこれに100を掛けますが、実際には、これはおそらく間違った見積もりです。一部の修正は他の修正よりも簡単な場合もあれば、難しい場合もあります。また、この見積もりが正しい場合でも、デバッグにかかる​​コストは100倍になります彼らがする必要があるので、あなたは会社にたくさんのお金を要します。

同じことが、次回、顧客がこれらすべてのレポートで会社のエンブレムの色を変更したり、ページサイズを構成可能にしたり、または同様の方法ですべてのレポートに影響する他の新しい要件によって要求されたときにも発生します。したがって、それが発生した場合は、コストを見積もり、コードがDRYであったときに顧客が支払う必要がある価格の100倍を顧客に請求できます。ただし、これを数回試してみてください。そうすれば、顧客はおそらくあなたの法外な進化の費用を支払う気がないので、プロジェクトをキャンセルします。そしておそらくその時点で、誰かがなぜこれが起こったのかという質問をし、このコピーと貼り付けのプログラミングを決定した人を指で指すでしょう。

私の要点は、他の人のためにソフトウェアを作成するとき、少なくとも短期間には、物事を機能させること、バグを修正すること、プログラムを変化する要件に適合させることなどの責任を常に持っていることです。グリーンフィールドプロジェクトでも、これらは部品は、最初に計画された開発作業よりもはるかに多くなります。特に、コピー貼り付けされたすべてのコードが1つの製品内にある場合、責任の期間はすべての部分で同じです。これは、もはや死んでいないプロジェクトから古いコードをコピー貼り付けした状況とはかなり異なります。アクティブなメンテナンス中。

39
Doc Brown

そのため、プロジェクト管理の観点からは、既存のコードを100回コピーしてタスクを解決し、必要に応じて各コピーにいくつかの小さな変更を加えるのは素晴らしいことです。常に、自分が実行した作業量と残りの作業量が正確にわかります。すべてのマネージャーはあなたを愛します。

ベースアサーションが正しくありません。

ソフトウェアを他の職業と異なるにするのは、毎日何か新しいものを作っているということです。結局のところ、他の誰かがすでに作成したものを構築するためにyouを支払う顧客はいないでしょう。プロジェクトマネージャーは予測可能性を好むかもしれませんが、彼ら上司valueのような上司。わずかなバリエーションのあるコードをコピーして貼り付けるだけの場合は、会社に大きな価値はありません。

最終的には会社は、優れたプログラマーを雇うことで、ほんのわずかな時間で同じ仕事ができることに気付くでしょう。そして、彼らがそうしないなら、彼らの競争相手はそうするでしょう。

19
Telastyn

カットアンドペーストプログラミングは、結局は放棄されたソフトウェアにつながります。私は非常に大規模な電話会社に有線サービスを注文するシステムの請負業者でした。すべてのテストが手動で行われ、動作中のコードを変更したくなかったため、システムはカットアンドペーストされた悪心でした。ほんのわずかな拡張でも、数百行のコードの新しいコピーが作成される可能性があります。もともとアプリケーションは、最大12の物理回線のアカウントを処理するように作成されていました。もちろん、この制限はコードの何百もの場所で行われました。約4年後、ビジネスはチームに大きなアカウントを処理するために何が必要かを尋ねました。彼らは約1800万ドルを見積もりました。その時点で、プロジェクトは最小限のメンテナンスのためにオフショアチームに引き渡されました。既存のチームはすべて解雇されました。

このように考える組織は、より優れたテクノロジーを備えた企業に押しつぶされています。

12
kevin cline

ここで適用されるよく忘れられる格言は の法則 です。これは、コードを1回コピーすることは問題ありませんが、それを超えて汎用コードに置き換える必要があることを示しています。

3は任意の数のようにseemかもしれませんが、一般的なシナリオは、アプリケーションとデータベースでデータとロジックが複製される場合です。よく引用される例は、データベースと列挙型クライアント側にルックアップテーブルがある場合です。パラダイムの違いにより、これを1つの場所に簡単に格納できないため、情報が両方の場所に表示されることがよくあります。

DRYコードを使用するのは良いことですが、ビジネスロジックが例外を指示する場合があるため、以前は一般的であったソースから2ビット以上のコードを作成する必要があります。

じゃあ何をすればいいの?現状維持のコード(結局のところ [〜#〜] yagni [〜#〜] )。コードは簡単に変更できるように作成する必要がありますが、必要のないもののためにたくさんのベルとホイッスルを書くことは、お金を燃やすだけです。

10
Robbie Dee

質問では、プロジェクト管理の3つの機能(見積もり、スケジュール、および制御)のみをリストします。プロジェクト管理とは、プロジェクトの制約内で目標を達成することです。プロジェクトの制約内で目標を達成するために使用される方法は、ソフトウェアプロジェクトの場合、他の多くのタイプのプロジェクトとは異なります。たとえば、製造プロセスを高い再現性と十分に理解したいとします。ただし、ソフトウェア開発はほとんどが 知識作業 -それは非日常的であり、厳格な指示や手順に従うのではなく、考える必要があります。ソフトウェアプロジェクトの開始、計画、実行、監視、制御、および終了に使用される手法は、ソフトウェアプロジェクトで実行する必要がある作業のタイプ、具体的には実行できない非日常的な作業を説明する必要があります。特定の指示と手順に。

もう一つの問題は、情報の繰り返しに関連する概念であるDRYを取り入れ、それをタスクの管理に適用しようとしていることです。 DRYは、情報の信頼できる表現を1つだけ持つべきだと単に言っています。プロジェクトマネージャーはこれを採用する必要があります。これは、誰もが情報の入手先を知っていることを意味し、変更の伝達が容易になります。変更は適切に制御および管理できます。DRYは、再利用可能な部品により、長期的なコストを抑え、長期的なスケジュールを維持し、品質を向上させるのに役立ちます-3点 プロジェクト管理の三角形 。物事を効果的に乾燥させるために費やされる時間とお金の投資が必要ですが、プロジェクトマネージャーの仕事は、時間、コスト、スケジュール、および品質のトレードオフを行うことです。

8
Thomas Owens

DRYは便利ですが、過大評価されています。一部の人々はそれを取り過ぎることができます。多くの開発者が気づいていないのは、DRYを実装して2つの(わずかに)異なる目的で同じメソッドを使用するときはいつでも、さまざまな用途間に一種の非常に密な結合を導入していることです。最初のユースケースのコードを変更するときは、2番目のユースケースが後退するかどうかも確認する必要があります。これらが広く独立したユースケースである場合、緊密に結合する必要があるかどうかは非常に疑わしいです。

DRYの過度の使用は、一般的にいくつかのコードを複製するより小さなアトミックメソッドが多くなる場合、それらが置かれるすべての異なるユースケースを処理するために複雑に爆発する神の方法につながる可能性がありますより保守しやすい。

ただし、この質問はプロジェクト管理レベルではあまり関係がないと思います。プロジェクトマネージャーは、このレベルの実装の詳細を気にする必要はありません。もしそうなら、それはおそらくマイクロマネージメントです。本当に... how実装は開発者と技術リーダーの責任です。プロジェクト管理はwhatが実行され、whenがより重要になります。

編集:コメントごとに、ただし、推定開発時間を容易にする限り、DRYを回避することで不確実性の量を減らすことができる場合があります。しかし、これはは、(1)ビジネス要件が満たされるまでの期間、(2)プロセス内でどの技術的負債がかかるか、および(3)建築の総所有コストに対するリスクという、より差し迫った質問に関しては重要ではない問題です。選択されたもの-行くかどうかDRY多くの場合、それはプロジェクトの提供を少し簡単にするかどうかよりも、これらの要因に対するリスク/報酬に基づくべきである設計上の選択ですより正確な情報を持つマネージャー。

4
Brad Thomas

新しいコードを書くことはタスクのほんの一部です

あなたの提案は、最初に新しいコードを書く部分を推定することを容易にします。ただし、実際に何か新しいものを導入する場合(まったく新しいシステム、機能の追加、機能の変更にかかわらず)、これを行うだけでは不十分であり、作業のごく一部です-文献にある見積もりでは、これは実際には部分は全体の20%-40%のようなものです。

したがって、大部分の作業(最初の開発を実際に必要なものに適合させること、統合、テスト、書き換え、再テストを含む)は、見積もりが容易ではありません。反対に、DRYを意図的に避けて、その部分をより大きく、より困難にし、より多くの変数の見積もりを作成しました-すべての複製された部分を変更する必要があるバグまたは変更の必要性が発生しない場合があります、しかし、もしそうなら、あなたの推定値は完全に間違いになるでしょう。

仕事の小さな部分の見積もりの​​質を向上させることでより良い見積もりを得るのではなく、仕事の大部分でそれを悪化させる。したがって、これは実際にはトレードオフではなく、生産性が低下するだけでなく、見積もりが低下する負け負けの状況です。

4
Peteris

あなたはDRYを誤解していると思います。

例を使用してみましょう:

public Class A
{
    public int Multiply(int x, int y)
    {
        return x * y;
    }
}

public Class B
{
    public int Multiply(int x, int y)
    {
        return x * y;
    }

    public int Add(int x, int y)
    {
        return x + y;
    }
}

vs.

public Class C : A
{
    public int Add(int x, int y)
    {
        return x + y;
    }
}

クラスBをCで置き換えることにより、DRYの原則に従い、コードの重複を減らしました。ただし、プロジェクトへの未知数やリスクを増やしていません(以前に継承を行ったことがない場合を除く)。

DRYについて話すとき、あなたが何を意味しているのかというと、デザインタスクのようなものです。つまり、

public Class A
{
    public int Multiply(int x, int y)
    {
        return x * y;
    }
}

!!!新しい要件!一部のクライアントはダブルを乗算できる必要があります!!

// Use class B for new clients!!
public Class B
{
    public int Multiply(double x, double y)
    {
        return x * y;
    }
}

vs.

public Class A // Version 2
{
    public int Multiply(int x, int y)
    {
        return Multiply(x as double, y as double);
    }

    public int Multiply(double x, double y)
    {
        return x * y;
    }
}

ここで(それが機能すると仮定して)、新旧両方の要件に対処できるソリューションを設計し、基本的に実際の問題またはビジネスルールの数学モデルを作成しようとしました。実際には、モデリングしているシステムは明らかにはるかに複雑になり、そのモデルは正確に適合せず、エッジのケースと予期しない結果を見つけて修正するのに時間がかかります。

この場合、BまたはAバージョン2を使用する必要がありますか?

  • Bは、副作用が少なく、実際に要求された変更により具体的であり、推定がより簡単で、より迅速になります。

  • バージョン2では、全体的なコードが少なくなり、よりエレガントなソリューションになります

繰り返しになりますが、それは仕様と要件の品質にかかっています。

Edgeのケースと後方互換性をカバーする非常に明確な仕様がある場合、バグを発生させることなくモデルをリファクタリングするのに十分なシステムを理解していると確信できます。

システム全体を考慮せずにその顧客の行動が変化することが唯一の要件である単一の顧客に対する緊急の要求がある場合。次に、Aをリファクタリングしてモデルを「改善」すると、かなりのリスクが伴います。ソリューションの設計とテストに必要な未知の時間が余分にあるため、他の顧客を壊したり、期限を超過したりすることの両方。

2
Ewan

段落ごと

ソフトウェア開発の最も基本的で広く受け入れられている原則の1つは、DRY(繰り返さないでください)です。ほとんどのソフトウェアプロジェクトには、何らかの管理が必要であることも明らかです。

正しい。

次に、管理が容易なタスク(推定、スケジュール、制御)について教えてください。正しい、反復的なタスク、正確にはDRYに従って回避されるべきタスク。

反復的なタスクは自動化する必要があります必須。手作業で作成すると、退屈でエラーが発生しやすくなります。

そのため、プロジェクト管理の観点からは、既存のコードを100回コピーしてタスクを解決し、必要に応じて各コピーにいくつかの小さな変更を加えるのは素晴らしいことです。いつでも、あなたは自分がどれだけの仕事をし、どれだけ残っているかを正確に知っています。すべてのマネージャーはあなたを愛します。

「適応」という言葉を「構成」で変えることができると思います。コピーされることになっているこのコードの一部にバグがあると考えてください。特定の条件下で発生するバグ。元のソースで修正されておらず、コピーされている場合は、修正する場所がたくさんあります。これは悪いことかもしれませんが、その後誰かがしなければなりません:

  • 最初に元のソースのコードを修正します。
  • 他のすべての場所でコードを修正します。
  • それらがすべての場所であることを確認してください。あなたがこれをマネージャーに行わなければならなかったと言うとき、彼はおそらく誰かを憎むでしょう。

代わりにDRY原理を適用し、多かれ少なかれ重複するコードを排除する抽象化を見つけようとすると、状況は異なります。通常、多くの可能性があり、決定を下さなければならず、調査を行います、創造的です。より短い時間でより良い解決策を思い付くかもしれませんが、失敗する可能性もあります。ほとんどの場合、残された作業量を実際に言うことはできません。あなたはプロジェクトマネージャーの最悪の悪夢です。

重複を削除すると、単一点障害が発生します。何かが失敗した場合、これがどこで発生するかかなり確信で​​きます。固体。そして、デザインパターンは、その問題を正確に修正するのに役立ちます。期限が短すぎると、手続き型の「コーディング」が引き起こされる傾向があります。再利用可能なものを作成するために1つのプロジェクトに多くの時間を費やすことは、機能が再利用されるときに次のプロジェクトで費やされる時間が最小限であることを意味しますしかし、それは構成可能でなければなりませんそもそも。

もちろん大げさですが、明らかにジレンマがあります。私の質問は次のとおりです。開発者がDRYをやりすぎているかどうかを判断する基準は何ですか?どうすればよい妥協案を見つけることができますか?または、妥協点を見つけるだけでなく、このジレンマを完全に克服する方法はありますか?

多くの人がジレンマはないと指摘しました。はいといいえ。

あなたがこれまで行われたことのない非常に実験的なものを持っている場合-ジレンマはありません。それ以外の場合、新しい予約システムのように、もう一度実行する必要があるものがある場合は、すでに抽象化があり、必要なものに依存します。

ジレンマは、機能に何かを実装する必要がある場合、それが要求される可能性が低い場合です。要求されたときに何かを実装します。誰も使用しない巨大なインフラストラクチャを必要としません。

1
Bakudan

これは、将来の再利用のための設計やYAGNIの原則に関するものではありません。現在の作業パッケージでコードを繰り返すことについてです。

それは絶対にデザインについてです。たぶん再利用自体ではないかもしれませんが、それでもデザインします。

開発者がDRYをやりすぎているかどうかを判断する基準は何ですか?

経験と既存の環境/状況。与えられた問題について、より高い乾燥度を試みると、プラドの原則を強く感じるようになります。次に、突然、管理に関する考慮事項が出てきます。時間、目標、顧客、長期的なコード管理(誰かが技術的負債と述べた)などが攻撃の計画を通知します。

どうすればよい妥協案を見つけることができますか?

えっと…デザイン?リファクタリングは設計ですが、そうであるはずです。乾燥の範囲は、ループからメソッド、クラスへの超新星のように簡単に拡張できます。そこに行って、それをやった。しかし、問題を研究するまで実際に知ることはできません-これはデザインです。

どうして設計上の問題にならないのでしょうか?手元にあるすぐに複製されたコードよりも広く問題を考慮する必要があります。これは、既存のコードでも空白のシートでも、デザインアクティビティです。 「抽出メソッド」であるか、新しいクラスとモジュールを作成するか。

エピローグ

...参照された質問とその回答は、プロジェクト管理の側面をカバーしていません。

設計時間を無視した一般的な管理。理想的には、コーディングの前に、余分に冗長な反復を設計する必要があります。代わりに、経営陣は、開発(およびバグ修正)がオリンピックの1つのイベント-コーディング-であり、実際には十種競技であると考えています。そして、すべてがアナログだと思っているので、1/1000秒に測定します。

代わりに、DRY=原則を適用し、多かれ少なかれ重複するコードを排除する抽象化を見つけようとする場合、状況は異なります。

私はこの経験をしました。「この行(GUIフォームの)を2日間書き、残りのフォームを2時間書きました。」時間をかけて再利用可能なクラスを特定したことを意味します-DRYは自然な副作用です-GUIフォーム行と他のいくつかのw/inを含みます。デバッグされると、これらは個別におよび構成で使用されました、非常に高速にコード化されたフォーム全体で、テストは複雑さを増しているにも関わらず非常に高速でした。また、驚くほど低いバグ率で正式なテストも通過しました。

ほとんどの場合、実際にどれだけの作業が残っているかはわかりません。あなたはプロジェクトマネージャーの最悪の悪夢です。

どちらも知りませんでしたが、事前の設計努力が報われると確信していました。私たちは皆これを言いますが、特に経営陣はそれを信頼しません。経営者は私がいたずらしていると思っていただろう。 「2日間で、まだ2%もコーディングされていません!」

あるケースでは、経営陣が「設計に多くの時間を費やしている、始めよう」と言ったとき、私たちは銃にこだわりました。そして、同僚は「クラスが多すぎる」と言っています。まあ、それほど複雑ではないサブプロジェクトは約1か月かかるはずでした(私はそれは大丈夫だと思いました)5か月かかりました。それがそのようなPOSだったので、その3か月はテスト/修正にありました。 「しかし、設計する時間はありませんでした!」。彼らは実際にそう言った。

私の質問は次のとおりです。開発者がDRYをやりすぎているかどうかを判断する基準は何ですか?どうすればよい妥協案を見つけることができますか?または、妥協点を見つけるだけでなく、このジレンマを完全に克服する方法はありますか?

それがどのように機能するかを経営陣に示す。データをキャプチャします。他の仕事、特に平手打ちラッシュの仕事をしている同僚の仕事と比較してください。その失敗の山は常に競争を失い、テストに行き詰まり、リリース後に何度も何度もバグを修正するために戻ってきました。

1
radarbob