より良い生産性を得るために愚かですか?


112

私は「良いデザイン」、「デザインパターン」などに関するさまざまな本を読むのに多くの時間を費やしました。私は、SOLIDアプローチの大ファンであり、簡単なコードを書く必要があるたびに、未来。したがって、新機能またはバグ修正を実装するために、次のような3行のコードを追加するだけでよい場合:

if(xxx) {
    doSomething();
}

この方法でやるという意味ではありません。このコードが近い将来大きくなる可能性が高いと思う場合は、抽象化を追加したり、この機能を他の場所に移動したりすることを考えます。私が追求している目標は、平均的な複雑さを変更前と同じに保つことです。

私は、コードの観点から、それは非常に良いアイデアだと信じています-私のコードは決して十分に長くはなく、クラス、メソッド、クラスとオブジェクト間の関係などのさまざまなエンティティの意味を理解するのは非常に簡単です。

問題は、時間がかかりすぎることです。その機能を「そのまま」実装した方が良いと思うことがよくあります。「3行のコード」と「新しいインターフェイス+そのインターフェイスを実装するための2つのクラス」だけです。

製品の観点から(結果について話しているとき)、私がすることはまったく無意味です。次のバージョンで作業する場合、優れたコードを持つことは本当に素晴らしいことを知っています。しかし、一方で、コードを「良い」ものにするために費やした時間は、いくつかの便利な機能の実装に費やされた可能性があります。

私はしばしば結果に非常に不満を感じます-Aしかできない良いコードは、A、B、C、Dをできる悪いコードより悪いです

このアプローチは、ソフトウェアプロジェクトにプラスの純利益をもたらす可能性がありますか、それとも時間の無駄ですか?


141
私はあなたのSOLIDを見て、あなたにYAGNIとKISS rasieます
JKを。

16
ブーム、ヘッドショット。
ロバートS.

16
「オーバーエンジニアリング」の問題は、正しく機能していない要件キャプチャプロセスの現れである場合があります。「what-if's」に苦労していて、利害関係者/クライアントとのやり取りなしに自分で答えるなら、それは未来を予測する立場にあなたを置くでしょう。おそらく、その努力の一部を取り、コードをより複雑にする前に、要件をよりよく理解することに戻しますか?
アンジェロ

4
多分それは私だけだが、私は私のクライアント/雇用主は、彼らが/要求しなかった何かを持っているためにお金を費やす作る「愚か」検討するにはしたい:S
トーマスBonini

2
実際に必要なときに将来的に機能を追加することはほとんど難しくありません。今それをしても何も得られません。
Hardwareguy

回答:


153

Aしかできない良いコードは、A、B、C、Dができる悪いコードよりも悪い

これは、投機的な一般性のような匂いがします。クライアントが機能B、C、およびDを必要とすることを知らない(または少なくとも合理的に確信している)ことなく、設計を不必要に複雑にしすぎています。長い目で見れば、より複雑なコードを理解し、維持することは困難です。余分な複雑さはのみによって正当化される便利な追加機能。しかし、私たちは通常、将来の予測が非常に苦手です。私たちが考える機能のほとんどがあり、将来的に必要になることは、これまで実際の生活の中で要求されることはありません。

そう:

Aしか実行できない良いコード(ただし、その1つのことを簡単かつきれいに実行している)は、A、B、C、Dを実行できる悪いコードよりも優れています(そのうちのいくつかは将来必要になるかもしれません)。


87
1ユーザーが最も可能性の高い:) Eをお願いします「私たちは通常、将来を予測するのは非常に悪い」のための
のMichałPiaskowski

1
+1同意できませんでした。私は最近、ある会社で勤務期間を終えましたが、これは私が学んだ最も重要な教訓だと思います。
Wipqozn

4
@Michał、それは予測ですか?;-)
ペテル・トレック

10
彼らはDをお願いしている場合でも、彼らはおそらくそれの異なるメンタルモデルを持っています、そして、あなたの抽象化が...サポートしていないDのわずかに異なる種類を尋ねてきます
クリス・バート・ブラウン

「問題が完全に理解されていない場合、おそらくソリューションをまったく提供しないことが最善です」または「実装者がそれなしでは実際のアプリケーションを完了できない場合を除き、新しい機能を追加しないでください。」ここに適用されます。経由en.wikipedia.org/wiki/X_Window_System#Principles
nwahmaet

