フィーチャーフラグの削除まで考える

「脳に収まるコードの書き方 - 複雑さを避け持続可能にするための経験則とテクニック」を読んだ中で、フィーチャーフラグの方針いいなぁと感じたので、これをやってみたい。

本書だとC#での記載例になるが、ホームのRubyでの実装例を考えてみる。

参考

理解

本書に書いてある「フィーチャーフラグ」は、未完成の機能が本番システムにデプロイされてしまうことを良くないこととして、解決策を以下のように提示している。

解決策は、機能そのものと実装しているコードを区別することです。「不完全なコード」が実装しているふるまいが表に出なければ、本番環境にもデプロイできます。

とある。

本書の前提としている進め方のようなものについて、かなり原理主義的なものも感じるし、自分の考えるフィーチャーフラグとは少々違うところがある。

本書では、実装の途上で使うものとして定義するが私の理解だと、リリースの後もしばらく有効化しておき、運用の中で有効化。
最終的な安定を見て削除するものをという理解がをしている。

ただこの部分の理解は、本書の示すこととしてはおそらく一番の主張ではない。

一番の主張としては、次の部分だと考える。
フィーチャ――フラグの削除にあたって。

コンパイラに頼ってフラグが使われている場所を見つけます。

とあるところ。

Rubyであれば「コンパイラに頼る」ことは原則頼れないから、おそらくテストコードに頼ることとなる。
これを踏まえて、Rubyでのフィーチャーフラグの実装例を考えてみる。

実装

方針

想定するのは、デプロイ後に順次機能を有効化し、最終的にコード削除するケース。
Ruby on Rails での実装とする。

前提として、以下のコントローラーがあるとする。

app/controllers/hello_controller.rb
1
2
3
4
5
class HelloController < ApplicationController
def index
render plain: "hello world"
end
end

これをフィーチャーフラグを使って、表示を切り替えるとする。

パターンA メソッドレベル

フィーチャーフラグ導入

フィーチャーフラグモデル定義は以下の様にする。

db/schema.rb
1
2
3
4
5
6
7
8
ActiveRecord::Schema[7.2].define(version: 2026_07_19_000001) do
create_table "feature_flags", force: :cascade do |t|
t.string "key", default: "", null: false
t.boolean "is_available", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
app/models/feature_flag.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FeatureFlag < ApplicationRecord
validates :key, presence: true, uniqueness: true
validates :enabled, inclusion: { in: [true, false] }

scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }

enum :key, { good_bye_world: "good_bye_world" }

def self.good_bye_world_enabled?
enabled?(:good_bye_world)
end

private
def self.enabled?(key)
feature_flag = find_by(key: key)
feature_flag&.enabled? || false
end
end

これを踏まえ、コントローラーに実装を以下のようにする。
最終的に残る処理を本筋の処理として、分岐させることとする。

app/controllers/hello_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class HelloController < ApplicationController
def index

unless FeatureFlag.good_bye_world_enabled?
return render plain: "hello world"
else

render plain: "good bye world"
end
end

動作確認として以下のspecを用意する。

spec/requests/hello_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do
context "when good_bye_world feature flag is enabled" do
before do
allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(true)
end

it "returns 'good bye world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world")
end
end

context "when good_bye_world feature flag is disabled" do
before do
allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(false)
end

it "returns 'hello world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("hello world")
end
end

context "when good_bye_world feature flag is not set" do
it "returns 'hello world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("hello world")
end
end
end
end

実行して、テストが通ることを確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ bundle exec rspec

Hello
GET /
when good_bye_world feature flag is enabled
returns 'good bye world'
when good_bye_world feature flag is disabled
returns 'hello world'
when good_bye_world feature flag is not set
returns 'hello world'

Finished in 0.48919 seconds (files took 7.65 seconds to load)
3 examples, 0 failures

フィーチャーフラグ削除

