Slide 1

Slide 1 text

仕様変更に耐えるための "今の"DRY原則を考える まきまき 2025/02/22 PHPカンファレンス名古屋 1

Slide 2

Slide 2 text

まきまき          @_mkmk884 愛媛→京都→小田原 PHPer 名古屋目的でちゃんと来るのは初めて 学生のころに音楽フェス(TREASURE05X) で蒲郡にはよく行っていた 2

Slide 3

Slide 3 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 3

Slide 4

Slide 4 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 4

Slide 5

Slide 5 text

今回のおはなし 5 二つの似た処理を一つのメソッドに統合した ソースコード。 そのメソッドは、フラグで細かい違いを 切り替えていました。 片方の処理に変更を加えようとしたときに、 もう片方に影響が出たというお話です。

Slide 6

Slide 6 text

占い配信サービス 6 入稿データ 配信 サービスA データ 配信 サービスB データ … 2/24〜3/2の おとめ座 ★★★★★ 変 換

Slide 7

Slide 7 text

自社データをもとに外部サービス用のデータを作る 7 入稿データ ユニークコード ‘submission0222’ … 配信 サービスA データ 配信 サービスB データ サービスA内 ユニークコード ‘phone0222’ サービスB内 ユニークコード ‘tv0222’

Slide 8

Slide 8 text

[やりたいこと] 配信サービスデータを削除する 8 入稿データ ユニークコード ‘submission0222’ … 配信 サービスA データ 配信 サービスB データ サービスA内 ユニークコード ‘phone0222’ サービスB内 ユニークコード ‘tv0222’

Slide 9

Slide 9 text

9 配信サービスデータを削除できる画面は2つ 配信サービスデータ一覧画面  ぺちなご占い   2/24〜3/2 ♈おひつじ座 配信サービスAデータ一覧   2/24〜3/2 ♉おうし座 ☑ ☑   2/24〜3/2 ♊ふたご座 □ 削除   2/24〜3/2 ♉おうし座 □

Slide 10

Slide 10 text

10 入稿データ一覧画面  ぺちなご占い   2/24〜3/2 おひつじ座 入稿データ一覧   2/24〜3/2 おうし座 ☑ ☑   2/24〜3/2 ふたご座 □ 配信データ削除   2/24〜3/2 おうし座 □ 削除対象の配信サービス ✕   サービスA   サービスB ☑ □ 削除 外部サービスを選択する ことで入稿データに紐づく 配信データが削除できる 配信サービスデータを削除できる画面は2つ

Slide 11

Slide 11 text

削除メソッド 11 delete() 前処理 削除リスト(配列) を作る 削除処理

Slide 12

Slide 12 text

削除メソッド 12 delete() 前処理 削除リスト(配列) を作る 削除処理 $is_from_submission フラグで、 どちらの画面から 呼ばれているかを判断 if ($is_from_submission)

Slide 13

Slide 13 text

削除メソッド 13 delete() 削除リスト(配列) を作る 削除処理 ▼ 配信サービスデータ一覧画面 ▼ 入稿データ一覧画面 前処理 if ($is_from_submission) if ($is_from_submission) { } else { }

Slide 14

Slide 14 text

削除メソッド 14 delete() 削除リスト(配列) を作る 削除処理 ▼ 配信サービスデータ一覧画面 ユニークコード レコードID 前処理 if ($is_from_submission)

Slide 15

Slide 15 text

ユニークコードの取得方法 15 ユニークコードとする カラム名が プロパティで定義 Factoryパターンを用いたガチガチ継承クラス → 対応サービスの拡充を楽にするため ▼ 入稿データ ▼ 配信サービスAデータ

Slide 16

Slide 16 text

16 ここで 問 題 発 生

Slide 17

Slide 17 text

17 \ ナンダッテ---!!! / 外部配信サービスA 仕様変更

Slide 18

Slide 18 text

Aサービスが2つのコードでユニークになった 18 入稿データ ユニークコード ‘submission0222’ … 配信 サービスA データ 配信 サービスB データ Aサービス内 ユニークコード ‘phone’ ‘0222’ Bサービス内 ユニークコード ‘tv0222’ 2つ掛け合わせて ユニーク

Slide 19

Slide 19 text

19 配信サービスデータ一覧画面  ぺちなご占い   2/24〜3/2 ♈おひつじ座 配信サービスAデータ一覧   2/24〜3/2 ♉おうし座 ☑ ☑   2/24〜3/2 ♊ふたご座 □ 削除   2/24〜3/2 ♉おうし座 □ 今回修正したい画面の処理 配信サービスデータ一覧 画面からの削除処理の 修正が必要になった

Slide 20

Slide 20 text

何も修正しなければ起こる問題 20 配信サービスデータ一覧画面からの処理で出来上がる配列 キーが重複し、後続のレコードIDのみしか削除できない ※ イメージ

Slide 21

Slide 21 text

レコードIDをキーにするか…! 21 削除するレコードIDの保持はできる 配信サービスデータ一覧画面からの処理で出来上がる配列