87

逸話の時間:

この方法でオーバーエンジニアリングに傾倒した2人の開発者が私のために働きました。

そのうちの1人の場合、これは基本的に、特に新しいプロジェクトを開始するときに、生産性を停止させます。特に、プロジェクトがその性質上、かなり単純だった場合。最終的には、現在動作するソフトウェアが必要です。これがひどくなり、私は彼を行かせなければならなかった。

過剰なエンジニアリングになりがちだった他の開発者は、非常に生産的であるためにそれを補いました。このように、すべての無関係なコードにもかかわらず、彼はまだほとんどよりも速く配信しました。しかし、彼が先に進んだ今、抽象レイヤーなどを変更する必要があるために機能を追加するために必要な追加の作業量にイライラすることがよくあります。

だから、モラルは、過剰なエンジニアリングは、有益なことをするのによりよく費やすことができる余分な時間を使い果たしてしまうということです。そして、あなた自身の時間だけでなく、あなたのコードで作業しなければならない人々の時間も。

そうしないでください。

コードをできるだけシンプルにしたい(そしてよりシンプルにしたくない)。「maybes」の処理は、物事を簡単にするものではありません。間違っていると思うと、コードをより複雑にし、それを示すための本当の利益はありません。


10
+1は可能な限りシンプルですが、シンプルではありません。
ジュリアン

33
「デザイナーは、追加するものが何もないときではなく、奪うものが何もないときに完璧を達成したことを知っています。」アントワーヌドサンテグジュペリ
アルク

ペストのような重複を避ければ、あなたは大した間違いをしないでしょう。
ケビン

@arkh ...私は同じ引用を使用するつもりだった:P
マイケルブラウン

6
私はこのように言います:すべてのコード行はそれに関連する高いコストがかかります。したがって、コードを最小限に抑えることでコストを最小限に抑えます。不要なコードを削除することは、新しいコードを書くことよりも生産的です(さらに生産的かもしれません)。
クリストファージョンソン

26

SOLIDとKISS / YAGNIの原則は、極に近い反対です。doSomething()を実行するジョブの不可欠な部分としてdoSomething()を正当化できない場合は、疎結合されて注入される別のクラスにある必要があることがわかります。もう1つは、これがdoSomethingが使用される1つの場所である場合、メソッドにそれを抽出することでさえもやり過ぎである可能性があることを示します。

これが、優れたプログラマーを金の重さに見合うものにする理由です。「適切な」構造はケースバイケースであり、現在のコードベース、プログラムの将来のパス、およびプログラムの背後にあるビジネスのニーズに関する知識が必要です。

この単純な3段階の方法論に従うのが好きです。

  • 最初のパスで、それを機能させます。
  • 2回目のパスで、きれいにします。
  • 3番目のパスで、SOLIDにします。

基本的に、これがKISSとSOLIDのブレンド方法です。最初にコード行を記述するとき、知っているすべての人にとって、それは1回限りのことです。単純に機能する必要があり、誰も気にしないので、空想にならないでください。コードのその行にカーソルを2回目にすると、元の仮説に反することになります。このコードを再検討すると、おそらくどこかからコードを拡張したり、フックしたりすることになります。ここで、少しクリーンアップする必要があります。一般的に使用されるヒントのメソッドの抽出、コピー/貼り付けコーディングの削減または削除、コメントの追加などを行います。このコードに再度アクセスすると、プログラムの実行パスの主要な交差点になります。今、あなたはそれをあなたのプログラムの中核部分として扱い、可能であればSOLIDルールを適用すべきです。