本書に寄れは、削除はコンパイラに頼るべきであるから、使用しているコントローラーから消すのではなく、定義たるモデルから消すべきだと考える。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class FeatureFlag < ApplicationRecord
validates :key, presence: true, uniqueness: true
validates :enabled, inclusion: { in: [true, false] }

scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }

enum :key, { good_bye_world: "good_bye_world" }

# 消した、ここではコメントアウトにしておく
# def self.good_bye_world_enabled?
# enabled?(:good_bye_world)
# end

private
 def self.enabled?(key)
feature_flag = find_by(key: key)
feature_flag&.enabled? || false
end

end

ここで再度テストを実行する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
$  bundle exec rspec

Hello
GET /
when good_bye_world feature flag is enabled
returns 'good bye world' (FAILED - 1)
when good_bye_world feature flag is disabled
returns 'hello world' (FAILED - 2)
when good_bye_world feature flag is not set
returns 'hello world' (FAILED - 3)

Failures:

1) Hello GET / when good_bye_world feature flag is enabled returns 'good bye world'
Failure/Error: allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(true)
FeatureFlag(id: integer, is_available: boolean, created_at: datetime, updated_at: datetime, key: string) does not implement: good_bye_world_enabled?
# ./spec/requests/hello_spec.rb:7:in `block (4 levels) in <top (required)>'

2) Hello GET / when good_bye_world feature flag is disabled returns 'hello world'
Failure/Error: allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(false)
FeatureFlag(id: integer, is_available: boolean, created_at: datetime, updated_at: datetime, key: string) does not implement: good_bye_world_enabled?
# ./spec/requests/hello_spec.rb:19:in `block (4 levels) in <top (required)>'

3) Hello GET / when good_bye_world feature flag is not set returns 'hello world'
Failure/Error: unless(FeatureFlag.good_bye_world_enabled?)

NoMethodError:
undefined method `good_bye_world_enabled?' for class FeatureFlag
# ./app/controllers/hello_controller.rb:4:in `index'
# /usr/local/bundle/gems/turbo-rails-2.0.23/lib/turbo-rails.rb:24:in `with_request_id'
# /usr/local/bundle/gems/turbo-rails-2.0.23/app/controllers/concerns/turbo/request_id_tracking.rb:10:in `turbo_tracking_request_id'
# /usr/local/bundle/gems/actiontext-7.2.3.1/lib/action_text/rendering.rb:25:in `with_renderer'
# /usr/local/bundle/gems/actiontext-7.2.3.1/lib/action_text/engine.rb:71:in `block (4 levels) in <class:Engine>'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/tempfile_reaper.rb:20:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/etag.rb:29:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/conditional_get.rb:31:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/head.rb:15:in `call'
# /usr/local/bundle/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:274:in `context'
# /usr/local/bundle/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:268:in `call'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/rack/logger.rb:41:in `call_app'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/rack/logger.rb:29:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/method_override.rb:28:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/runtime.rb:24:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/sendfile.rb:131:in `call'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/engine.rb:535:in `call'
# /usr/local/bundle/gems/rack-test-2.2.0/lib/rack/test.rb:360:in `process_request'
# /usr/local/bundle/gems/rack-test-2.2.0/lib/rack/test.rb:153:in `request'
# ./spec/requests/hello_spec.rb:31:in `block (4 levels) in <top (required)>'

Finished in 0.37588 seconds (files took 8.03 seconds to load)
3 examples, 3 failures

Failed examples:

rspec ./spec/requests/hello_spec.rb:10 # Hello GET / when good_bye_world feature flag is enabled returns 'good bye world'
rspec ./spec/requests/hello_spec.rb:22 # Hello GET / when good_bye_world feature flag is disabled returns 'hello world'
rspec ./spec/requests/hello_spec.rb:30 # Hello GET / when good_bye_world feature flag is not set returns 'hello world'