Slide 22

Slide 22 text

レコードIDをキーにするか…! 22 入稿データ一覧画面からの処理で出来上がる配列 同じ形にできない!!! 入稿データに紐づくサービスの レコードがない可能性がある

Slide 23

Slide 23 text

片方の処理の変更がもう片方の処理に影響する 23 家の都合で 実家に帰ることに したんだよね 入稿データ一覧 画面からの処理 配信サービスデータ 一覧画面からの処理 え? 俺普通に今の家に 帰りたい

Slide 24

Slide 24 text

サクッと終わらないことに絶望 24 ユニークキーが2つになる仕様変更 ● ガチガチの継承クラスを用いている ● 2画面から呼ばれるメソッド 影響範囲〜〜〜! 課題 現状 制約〜〜〜!

Slide 25

Slide 25 text

サクッと終わらないことに絶望 25 ユニークキーが2つになる仕様変更 ● ガチガチの継承クラスを用いている ● 2画面から呼ばれるメソッド 影響範囲〜〜〜! 課題 現状 制約〜〜〜!

Slide 26

Slide 26 text

26 これって世にいう DRYなん…? Don't repeat yourself

Slide 27

Slide 27 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 27

Slide 28

Slide 28 text

DRY(Don't repeat yourself)原則 28 ● 同じ意味や機能を持つ情報を複数の場所に 重複して置くことをなるべく避けるべきとする考え方 ● ソフトウェアの構成や構築手法についての原則の一つ 信頼性の高いソフトウェアを開発して、開発そのものを簡単に理解したり メンテナンスできるようにする唯一の方法は、DRY原則に従うことです。   新装版 達人プログラマー 職人から名匠への道

Slide 29

Slide 29 text

29 delete() 前処理 削除リスト(配列) を作る 削除処理 (たぶん)当時はDRYだと思っていた

Slide 30

Slide 30 text

30 前処理 削除リスト(配列) を作る 削除処理 (たぶん)当時はDRYだと思っていた ▼ 配信サービスデータ一覧画面 ▼ 入稿データ一覧画面 削除リスト(配列) を作る 削除処理

Slide 31

Slide 31 text

31 前処理 削除リスト(配列) を作る 削除処理 (たぶん)当時はDRYだと思っていた ▼ 配信サービスデータ一覧画面 ▼ 入稿データ一覧画面 削除リスト(配列) を作る 削除処理

Slide 32

Slide 32 text

32 前処理 削除リスト(配列) を作る 削除処理 (たぶん)当時はDRYだと思っていた ▼ 配信サービスデータ一覧画面 ▼ 入稿データ一覧画面 削除リスト(配列) を作る 削除処理 同じようなコードだし 共通化するとよさそう 当時の開発者

Slide 33

Slide 33 text

[当時] これからも変わらない部分と変わる部分 33 変わらない部分 ● サービスデータを消すこと 自分たちのサービスの2種類の画面で共通なこと ● 各サービスのデータの構成方法

Slide 34

Slide 34 text

[当時] これからも変わらない部分と変わる部分 34 変わる部分 ● サービス間の差異 新規サービス連携開始時に拡充すること ● 例:外部サービスAと外部サービスBの値

Slide 35

Slide 35 text

DRY(Don't repeat yourself)原則 35 すべての知識はシステム内において、単一、かつ明確な、そして信頼できる 表現になっていなければならない。  新装版 達人プログラマー 職人から名匠への道 DRY原則を破るということは、同じ知識を2箇所以上に記述することです。  新装版 達人プログラマー 職人から名匠への道

Slide 36

Slide 36 text

DRY(Don't repeat yourself)原則 36 すべての知識はシステム内において、単一、かつ明確な、そして信頼できる 表現になっていなければならない。  新装版 達人プログラマー 職人から名匠への道 DRY原則を破るということは、同じ知識を2箇所以上に記述することです。  新装版 達人プログラマー 職人から名匠への道

Slide 37

Slide 37 text

37 ❌ コードの重複を許さない DRY(Don't repeat yourself)原則 ⭕ 知識の重複を許さない

Slide 38

Slide 38 text

知識…? 38 ● データ構造やフォーマット ● ビジネスルール ● 設定値 などなど… 将来にわたって同じ動作が期待されるもの

Slide 39

Slide 39 text

今回の機能の「変わらない部分」とは 「自分たちのサービス内で決められること」 なのでは…!?

Slide 40

Slide 40 text

[今] これからも変わらない部分と変わる部分 40 変わらない部分 ● サービスデータを消すこと この機能の役割

Slide 41

Slide 41 text

[今] これからも変わらない部分と変わる部分 41 変わる部分 ● サービス間の差異 ● 同一サービスの仕様変更 自社でコントロールできない部分

Slide 42

Slide 42 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 42

Slide 43

Slide 43 text

リファクタリングから始めた 43 仕様変更への追従の前に 削除できる2画面同士の依存を剥がすことにした 別居 ※ 想いは同じ

Slide 44

Slide 44 text

[手順①] メソッドを2つに分ける 44 1. 複製してメソッド名だけ変える 2. テストコードを複製 3. 呼び出し箇所を $is_from_submission フラグで分岐

Slide 45

Slide 45 text

[手順②] 特化することで不要になった処理を消す 45 $is_from_submission フラグを消す

Slide 46

Slide 46 text

[手順③] 仕様変更への追従を行う 46 レコードIDがキーになる配列ができるように修正

Slide 47

Slide 47 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 47

Slide 48

Slide 48 text

変わらない部分と変わる部分を明確にする 48 改修・機能追加を行うときは これからも変わらない部分と変わる部分を明確にしておく 変わらない部分 変わる部分 自社でコントロール できない部分 この機能の役割

Slide 49

Slide 49 text

今回、私は対象の機能の「変わらない部分」とは 「自分たちのサービス内で決められること」とした “今の”変わらない部分と変わる部分を明確にする 49 しかし、方針や需要によって、 自分たちのサービス内でも仕様変更はある

Slide 50

Slide 50 text

50 ユニークコードとする カラム名が プロパティで定義 Factoryパターンを用いたガチガチ継承クラス → 対応サービスの拡充を楽にするため ▼ 入稿データ ▼ 配信サービスAデータ 実際に何年かは変更に強かった “今の”変わらない部分と変わる部分を明確にする

Slide 51

Slide 51 text

“今の”変わらない部分と変わる部分を明確にする 51 将来どういう変化が起こるかは可能性でしかない できるだけ変更に耐えられるように ● “今”のものを”今”考えておく ● 考えたものを意図を含めてコードに落とし込んでおく → 今後の変更に比較的耐えられそう

Slide 52

Slide 52 text

変更が容易に加わりそうなところほど、 あえて共通化をしない できるだけ決定を遅らせる 52 優れたシステムアーキテクチャは、このような決定を従属的かつ遅延可能なも のにする。優れたシステムアーキテクチャは、このような決定に依存しない。 優れたシステムアーキテクチャは、重大な影響を与えることなく、このような 決定を最終時点まで引き延ばせる。  Clean Architecture 達人に学ぶソフトウェアの構造と設計

Slide 53

Slide 53 text

● 外部サービスとの連携箇所 ● 開発を繰り返しながら機能を拡充している途中のもの → ある程度固まってからまとめる 例 できるだけ決定を遅らせる 53

Slide 54

Slide 54 text

ユーザーフローが異なる場合はメソッドも分ける 54 入稿データ 配信 サービスA データ 配信 サービスB データ … 今回 配信サービス データ 一覧画面 入稿データ 一覧画面

Slide 55

Slide 55 text

ユーザーフローが異なる場合はメソッドも分ける 55 改修前の実装はたまたま同じであっただけ 入稿データ一覧画面 画 面 挙 動 配信サービスデータ 一覧画面 ユーザーが選択した情報を もとに紐づく情報を検索 して削除 ユーザーが選択した情報を 削除 概念は異なる

Slide 56

Slide 56 text

ユーザーフローが異なる場合はメソッドも分ける 56 異なるユーザーフローの処理が同じメソッドを通っている → SOLID原則の中の 単一責任原則 に違反している つまりソフトウェアにおける責任とは、「ある関心事について、不正な動作に ならないよう、正常に動作するよう制御する責任」と考えることができます。 ここで重要な役割を果たすのが、単一責任の原則です。「クラスが担う責任 は、たったひとつに限定すべき」とする設計原則です。  良いコード悪いコードで学ぶ設計入門

Slide 57

Slide 57 text

ユーザーフローが異なる場合はメソッドも分ける 57 変更する場合だけでなく、読むときにも読みやすい → 他のことを気にしなくてよくなる こないださ〜、 〇〇のときに△△が 起こってさ〜 ちょ、聞いてや 昨日✕✕さんと☆☆ に行ったんやけどね ひ…一人ずつ頼む… ねぇ、聞いたー?

Slide 58

Slide 58 text

今回気づいた変更に耐えないコードと耐えるコード 58 当時 似たような処理が まとまっていること そのメソッド・クラスが、 自身のやるべきことだけ を担う 耐 え な い 耐 え る 似たような処理を 重複して書く 同時に気にしなくていい 処理がまとまっていること 今

Slide 59

Slide 59 text

目次 1. 私が直面した問題について 2. ”今”考える共通にするべきもの 3. どう変えて仕様変更へ追従したか 4. 直面して実感したこと、学び 5. まとめ 59

Slide 60

Slide 60 text

まとめ 60 ● DRY原則とは、コードの重複を避けるのではなく、 知識を複数の場所に重複して置くことをなるべく避ける べきとする考え方 ● できるだけ変更に耐えられるよう、変わらない部分と 変わる部分を“今の”段階で、明確にする ● 責務が異なる場合は、似たような処理であっても メソッドやクラスを分ける