例:簡単なテキスト行をコンソールに書き込む必要があります。これが初めて発生した場合、Console.WriteLine()は問題ありません。次に、新しい要件が同じ行を出力ログに書き込むことを指示した後、このコードに戻ります。この単純な例では、繰り返しの「コピー/貼り付け」コードはそれほど多くないかもしれませんが(または、あるかもしれません)、基本的なクリーンアップを行うことができます。 。次に、クライアントが監視サーバーの名前付きパイプに同じテキスト出力を必要とするときに戻ります。さて、この出力ルーチンは大したことではありません。同じテキストを3つの方法でブロードキャストしています。これは、複合パターンの恩恵を受けるアルゴリズムの教科書の例です。Write()メソッドを使用して簡単なIWriterインターフェイスを開発します。そのインターフェイスを実装して、ConsoleWriter、FileWriter、NamedPipeWriterクラスを作成し、もう一度「MultiWriter」複合クラスを作成してから、クラスのIWriter依存関係を公開し、3つの実際のライターでMultiWriter複合を設定し、プラグインします。今、それはかなり固いです。この点から、要件が出力が新しいどこにでも行くように指示するときはいつでも、新しいIWriterを作成してMultiWriterにプラグインするだけで、既存の作業コードに触れる必要はありません。


同意しますが、通常、最初のステップを通過すると、2番目または3番目に戻ることはありません。これは、機能が「ライブ」になり、パイプの下にさらに多くの機能があるため、戻って修正できないためです。最初の機能。あなたは見つけることのすべてを行うことができますが、との最初のステップであり、すべての機能と、あなたが沼に残されています。
ウェインモリナ

2
@Wayne M-ウォーターフォールSDLCまたは短時間のシナリオでは、そのように起こる可能性があります。これらの場合、期限までに元の仕様に従ってジョブを実行することが評価されており、コードの保守性ではありません。ソースコードの品質を重視する場合、リファクタリングのスケジュールに時間を組み込む必要があります。書面による情報の作成を含む他の仕事でコンテンツの品質を重視する場合と同様に、校正と編集に時間をかけます。
キース

あなたのオープニングラインは誤解を招く-彼らは反対していると言うし、一緒にそれらを使用するための最良の方法を説明します。悪いラインに賛成票を投じるべきか、良いアドバイスに賛成票を投じるべきかはわかりません。
ショーンマクミラン

1
私は自分自身に矛盾するとは思わない。それらは極に近い反対です。どちらか一方を宗教的に遵守することは、必ず他方を拒否することを意味します。ただし、それらが相互に排他的であることを意味するものではありません。どちらの方法論にも100%従うことを選択する必要はありません。それが答えの残りのポイントでした。各原則の結論からのギブアンドテイクを伴う場合、どのようにバランスを取るのですか?
キース

本当に素晴らしいプロセス:動作し、きれいで、固い。これの帰結は「最初にハックされたプロトタイプなしで何かを試みたり、設計したりしないでください」かと思います。
スティーブベネット

17

Aしかできない良いコードは、A、B、C、Dができる悪いコードよりも悪い

1)想定されることのみを行うコードを用意します。

このコードが近い将来大きくなる可能性が高いと思う場合は、抽象化を追加したり、この機能を他の場所に移動したりすることを考えます。

2)A、B、C、Dを実行するコードを計画する場合、顧客は最終的にEを要求します。

あなたのコードはするべきことをするべきであり、あなたはコードを継続的に変更することを決して終わらせないであろうし、あなたのコードを過剰に設計することがより重要なので、将来の実装について今考えるべきではない。もちろん、プロジェクトアーキテクチャの一部として計画していない限り、現在の機能のためにコードが必要だと感じたらすぐにリファクタリングする必要があります。

良い本を読むことをお勧めします:The Pragmatic Programmer。それはあなたの心を開き、あなたがすべきこととすべきでないことを教えてくれます。

また、Code Completeは、すべての開発者(プログラマーだけでなく)が読むべき有益な情報に満ちた素晴らしいリソースです。


12

非常に簡単なコードを書く必要があるとき、将来について考えます

ここに問題があるのか​​もしれません。

初期段階では、最終製品がどうなるかはわかりません。または、それを持っている場合、それは間違っています。確かに。数日前にProgrammers.SEで尋ねた14歳の少年のようです。将来のキャリアのために、Webアプリを選択する必要があるかどうか、他に何を覚えていないか、数年後には、彼は変わり、彼は他のドメインなどに興味を持ちます。

3行のコードを作成するために、新しいインターフェイスと2つのクラスを作成すると、オーバーエンジニアリングになります。有用なコード行ごとに、不要なコード行が2行あるという理由だけで、保守が難しく読みにくいコードが得られます。XMLドキュメント、単体テストなどをカウントしない