スタックトレースを見て、実際利用しているコントローラーでメソッド定義が無いというエラーを確認できる。

対応として、削除したgood_bye_world_enabled?の呼び出しをするコントローラーを以下のように修正する。

app/controllers/hello_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class HelloController < ApplicationController
def index

# 削除、ここではコメントアウト
# unless FeatureFlag.good_bye_world_enabled?
# return render plain: "hello world"
# end

render plain: "good bye world"
end
end

同様にspecも削除したgood_bye_world_enabled?の呼び出しがあるテストを削除する。

spec/requests/hello_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do
# 削除 ここではコメントアウト
# context "when good_bye_world feature flag is enabled" do
# before do
# allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(true)
# end
#
# it "returns 'good bye world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("good bye world")
# end
# end
#
# context "when good_bye_world feature flag is disabled" do
# before do
# allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(false)
# end
#
# it "returns 'hello world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("hello world")
# end
# end

context "when good_bye_world feature flag is not set" do
it "returns 'hello world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("hello world")
end
end
end
end

ここで、再度実行すると、以下のようにテストは削減されていたうえで失敗する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ bundle exec rspec

Hello
GET /
when good_bye_world feature flag is not set
returns 'hello world' (FAILED - 1)

Failures:

1) Hello GET / when good_bye_world feature flag is not set returns 'hello world'
Failure/Error: expect(response.body).to eq("hello world")

expected: "hello world"
got: "good bye world"

(compared using ==)
# ./spec/requests/hello_spec.rb:34:in `block (4 levels) in <top (required)>'

Finished in 0.22922 seconds (files took 7.74 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/requests/hello_spec.rb:31 # Hello GET / when good_bye_world feature flag is not set returns 'hello world'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do
# 削除 ここではコメントアウト
# context "when good_bye_world feature flag is enabled" do
# before do
# allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(true)
# end
#
# it "returns 'good bye world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("good bye world")
# end
# end
#
# context "when good_bye_world feature flag is disabled" do
# before do
# allow(FeatureFlag).to receive(:good_bye_world_enabled?).and_return(false)
# end
#
# it "returns 'hello world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("hello world")
# end
# end

context "when good_bye_world feature flag is not set" do
it "returns 'good bye world'" do         # < -- 修正
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world") # < -- 修正
end
end
end
end

これでgood_bye_world_enabled?の定義を削除、すなわちフィーチャーフラグの削除が完了する。

最後のテストの修正は、やや気持ちが悪いとも考えられる。
以下のように最初から定義メソッド定義が無くなったらとしておけばテストの修正は不要だが、これは意見が分かれそうでもある。

spec/requests/hello_spec.rb(抜粋)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do

# 省略

context "when good_bye_world_enabled is not defined" do
it "returns 'good bye world'" do
get "/"

# FeatureFlag は、good_bye_world_enabled?メソッドを持っているか?
if !FeatureFlag.respond_to?(:good_bye_world_enabled?)
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world")
else
expect(response).to have_http_status(:ok)
expect(["hello world", "good bye world"]).to include(response.body)
end
end
end
end
end

と、このように書いたとして最終的に、無いメソッドのテストがずっとあるということなる。
将来的には、別のテストを定義して、この部分も捨てることを考えてしかるべものと理解している。

パターンB クラスレベル

パターンAではメソッドレベルで定義したが、クラスレベルにすると、エラーの出る様子が変わってくる。

これをPORO(Plain Old Ruby Object)での実装例を考えてみる。

フィーチャーフラグ導入

モデルには、特定キーを引き当てるメソッドを載せず、問い合わせるenabled?メソッドはpublicにしておく。

app/models/feature_flag.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class FeatureFlag < ApplicationRecord
validates :key, presence: true, uniqueness: true
validates :enabled, inclusion: { in: [true, false] }

scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }

enum :key, { good_bye_world: "good_bye_world" }

