r/programming🔥 98
💬 59

コピペは悪?重複コードが必ずしもアンチパターンとは限らない理由

Exact_Prior6299
約1か月前

ディスカッション (59件)

1
tadfisher
👍22約1か月前

Mediumの記事にある、5段落構成のやつね。

2
myowndeathfor10hours👍 61
約1か月前

ここでもよく言われてるけど、何度見ても共感する。DRY原則は過剰適用されると、マジで問題の元になるんだよね。

3
Wonderful-Citron-678
👍8約1か月前

それってどんな時に経験した?オレは何十個ものプロジェクトに貢献してきたけど、DRY原則はいつも良い結果をもたらしてくれたよ。オンラインで見る例って、学校の宿題みたいなのばっかりだし。

4
jbmsf
👍18約1か月前

DRY原則は、ほとんどの人が見つけやすい、一番簡単な「デザインパターン」の解決策だから、一番使われるんだよね。その失敗パターンとしては、不必要な結合、早すぎる一般化、カプセル化の破綻、とかがある。

5
Wonderful-Citron-678
👍1約1か月前

問題点は理解できるんだけど、今まで経験した限りじゃ、常識でなんとかなっちゃうんだよね。C言語をよく書くから、そもそも抽象化に限界があるってのもあるかも。でも、C++やPythonでも同じような問題はあんまりないかな。

6
turudd
👍2約1か月前

大体の場合、無駄な時間を使うのは、中途採用されたばかりの、カスタムライブラリを作って、ちょっとでもコードが重複してるとリファクタリングしたがるシニア連中なんだよね。

2回しか繰り返されてないのに、ユニットテストしてみたら、実はちょっと違ってて、結局元に戻す羽目になる。でも、簡単だと思ったから、ソフトウェアの実際のバグ修正のためにPRに含めた他の変更を、cherry-pickで取り除こうとするんだよ…。

それで、「テーブルに新しいヘッダーを追加して、APIコールを2つ追加するのに、なんで16時間もかかったんだ?」って聞くことになるんだよね。連中がモジモジしてるのを見るのが楽しい。何をやったか正直に認めたらボーナスポイントだわ。それはそれで評価する。

7
HAK_HAK_HAK
👍4約1か月前

>テーブルに新しいヘッダーを追加して、APIコールをいくつか追加するのに、なんで16時間もかかったんだって聞くことになるんだよね。

マネージャーかチームリーダーでもない限り、あなたには関係ないことだよ。この行動が示しているのは、技術的負債を解決するよりも無視するチーム文化だよ。開発者は、機能開発中に技術的負債を解決する必要があると感じるべきじゃない。もし、技術的負債の解消作業が軽視されて、全然終わらないなら別だけど。

8
RICHUNCLEPENNYBAGS
👍1約1か月前

この記事が参考になるかも:https://ericlippert.com/2015/04/23/dry-out-your-policies/

要するに、DRY原則が重要なのは「信頼できる情報源」とかビジネスロジックの話であって、単なる汎用的なメカニズムなら、むしろ手間が増える可能性があるってこと(多くのプロジェクトで使うライブラリを立ち上げることになった場合は特に)。

9
editor_of_the_beast
👍17約1か月前

良い子も一緒に風呂の水と捨てちゃダメだよ。DRY原則が誤用されることもあるけど、90%の場合はマジでマジでマジでロジックの重複は避けるべき。

10
Kind-Armadillo-2340
👍2約1か月前

もっと頻度高いよ。DRY原則を適用して後悔したことなんて一度もない。もし抽象化した重複ロジックが、実は抽象化すべきじゃなかったって判明したとしても、修正すればいいだけだし。

11
stingraycharles
👍12約1か月前

エレガントな抽象化を発明しようとして、結局うまくいかなかった経験をたくさん積むと、そういうことに気づくんだよね。

チームのメンバーにはいつも、経験則として3回の重複を基準にしろって言ってる。それ以上になったら、一般化を検討し始めてもいい。そうすることで、本当に必要な抽象化について、より深く理解できるようになるんだ。

コードのコピーは、生産性とコード品質の面で過小評価されてると思う。