考えてみてください:コードに機能がどのように実装されているかを知りたい場合、20行のコードを読みやすくするか、半空のクラスを数十個開き、どちらがどれを使用しているか、どのように関連しているかなどを把握するためのインターフェース

覚えておいてください:コードベースが大きくなると、メンテナンスが増えます。回避できる場合は、それ以上コードを書かないでください。

あなたのアプローチは他の側面でも有害です:

  • 機能を削除する必要がある場合、何十ものクラス間の依存関係を理解するのに時間を費やすよりも、特定の20行のメソッドがどこで使用されているかを把握する方が簡単ではありませんか?

  • デバッグするとき、小さなスタックトレースを取得する方が簡単ではありませんか?何によって何が呼び出されているかを把握するために、何十行も読みたいですか?

結論として、それは時期尚早な最適化に似ているようです。そもそも問題があるのか​​、どこにあるのかわからないまま、問題を解決しようとしています。製品のバージョン1を使用する場合は、すぐに実装する必要がある機能を実装してください。バージョン14で2年以内に実装する予定のものについては考えないでください。


「セミエンプティクラスの数十」には、これらのクラスが何のために良いのかを理解するのに十分な資料がないという追加の欠点があります。
gnasher729

5

(おそらく)決して使用されない多くのコードを書くことは、P45で発行される非常に良い方法です。あなたは水晶玉を持っていませんし、開発がかかる最終的な方向についての見当がつかないので、これらの機能に時間を費やすことは、リターンなしのお金がかかるだけです。


3

将来、コードから何が必要になる可能性があるかを予測しようとすると、多くの場合、不必要なオーバーエンジニアリングになります(私は現在、振ろうとしている習慣です)。私は3行だけを行うと言うでしょう。必要が生じた場合(以前ではない場合)、リファクタリングします。このようにして、コードは複雑になることなく常に必要なことを行い、リファクタリングによって自然に良い構造を進化させます。


3

私はしばしばコーディングはフォースの明るい側/暗い側に似ていると言います-「明るい」側はより多くの努力を必要としますが、より大きな結果をもたらします。「ダーク」サイドは迅速かつ簡単で、すぐに大きなメリットが得られますが、道を踏み外してしまいます。暗い道をスタートすると、永遠にあなたの運命を支配します。

私はこれまでずっと、これまで出会ったすべての仕事で、逃げられない呪いのようなものに遭遇します。会社の文化は常に暗黒面のパスであり、新しい機能をプッシュするための迅速なハック/修正があります。 「ものを変えようとする」(デザインパターンを導入し、迅速なハックから離れたいと思ったので、それはほんの数回発生した冗談ではありません)。

悲しい真実は、多くの場合、愚かな/暗い側の道はあなたが踏まなければならない方法であるということです、あなたはただ軽く踏むことを確実にしなければなりません。ソフトウェアを書く正しい方法を理解しているプログラマー、つまり、SOLIDに従う、デザインパターンを使用する、SoCに従うなどif、バグを修正するためのステートメントを書く無知なハックよりもはるかに一般的ではないことに気づきました。より多くのバグが発生した場合、「これを行うにはもっと良い方法があるかもしれない」と考え、適切に拡張できるようにコードをリファクタリングする代わりに、そのステートメントに追加するだけです。


3
ifIAbstractBugFixerからよりも保守がずっと簡単IAbstractBugFixerFactoryです。そして、IFを追加したらif、リファクタリングを行います。設計パターンとSOLIDは、アーキテクチャフェーズでは非常に重要ですが、製品が既に実行されており、すべてのチームメンバーが同意した1つのスタイルで記述されている場合は重要ではありません。
コーダー

@Coderアーキテクチャはいつでも変更できないと想定しないようにしてください。できるし、する。
リッキークラークソン

1
ウェインM、あなたの仕事の状況に共感できます。フォースと一緒にいてください。:)
ジェニファーS

3

何が起こるか(将来)を意識することは決して悪いことではありません。何かをするためのより良い方法であるかもしれないものについて考えることは、あなたの仕事を上手にするものの一部です。難しい部分は、費やした時間:ペイオフ率が正当化されるかどうかを判断することです。人々が「簡単に」を実行して即時出血(および/または大声で叫ぶ)を止める状況をすべて見てきました。また、私たちの多くは、元のコーダーが移動すると謎であるやり過ぎの抽象化を経験しており、これも混乱するコードを生成します。