def self.enabled?(key)
feature_flag = find_by(key: key)
feature_flag&.enabled? || false
end
end

POROで1枚ラップしたクラスを定義は以下のようにする。

app/models/good_bye_world_feature_flag.rb
1
2
3
4
5
6
7
class GoodByeWorldFeatureFlag
KEY = FeatureFlag.keys[:good_bye_world]

def self.enabled?
FeatureFlag.enabled?(KEY)
end
end

コントローラーでは、以下のような利用とする。

app/controllers/hello_controller.rb
1
2
3
4
5
6
7
8
9
class HelloController < ApplicationController
def index
unless GoodByeWorldFeatureFlag.enabled?
return render plain: "hello world"
end

render plain: "good bye world"
end
end

テストコードは以下のようにする。

spec/requests/hello_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do
context "when good_bye_world feature flag is enabled" do
before do
allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(true)
end

it "returns 'good bye world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world")
end
end

context "when good_bye_world feature flag is disabled" do
before do
allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(false)
end

it "returns 'hello world'" do
get "/"
expect(response).to have_http_status(:ok)
expect(response.body).to eq("hello world")
end
end

context "when good_bye_world feature flag is not set" do
it "returns 'good bye world" do
get "/"

if defined?(GoodByeWorldFeatureFlag)
expect(response).to have_http_status(:ok)
expect(["hello world", "good bye world"]).to include(response.body)
else
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world")
end
end
end
end
end

こちらもテストは以下のように成功する。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ bundle exec rspec

Hello
GET /
when good_bye_world feature flag is enabled
returns 'good bye world'
when good_bye_world feature flag is disabled
returns 'hello world'
when good_bye_world feature flag is not set
returns 'hello world' or 'good bye world

Finished in 0.40968 seconds (files took 7.96 seconds to load)
3 examples, 0 failures

フィーーチャーフラグ削除

フィーチャーフラグとなっているクラス GoodByeWorldFeatureFlagクラス、./app/models/good_bye_world_feature_flag.rbを削除する。

そのうえで、再度テストを実行する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
$ bundle exec rspec

Hello
GET /
when good_bye_world feature flag is enabled
returns 'good bye world' (FAILED - 1)
when good_bye_world feature flag is disabled
returns 'hello world' (FAILED - 2)
when good_bye_world feature flag is not set
returns 'hello world' or 'good bye world (FAILED - 3)

Failures:

1) Hello GET / when good_bye_world feature flag is enabled returns 'good bye world'
Failure/Error: allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(true)

NameError:
uninitialized constant GoodByeWorldFeatureFlag
# ./spec/requests/hello_spec.rb:7:in `block (4 levels) in <top (required)>'

2) Hello GET / when good_bye_world feature flag is disabled returns 'hello world'
Failure/Error: allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(false)

NameError:
uninitialized constant GoodByeWorldFeatureFlag
# ./spec/requests/hello_spec.rb:19:in `block (4 levels) in <top (required)>'

3) Hello GET / when good_bye_world feature flag is not set returns 'hello world' or 'good bye world
Failure/Error: unless GoodByeWorldFeatureFlag.enabled?