12
Kind-Armadillo-2340
👍1約1か月前

DRYって、エレガントな抽象化を作るって意味じゃないんだよ。ちょっとでも重複したロジックを見つけたら、関数でラップして2回呼ぶだけ。プログラミングで一番簡単なことの一つだし、後でそれが間違いだったって分かったら、関数を削除して、必要なロジックを書き出せばいいだけ。これもまた、プログラミングで一番簡単なことの一つだよ。

13
stingraycharles
約1か月前

それって単純化しすぎじゃない?入力型が違う場合はどうするの?コードの別の場所にある場合は?とか。

14
Elathrain
👍12約1か月前

そんなに違ってたら、それは重複とは言えないでしょ。

15
Kind-Armadillo-2340
👍3約1か月前

入力型が違う場合はどうするの

完全に異なる入力型?だったらロジックは重複してないよね。

コードの別の場所にある場合は

関数をutilsパッケージに入れて、両方の場所でインポートすればいいじゃん。

16
stingraycharles
約1か月前

完全に異なる入力型?だったらロジックは重複してないよね。

それは違うな。完全に異なる型で動作するロジックを簡単に持つことができるよ。

17
Kind-Armadillo-2340
👍3約1か月前

すごく特殊な状況っぽいけど、関係のない型を同じ関数に渡すべきじゃないよ。たとえそれが許される言語を使ってたとしてもね。型が同じ型階層に属してないとか、同じプロトコルに従ってないなら、似たような処理に見えても、それらを別個のものとして扱うべき。そうじゃないと、ミスにつながるよ。

18
RationalDialog
👍3約1か月前

utils パッケージの中で

それ自体がめっちゃ厄介な問題の種だよね。

19
JarredMack
👍13約1か月前

完全に問題を誤解してるね。これ、ジュニアからミドルレベルの開発者がよくやらかすミスなんだ。

問題は、重複した振る舞いを抽象化して再利用することじゃない。そうじゃなくて、一見重複しているように見える(最初は実際重複してることも多い)けど、実は別々のビジネスケースのための振る舞いを抽象化しちゃうことなんだ。善意の開発者が抽象化して、後から機能を追加していくうちに、その抽象化が「if (コードパス1) else (コードパス2)」みたいなひどいことになるんだよ。

で、「書き直せばいいじゃん」って言うのは簡単だけど、そういう機能って12ヶ月後とかに追加されて、抽象化について何も知らない別のチームが実装したりするんだよね。2週間かけて解きほぐすより、3行の変更を加えてチケットをクローズしちゃうんだ。

20
startwithaplan
👍45約1か月前

HUMID - ダブりが複数確認できるまで、とりあえず保留!

21
TulipTortoise
👍3約1か月前

ボンドさん、シカゴにはこういう言い方がある。「一度は偶然。二度は一致。三度目は敵の行動」

22
shogun77777777
👍3約1か月前

関数の入力、出力、挙動がちゃんと定義されてれば、そんなことないけどね。

23
Venthe
👍1約1か月前

いつものリマインダー:DRY原則はコードの重複じゃなくて、知識の重複を避けること。

24
pohart🔥 165
約1か月前

俺は1回だけなら繰り返してもいいと思ってる。2回で抽象化しようとすると、何が本質的に共通で、何が偶然共通なのかが分かりにくいんだよね。3回目になったら、_実際の_パターンが見えてくる。

25
OrchidLeader👍 56
約1か月前

同僚が、ユースケースが1つしかないのに「念のため」って言って、抽象化に余計なスプリントを費やしてる時の俺:ಠ_ಠ

26
RationalDialog
👍6約1か月前

読みやすくするために、明確な機能を持つコードブロックを「抽象化」することがあるよ。長い関数は好きじゃないんだ。

27
dyingpie1
👍3約1か月前

マジそれな。セクションごとに何やってるかコメントで区切るくらいなら、関数を複数の関数に分割する方が好き。

28
ydieb
👍1約1か月前

それは唯一のルールじゃなくて、一つのルールにすぎない。もしかしたら唯一のルールは「凝集度が高く疎結合」なコードを書くことかもしれないけど、それをどう適用すればいいのかわからないかもしれない。
でも、抽象化によって、他の部分が知る必要のない詳細を明確に分離できる場合もある。実際の重複がなくてもね。