私はあなたの状況を見て、これらの質問をします:

  1. このコードはミッションクリティカルなものであり、コードを書き直すと大幅に安定しますか?手術で言えば、これは人命救助のリファクタリングですか、それとも単に選択的で化粧品ですか?

  2. 6か月後に置き換えるコードのリファクタリングを検討していますか?

  3. リファクタリングに費やすのと同じくらいの時間をかけて、設計と将来の開発者に対する自分の推論を文書化することに時間をかけるつもりはありますか?

  4. 将来の機能を追加するためのエレガントなデザインに関して、これはユーザーが毎週変更を要求するコード内ですか、それとも今年初めて触ったのですか?

YAGNIとKISSがその日を勝ち取る時がありますが、根本的な変化があなたをクラッピーへの下降スパイラルからあなたを引き離す日があります。あなたが望むものだけでなく、他の人があなたの仕事を維持するために何をしなければならないかについてあなたの評価において現実的である限り、あなたはどの状況がどれであるかをよりよく決定することができるでしょう。ああ、あなたがしたこととその理由を書き留めることを忘れないでください。あなたをフォローしている人だけでなく、後で戻って来なければならないときにもあなた自身を救います。


3

Stroustrupsの第2版「C ++プログラミング言語」で(このページは利用できません)を読みました

自発的にコードを追加しないでください。

アドバイスに従って順調に進みました。もちろん、トレードオフがあり、バランスを見つける必要がありますが、短い断片は大きなスパゲッティの混乱よりもテスト可能です。

私はよく、1つのケースから2つのケースに区別しながら、2をnケースと考えると、多くの新しい可能性への扉を開くことを経験しました。

しかし、その後、YAGNIの質問をする必要があります:それは価値がありますか?本当に役立つのでしょうか?経験を積むということは、あなたが間違っていることはめったになく、初心者としてより間違っていることを意味します。

パターンを認識し、抽象化が多すぎるためにコードの保守が困難であるか、すべてが適切に解決されるために保守が困難であるかを検出するのに十分な重要性が必要です。

解決策はこれまたはそれではなく、それについて考えることです。:)


2

「Aしかできない良いコードは、A、B、C、Dができる悪いコードよりも悪い」

これは、製品開発において何らかの意味があります。しかし、ほとんどのITプロフェッショナルは、製品開発ではなく「プロジェクト」で働いています。

「ITプロジェクト」では、優れたコンポーネントをプログラムすると、プロジェクトの存続期間はスムーズに機能します。5年または10年より長く実行されることはなく、ビジネスシナリオは時代遅れになり、新しいプロジェクト/ ERP製品が交換した可能性があります。この5/10年の寿命の間に、コードに欠陥がなければ、誰もそれが存在していることに気付かないでしょうし、あなたの最善の考えのメリットが見過ごされてしまったのです!(自分のトランペットを大声で賭けるのが得意でない限り!)

「Windows Ctl + Alt + Del」をプログラムする機会を得る人は多くありません。そのような人は、コードの将来の可能性に気付かないかもしれません。


1

リーン開発やアジャイル開発に関する多くの本は、このアプローチを強化するのに役立ちます。今必要なことをしてください。フレームワークを構築していることがわかっている場合は、抽象化を追加します。それ以外の場合は、必要になるまで複雑さを追加しないでください。Lean Software Developmentをお勧めします。これにより、生産性に実質的な違いをもたらす他の多くの概念が導入されます。


1

人々が物事の正しい方法/間違った方法について話す方法は面白いです。同時に、プログラミングのタスクは依然として非常に困難であり、大きな複雑なシステムを作成するための優れたソリューションを提供しません。

いつか私たちプログラマーが最終的に複雑なソフトウェアの書き方を見つけ出すかもしれません。それまでは、まず最初に「愚かな」プロトタイプの実装から始めてから、リファクタリングに十分な時間を費やして、大学がコードをフォローできるようにすることをお勧めします。


1
ほとんどの場合、リファクタリングのための特別な時間はありません-それがおそらくこれらすべての主な理由です「それを再実装することなくこれを実装することはできません」;-)最初から正しい方法で行う、または常に間違った方法で実行します。
アンドレイアギバロフ

