Slide 1

Slide 1 text

Feature Toggle は捨てやすく使おう PHPerKaigi 2026 / 2026-03-22 @gennei

Slide 2

Slide 2 text

自己紹介 アカウント:@gennei 所属: カオナビ 趣味 サンフレッチェ広島 積読 Podcast readline.fm コーヒー 2

Slide 3

Slide 3 text

Feature Toggle とは ソースコードをデプロイすることなく機能のON/OFF を切り替えら れる デプロイとリリースを分離する目的で使われることが多い 3

Slide 4

Slide 4 text

メリット トランクベース開発 git のコンフリクトが少なくなる ビッグバンマージなくなる リバートコミットも小さくなる リリースの切り戻しが素早くできる デプロイしないのですぐに反映 4

Slide 5

Slide 5 text

デメリット 削除するのを忘れがち 削除しようとする範囲がわかりにくいと事故が起きる 5

Slide 6

Slide 6 text

以下のスライドがおすすめです https://speakerdeck.com/kubotak/featuretogglezhan-lue-toyun-yong-fang-fa 6

Slide 7

Slide 7 text

カオナビでの導入事例 https://hatenanews.com/articles/2023/06/27/103000 7

Slide 8

Slide 8 text

よくある例 class HomeController { public function index(): Response { $user = auth()->user(); if (FeatureToggle::enabled('new_recommendation')) { // 新ロジック: パーソナライズしたおすすめのロジック $books = $this->recommendationService->getBooks($user); } else { // 旧ロジック:最近追加した本をおすすめとして出す $books = $this->latestBookService->getBooks($user); } return view('home.index', ['recommendedBooks' => $books]); } } 8

Slide 9

Slide 9 text

トグルを削除すると - if (FeatureToggle::enabled('new_recommendation')) { - // 新ロジック: パーソナライズしたおすすめのロジック - $books = $this->recommendationService->getBooks($user); - } else { - // 旧ロジック:最近追加した本をおすすめとして出す - $books = $this->latestBookService->getBooks($user); - } + $books = $this->recommendationService->getBooks($user); 9

Slide 10

Slide 10 text

修正後にやること コード修正して テストも直して/追加して レビューして デプロイする もう一度やり直すぐらい手間がかかってしまう 10

Slide 11

Slide 11 text

改善案1: private method に切り出して 早期 return する public function index(): Response { $books = $this->getBooks(auth()->user()); return view('home.index', ['recommendedBooks' => $books]); } private function getBooks($user): array { if (FeatureToggle::disabled('new_recommendation')) { return $this->latestBookService->findRecentlyAdded($user); } return $this->recommendationService->recommendByPersonalizedLogic($user); } 11

Slide 12

Slide 12 text

diff がとてもみやすい! - if (FeatureToggle::disabled('new_recommendation')) { - return $this->latestBookService->findRecentlyAdded($user); - } return $this->recommendationService->recommendByPersonalizedLogic($user); 12

Slide 13

Slide 13 text

改善案2 Interface を作り interface RecommendationEngine { public function recommendFor(User $user, int $limit = 20): array; } 13

Slide 14

Slide 14 text

実装したものを用意 旧 class RecentBooksRecommendationEngine implements RecommendationEngine { public function recommendFor(User $user, int $limit = 20): array { // とりあえず「最近追加された本」をおすすめとして扱う $books = []; return $books; } } 14

Slide 15

Slide 15 text

新 class PersonalizedRecommendationEngine implements RecommendationEngine { public function recommendFor(User $user, int $limit = 20): array { // 新ロジック本体を書く $books = []; return $books; } } 15

Slide 16

Slide 16 text

改善されたコード class HomeController { public function __construct( private readonly RecommendationEngine $recommendationEngine, ) {} public function index(): Response { $user = auth()->user(); $books = $this->recommendationEngine->recommendFor($user, 20); return view('home.index', ['recommendedBooks' => $books]); } } 16

Slide 17

Slide 17 text

結果 HomeController は変更しなくてよくなった 17

Slide 18

Slide 18 text

トグルはDI に使う class RecommendationServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(RecommendationEngine::class, function ($app) { if (FeatureToggle::disabled('new_recommendation')) { return $app->make(RecentBooksRecommendationEngine::class); } return $app->make(PersonalizedRecommendationEngine::class); }); } } 18

Slide 19

Slide 19 text

まとめ Feature Toggle は消すことから考えよう ロジックの分岐に使うのではなく振る舞いの分岐に使いましょう 作ったトグルはリリースが終わったら絶対に消そうね! 19

Slide 20

Slide 20 text

おまけ 捨てやすいコードに関心のある会社です 不要コード削除のススメ #phpcon_nagoya | ドクセル ソフトウェアは捨てやすく作ろう - Speaker Deck 開発者は消しやすいテストコードを書こう | Volare Viah テストも捨てやすく作ろう 20