29
Stevoman
👍15約1か月前

マジそれな。昔、めっちゃデカいIT企業(GAFAMじゃないけど、名前は聞いたことあるはず)で働いてたんだけど、チームで「3回ルール」ってのがあってさ。3回繰り返されるまで抽象化すんな、ってやつ。

30
ahal
👍42約1か月前

それってWET(同じことを何度も書く)アプローチってやつだね。

31
pohart
👍2約1か月前

そのネーミング、いいね!

32
spacelama
👍35約1か月前

電子エンジニアが書いたコードを見たことがあるんだけど、RS-232インターフェースから入力を受け取って処理するやつで、どのアクションの最初にも約10個の関数呼び出しがあって、カスタムのシリアルコマンドに依存したアクションがあって、その後にもまた10個の関数呼び出しがあるんだ。あるアクションでバグを見つけてコードを調べたら、修正内容がわかったんだけど、ページを下にスクロールしたら、他の45個くらいの関数がほとんど同じに見えたんだよね。でも、いくつかにはもう修正が入ってたりするんだ。で、さらにいくつかには別のバグがあったり。

それで、コードのブランチを消して立ち去った。

でも、「全部46回書く」っていうのに合う良い略語が思いつかない。

33
improbablywronghere
👍20約1か月前

リファクタリング中に「いや、やっぱいいや」ってなる瞬間、マジで最高にピュアだよね。

34
diMario
👍3約1か月前

このアクロニムは bana ね。

bananananananananananananananananana のスペルはわかるけど、

どこで止めればいいかわからない」みたいな。

35
Mount_Everest
👍2約1か月前

LLMにやらせる理想的なユースケースだね。

36
Determinant
👍7約1か月前