NameError:
uninitialized constant HelloController::GoodByeWorldFeatureFlag
# ./app/controllers/hello_controller.rb:3:in `index'
# /usr/local/bundle/gems/turbo-rails-2.0.23/lib/turbo-rails.rb:24:in `with_request_id'
# /usr/local/bundle/gems/turbo-rails-2.0.23/app/controllers/concerns/turbo/request_id_tracking.rb:10:in `turbo_tracking_request_id'
# /usr/local/bundle/gems/actiontext-7.2.3.1/lib/action_text/rendering.rb:25:in `with_renderer'
# /usr/local/bundle/gems/actiontext-7.2.3.1/lib/action_text/engine.rb:71:in `block (4 levels) in <class:Engine>'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/tempfile_reaper.rb:20:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/etag.rb:29:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/conditional_get.rb:31:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/head.rb:15:in `call'
# /usr/local/bundle/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:274:in `context'
# /usr/local/bundle/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:268:in `call'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/rack/logger.rb:41:in `call_app'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/rack/logger.rb:29:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/method_override.rb:28:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/runtime.rb:24:in `call'
# /usr/local/bundle/gems/rack-3.2.6/lib/rack/sendfile.rb:131:in `call'
# /usr/local/bundle/gems/railties-7.2.3.1/lib/rails/engine.rb:535:in `call'
# /usr/local/bundle/gems/rack-test-2.2.0/lib/rack/test.rb:360:in `process_request'
# /usr/local/bundle/gems/rack-test-2.2.0/lib/rack/test.rb:153:in `request'
# ./spec/requests/hello_spec.rb:31:in `block (4 levels) in <top (required)>'

Finished in 0.21992 seconds (files took 7.83 seconds to load)
3 examples, 3 failures

Failed examples:

rspec ./spec/requests/hello_spec.rb:10 # Hello GET / when good_bye_world feature flag is enabled returns 'good bye world'
rspec ./spec/requests/hello_spec.rb:22 # Hello GET / when good_bye_world feature flag is disabled returns 'hello world'
rspec ./spec/requests/hello_spec.rb:30 # Hello GET / when good_bye_world feature flag is not set returns 'hello world' or 'good bye world

当たり前だが、GoodByeWorldFeatureFlagクラスが無いので、コントローラーでの呼び出しもテストコードでの呼び出しもエラーになる。
クラスレベルにしたことで、NameErrorとなり、メソッドレベルのようにテストの失敗として解釈されないようになる。

これを修正するには、コントローラーの呼び出しを削除する。

app/controllers/hello_controller.rb
1
2
3
4
5
6
7
8
9
10
class HelloController < ApplicationController
def index
# 削除、ここではコメントアウト
# unless GoodByeWorldFeatureFlag.enabled?
# return render plain: "hello world"
# end

render plain: "good bye world"
end
end

同様に、テストコードでは、呼び出しされているテストごと削除する。

spec/requests/hello_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
require "rails_helper"

RSpec.describe "Hello", type: :request do
describe "GET /" do
# 削除 ここではコメントアウト
# context "when good_bye_world feature flag is enabled" do
# before do
# allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(true)
# end
#
# it "returns 'good bye world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("good bye world")
# end
# end
#
# context "when good_bye_world feature flag is disabled" do
# before do
# allow(GoodByeWorldFeatureFlag).to receive(:enabled?).and_return(false)
# end
#
# it "returns 'hello world'" do
# get "/"
# expect(response).to have_http_status(:ok)
# expect(response.body).to eq("hello world")
# end
# end

context "when good_bye_world feature flag is not set" do
it "returns 'good bye world" do
get "/"

if defined?(GoodByeWorldFeatureFlag)
expect(response).to have_http_status(:ok)
expect(["hello world", "good bye world"]).to include(response.body)
else
expect(response).to have_http_status(:ok)
expect(response.body).to eq("good bye world")
end
end
end
end
end

以下のようにテストが通ることを確認する。

1
2
3
4
5
6
7
8
9
$ bundle exec rspec

Hello
GET /
when good_bye_world feature flag is not set
returns 'hello world' or 'good bye world

Finished in 0.22334 seconds (files took 8 seconds to load)
1 example, 0 failures

クラスレベルでのフィーチャーフラグの設置から削除まで実行した。


というわけで、『脳に収まるコードの書き方 - 複雑さを避け持続可能にするための経験則とテクニック』のフィーチャーフラグの方針をRuby on Railsで実装してみた。

次自分がやるなら、クラスレベルの実装で導入することを検討するように感じられた。

設置するとき、削除まで方針立っているかといえば、いささか怪しい点もあっただろうが、認識を改められたように感じた次第だった。

では。