@ loki2302:新しいコードを書くことは常に簡単であることを忘れないでください。愚かなコードをプロトタイピングすると、2倍の速度になります。その後、約半分の時間で生産性がゼロに低下します。したがって、最終的には、プログラマーが正しい方法で設計しようとするのと同じくらい速くなります
。– AareP

1

後で来た実際の要件にまったく適合しないような、時期尚早に一般化された設計を見たので、私はルールを考案しました:

架空の要件については架空のコードのみを記述してください。

つまり、後で発生する可能性のある変更について考えることをお勧めします。しかし、これらの洞察を使用して、コードの設計を選択し、それらの要件が実際に生じた場合に簡単に変更およびリファクタリングできるようにしてください。その場合に書きたいコード(仮想コード)を頭に書くこともできますが、実際のコードは書きません。


0

あなたを助ける考え方は、抽象的な解決策ではなく、コーディングの問題に対する具体的な解決策を常に模索することだと思います。抽象化は、実際にコードベースの簡素化に役立つ場合(たとえば、コードベースを小さくできる場合)にのみ追加する必要があります。

多くのプログラマーは、コードを乾燥させると、抽象化がほとんど独力で現れることを発見しました。デザインパターンとベストプラクティスは、まさにそれを行う機会を見つけるのに役立ちますが、それだけでは追求する価値のある目標ではありません。


0

過剰なエンジニアリングは、コードの記述に関する不安から生じることが多いと思います。すべての抽象的な原則とパターンは、あなたを助けるツールとして扱われるべきです。頻繁に起こることは、それらが準拠しなければならない標準として扱われることです。

プログラマは、公理よりも抽象化の方法を決定するほうが常に良い立場にあると信じています。

残りはすでにKeithSによって言われています


コードの記述について自己セキュリティを確保する1つの方法は、Linuxシステムでrootとしてコーディングすることだと思います。willy-nilly、boomと入力すると、VMのイメージを再作成する必要があります。あらゆる種類の良い習慣をすぐに教えます。箱が実際のインターネットにあることを確認し、ログを確認してください。(ssh、http)これらも本当に楽しいです!
クリストファーマハン

私のバージョンは:あなたが理解していない原則を無視します。あなたがそれらを使用することにした場合、あなたが運動するよりも真剣にそれらを扱わないでください。
-sabof

0

優れたデザインの利点は次のとおりです。

  • 理解しやすい
  • メンテナンスが簡単
  • ポータブル
  • 長く使える
  • 新機能を簡単に追加

ここで、これらの抽象化レイヤーをすべて追加すると、上記のポイントのいずれかに実際に追加されるかどうかを自問してください。そうでない場合は、間違っています。

次のような3行のコードを追加して、新しい機能を追加できる場合:

if (condition()) {
  doSomething()
}

その後、お願いします。これは、以前の設計が適切で、適応が容易であることを示しています。クラスが特定の範囲を超えて成長し始めたら、リファクタリングを使用して関数を分割し、場合によっては新しいクラスを抽出します。

私の経験則では、新機能は可能な限り最小限に実装する必要があります。何かを事前に把握するのが大きすぎる場合(たとえば、実装に1日または半日以上かかる場合)にのみ、大まかなグローバルデザインを追加できます。以降は、コードのサイズが大きくなったときにのみ抽象化レイヤーを追加します。その後、リファクタリングします!しばらくして、もう少し作品をデザインしたり、急いで進んだりする必要が生じたときに、それが自然になってくるはずです。一部のコードが何らかのクリーンアップを使用できる別の兆候は、それを再利用するときです。新しい機能を追加したり、新しい場所で古いコードを呼び出したりするたびに、古いコードを調べて、もう一度追加する前に少し改善できるかどうかを確認してください。この方法で、ホットコードはゆっくりとよりきれいになりますが、関心のない部分はゆっくりと腐敗し、貴重な時間を費やすことはありません。

このように作業する場合、何かを過剰に設計することはありません。何か新しいものを追加したいときは古いコードに戻るか、あなたが好きなものよりもややleaveい新しいコードを残すにはある程度の規律が必要かもしれませんが、過剰に設計されたのではなく生産的なものに取り組んでいます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.