もっと上品に言うなら、「ちょっと湿ってる」方がDRY(Don't Repeat Yourself)よりマシってことだね。

37
IQueryVisiC
👍1約1か月前

そして、クリーンコードのおかげで、重複はほんの数行のコードで済むんだよね?

38
Kind-Armadillo-2340
👍6約1か月前

この考え方には正直、賛同できないんだよね。コードなんて簡単に変えられるじゃん。もし、2箇所で共通のロジックを抽象化した関数を書いて、それが実は今後一緒に変わらないって分かったら、関数を削除して、必要なロジックを書き直せばいいだけ。めっちゃ簡単だよ。

39
dezsiszabi
👍14約1か月前

変更しやすいのは、そうじゃなくなるまで。

40
Kind-Armadillo-2340
👍10約1か月前

コードが変更しにくいなら、DRYが問題じゃない。複雑にしすぎてるんだよ。DRYに従うことはコードを複雑にしないし、DRYに従わないとコードを複雑にしすぎる可能性がある。

コードをコピー&ペーストすることがシンプルさを保つと考えてる人もいるけど、それが解決策だとしても、それは貧弱な解決策だよ。ソフトウェアエンジニアは、複雑すぎないDRYなコードを書く方法を知っているべき。それが苦手だと感じるなら、もっと上達する必要があるってことだよ。

41
dezsiszabi
👍2約1か月前

そうあるべきなんだけど、そうじゃないことが多いんだよね。で、「単純な何か」を追加するためにプロジェクトに数年後に参加した時に、コピー&ペーストしてくれてた方がまだマシだった…って思うんだ、ぶっ壊れたのを直すのが楽になるから。

42
ZirePhiinix
約1か月前

抽象化が余計な依存関係を生む場合はそうじゃないね。

厳密なテストカバレッジを行う場所では、関数を削除するとエラーが発生するし、マジでヤバいことになる。プロジェクトで気軽に削除できるようなら、まだ十分に大きくないってこと。

43
Kind-Armadillo-2340
👍5約1か月前

CIスイートが不要な関数を削除したせいでエラーを吐くなら、問題は関数を削除したことじゃなくてCIスイートの方だろ。プロジェクトの規模に関わらず、不要な関数はいつでも削除できるべき。Linuxカーネルみたいな巨大プロジェクトだって常にコードを削除してるんだから。それができないなんて言い訳にならないよ。

44
JarredMack
👍7約1か月前

開発者はバグを入れなきゃいいだけじゃん?簡単じゃん。

45
diMario
👍1約1か月前

いっそのこと、コードを一切書かないことだね。これはもっと一般的な「何も行動しない者は決して間違いを犯さない」の特殊なケースだよ。

46
Luolong
👍8約1か月前

時期尚早なDRYの問題は、構造が既存のコードブロックと偶然似ている場合があるってこと。そこには統一されたコンセプトはなく、ただ類似性があるだけ。

反復の抽象的なパターンを中心に「コンセプト」を作り上げると、誰もが理解し、メンテナンスする必要のあるコンセプトの数が増える。

そして、ある使用箇所では反復ブロックの変更が必要だけど、別の箇所では必要ない場合、そのコンセプトに複雑さを加える誘惑に駆られる。そして、それが悪い抽象化がスパゲッティコードとしか言いようのないものに変わる瞬間なんだ。

47
gwillen
👍3約1か月前

たぶん、本当のルールは「良いセンスを持って、抽象化が良いアイデアか判断しろ」ってことだと思う。残念ながら… 😔

48
Duraz0rz
👍2約1か月前

「二度は偶然…三度はパターン」って覚え方をしてる。

49
editor_of_the_beast
👍13約1か月前

それはそうだけど、その割合ってどれくらいだと思う?俺は95%の場合、重複はアンチパターン_だ_と思ってる。

50
TheStatusPoe
👍12約1か月前

著者は「下手な抽象化や密結合は、重複よりもはるかに悪い」って言ってるけど、それはつまり、コードを重複させれば密結合は起こらないって言ってるように聞こえるんだよね。俺の経験では、一番密結合なコードベースって、重複が一番多いやつだったりする。依存関係をアップデートするのも一苦労で、1つか2つのファイルを更新するだけで済むはずが、何十個ものファイルを追跡して変更しなきゃいけないんだから。

51
HAK_HAK_HAK
👍2約1か月前

俺が一番好きな結合の種類:IDEでF12キーを押しても飛べないやつ。

52
Massless
👍4約1か月前

今のキャリア段階だと、結合度を下げるために、多少の重複は全然アリって判断になるな。

53
mark_99
約1か月前

重複は結合度を取り除くわけじゃなくて、隠すだけなんだ。修正や最適化で、暗黙的にリンクされているすべての場所でコードを変更する必要が出てくる場合がある。

重複がOKなのは、それが偶然の場合だけ。つまり、コードのインスタンスが論理的に分離されていて、たまたま似たような実装になった場合。

あまりにも教条的になる必要はないけど、短くて些細な重複が2つくらいなら大したことない。でも、見過ごさないようにね。共通のライブラリ/ユーティリティコードを追加する簡単な方法が常に必要だよ(階層的な依存関係はOK、双方向のクロスリンクはダメ)。

54
PurpleYoshiEgg
👍2約1か月前

それ、マジで結合の真逆じゃん。結合ってのは、ある場所で何かを変更すると、意図したかどうかに関わらず、全部に影響が出ることだよ。

55
mkluczka
👍2約1か月前

レイヤーがいくつかあって、実際にそれらが必要な場合、似たようなフィールドを持つDTO、イベント、コマンド、エンティティは実際にはコードの重複じゃないんだよね。

単純なケースでは、エンティティが全部になることもあるけど。

56
pakoito
👍1約1か月前

ネットワークモデルをドメイン層とプレゼンテーション層で再利用しようとすると、痛い目を見るよ。3つすべてを単一の抽象基底クラスに抽象化しようとすると、もっと痛い目を見る。できる限り強力な型でマッピングコードを書いて、モデルの進化に合わせて更新し続けること。それが今の君たちの契約だ。

57
renges
👍3約1か月前

クリーンコードって、コードの質にマジで悪影響しかない。今でもその影響を引きずってるわ。

58
RICHUNCLEPENNYBAGS
👍1約1か月前

開発に携わってれば携わるほど、みんなが同じような内容の記事を何度も何度も書けることに驚かされる。