You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Restructure setup-workflow.md into preparatory + dual-boot phases
Split the workflow into two explicit phases:
**Phase 1 (preparatory)** — Steps 1-7:
- Verify deprecation warnings not silenced (now a pointer to
`references/deprecation-tracking.md`, removing the duplicated
detection commands).
- Check for existing Gemfile.next.
- Add `next_rails` gem at Gemfile root (warning preserved).
- Run `next_rails --init`.
- Configure `DeprecationTracker` — interactive branch asks the user
whether they run CI with parallel test execution. Local/single-process
gets the plain config; parallel-CI gets the `node_index:` config and
the `deprecations merge --delete-shards` fan-in step. Both RSpec and
Minitest examples covered.
- Capture the deprecation inventory (`DEPRECATION_TRACKER=save`).
- Fix current-side deprecations unconditionally (Tier 1, no
`NextRails.next?` conditionals yet).
**Phase 2 (dual-boot)** — Steps 8-13:
- Configure the Gemfile `if next?` version conditional (Rails, Ruby,
core-gem examples all retained; related-gem handling preserved).
- Identify and pin incompatible gems using
`bundle_report compatibility --rails-version=<target>` with a
fix loop: read bundler's conflict output, check RailsBump /
CHANGELOG, pin inside `if next?`, re-run
`BUNDLE_GEMFILE=Gemfile.next bundle install` until clean.
- Install dependencies for both versions (lockfile-copy trick preserved
verbatim).
- Verify both sides boot and run tests.
- Fix two-sided breakage using Tier 2 / Tier 3 from code-patterns.md.
- Commit dual-boot setup.
Also documents:
- Minimum `next_rails` version requirement (>= 1.5.0, for
parallel-CI-native `DeprecationTracker` and `deprecations merge`).
- Explicit "CI is optional" note — local-only dual-boot is equally
valid via manual two-suite invocation.
Implements plan §7, §8, §10, §11, §13, §14.
-A Ruby application with a working `Gemfile` and `Gemfile.lock`
6
-
-Know the current and target versions for the dependency you are upgrading (Rails, Ruby, or another core gem)
5
+
-**Phase 1 (Preparatory):** Install `next_rails`, set up `DeprecationTracker`, and resolve current-version deprecations unconditionally (Tier 1). No Rails version hop yet. Both `Gemfile` and `Gemfile.next` resolve to identical gems.
6
+
-**Phase 2 (Dual-Boot):** Pin a different Rails version in the `if next?` Gemfile block, install gems on the next side, and fix genuine two-sided breakage using Tier 2 or Tier 3 patterns.
7
7
8
-
---
8
+
Keeping deprecation resolution in Phase 1 means most deprecation fixes land as plain, unconditional migrations on the current Rails version, without any `NextRails.next?` branching. Phase 2 is then scoped to the real two-sided breakage that only dual-boot can address.
9
9
10
-
## Step 0: Verify Deprecation Warnings Are Not Silenced
10
+
## Prerequisites
11
11
12
-
Before setting up dual-boot, ensure deprecation warnings are visible. Silenced deprecations mean you can't track upgrade progress.
12
+
- A Ruby application with a working `Gemfile` and `Gemfile.lock`.
13
+
- Know the current and target versions for the dependency you are upgrading (Rails, Ruby, or another core gem).
14
+
-**Minimum `next_rails` version: 1.5.0** (released 2026-04-02). The parallel-CI-aware `DeprecationTracker` configuration (`node_index:` option) and the `deprecations merge --delete-shards` CLI require v1.5.0. If your Gemfile currently pins an older version, update it before proceeding.
1. Change `:silence` to `:stderr` or `:log` (or `:raise` if you want strict mode)
22
-
2. Remove `ActiveSupport::Deprecation.silenced = true` if found
23
-
3. Remove `config.active_support.report_deprecations = false` if found (Rails 7.0+)
24
-
4. Run the test suite to see current deprecation warnings
20
+
Phase 1 is everything you do **before** the Rails version hop. At the end of Phase 1 you have `next_rails` installed, `Gemfile.next` in place as a symlink (both sides resolve to the same gems), `DeprecationTracker` configured, and all current-side deprecations fixed unconditionally. No `NextRails.next?` conditionals appear in application code during this phase.
25
21
26
-
See `references/deprecation-tracking.md` for detailed configuration guidance, including a gradual approach using custom deprecation behaviors for apps with many existing warnings.
22
+
## Step 1: Verify Deprecation Warnings Are Not Silenced
27
23
28
-
---
24
+
Before setting up dual-boot, ensure deprecation warnings are visible and tracked. See `references/deprecation-tracking.md` for the full procedure (detection of silenced deprecations, how to unsilence, and setting up `DeprecationTracker` to capture a regression-safe inventory).
29
25
30
-
## Step 1: Check if Dual-Boot is Already Set Up
26
+
## Step 2: Check if Dual-Boot is Already Set Up
31
27
32
28
```bash
33
29
# Check if Gemfile.next already exists
34
30
ls -la Gemfile.next
35
31
```
36
32
37
-
- If `Gemfile.next` exists → Skip to Step 4
38
-
- If `Gemfile.next` does NOT exist → Continue to Step 2
33
+
- If `Gemfile.next` exists, skip Steps 3 and 4 (gem install and `next_rails --init`) and proceed to Step 5.
34
+
- If `Gemfile.next` does not exist, continue to Step 3.
39
35
40
-
**⚠️ CRITICAL:** Running `next_rails --init` when dual-boot is already set up will duplicate the `next?` method definition in the Gemfile, causing errors.
36
+
**CRITICAL:** Running `next_rails --init` when dual-boot is already set up will duplicate the `next?` method definition in the Gemfile, causing errors.
41
37
42
-
---
43
-
44
-
## Step 2: Add `next_rails` Gem
38
+
## Step 3: Add the `next_rails` Gem
45
39
46
40
Add the gem to the Gemfile **at the root level, not inside any group**:
47
41
@@ -50,37 +44,132 @@ Add the gem to the Gemfile **at the root level, not inside any group**:
50
44
gem 'next_rails'
51
45
```
52
46
53
-
**⚠️ If the gem is already present, verify its location.** Grep for it:
47
+
**If the gem is already present, verify its location.** Grep for it:
54
48
55
49
```bash
56
50
grep -n "next_rails" Gemfile
57
51
```
58
52
59
-
If it lives inside a `group :development`, `group :test`, or `group :development, :test do ... end` block, move it outside. Any `NextRails.next?` in `config/environments/production.rb` or `config/application.rb` will raise `NameError: uninitialized constant NextRails` when the app boots in production (e.g., during `assets:precompile RAILS_ENV=production`). The gem must be loadable in every environment where conditional config runs.
53
+
If it lives inside a `group :development`, `group :test`, or `group :development, :test do ... end` block, move it outside. Any `NextRails.next?` in `config/environments/production.rb` or `config/application.rb` will raise `NameError: uninitialized constant NextRails` when the app boots in production (for example, during `assets:precompile RAILS_ENV=production`). The gem must be loadable in every environment where conditional config runs.
60
54
61
55
Then install:
62
56
63
57
```bash
64
58
bundle install
65
59
```
66
60
67
-
---
68
-
69
-
## Step 3: Initialize Dual-Boot
61
+
## Step 4: Initialize Dual-Boot
70
62
71
63
```bash
72
64
next_rails --init
73
65
```
74
66
75
67
This creates:
76
-
-`Gemfile.next` — Symlink to your Gemfile
77
-
-`Gemfile.next.lock` — Lock file for the next dependency set
68
+
69
+
-`Gemfile.next`, a symlink to your `Gemfile`.
70
+
-`Gemfile.next.lock`, the lockfile for the next dependency set.
71
+
72
+
At this point both sides resolve to identical gems. The infrastructure is in place, but no version hop has happened yet.
73
+
74
+
## Step 5: Configure `DeprecationTracker`
75
+
76
+
`DeprecationTracker` (shipped with `next_rails`) captures every deprecation warning emitted during a test run and saves it to a JSON shitlist. Setting it up **before** the first test run means a single run captures the full inventory, avoiding a double pass.
77
+
78
+
Before choosing a configuration, ask the user:
79
+
80
+
> Do you run tests in CI with parallel execution (multiple nodes/containers running subsets of tests)?
81
+
82
+
Pick the matching configuration below.
83
+
84
+
### No / local only / single-process CI
85
+
86
+
**RSpec**, add to `spec/spec_helper.rb` (or `spec/rails_helper.rb`):
Set `node_index:` to the environment variable your CI provider uses for the per-shard index (`CI_NODE_INDEX`, `CIRCLE_NODE_INDEX`, `BUILDKITE_PARALLEL_JOB`, `SEMAPHORE_JOB_INDEX`, and so on).
With `node_index:` set, each shard writes `deprecation_warning.shitlist.node-<N>.json` instead of the canonical file. After all nodes finish, a fan-in step runs:
131
+
132
+
```bash
133
+
deprecations merge --delete-shards
134
+
```
135
+
136
+
This merges every `node-<N>.json` shard into the canonical shitlist and removes the shard files. See `references/deprecation-tracking.md` for the full CI pipeline (save, merge, compare phases).
137
+
138
+
## Step 6: Capture the Deprecation Inventory
139
+
140
+
Run the test suite on current Rails with `DEPRECATION_TRACKER=save` to generate the initial shitlist:
141
+
142
+
```bash
143
+
DEPRECATION_TRACKER=save bundle exec rspec
144
+
```
145
+
146
+
(Substitute your project's test command: `bin/rails test`, `bin/test`, `bundle exec parallel_rspec spec/`, and so on. The `DEPRECATION_TRACKER=save` environment variable is what matters.)
147
+
148
+
This creates `spec/support/deprecation_warning.shitlist.json`, a JSON file listing every unique deprecation warning found during the run. Review it to understand the scope of deprecations you need to address.
Rails uses a deprecate-then-remove policy: the replacement API ships one version before the old form is removed. That means the new call almost always works on both the current and next sides, and the correct fix is Tier 1, an unconditional migration. **Do not wrap deprecation fixes in `NextRails.next?`**; that produces a dead branch you will only have to clean up later.
153
+
154
+
Workflow:
155
+
156
+
1. Pick a category of deprecation from the shitlist (for example, `update_attributes` → `update`).
157
+
2. Replace every call site with the new API, on the current Rails version.
158
+
3. Re-run `DEPRECATION_TRACKER=save bundle exec rspec`. The shitlist should shrink.
159
+
4. Commit the fix.
160
+
5. Repeat until the shitlist is empty or down to deprecations that genuinely cannot be resolved on current Rails.
161
+
162
+
See `references/code-patterns.md` "Tier 1: Unconditional Migration" for the underlying rule and examples.
78
163
79
164
---
80
165
81
-
## Step 4: Configure Gemfile with Version Conditionals
166
+
# Phase 2: Dual-Boot
167
+
168
+
Phase 2 is the Rails version hop. You now pin a different Rails version on the next side, install gems on that side, and fix genuine two-sided breakage that cannot be resolved by Tier 1.
82
169
83
-
The `next_rails` gem adds a `next?` helper method to your Gemfile. Use it to specify different versions of the dependency you are upgrading.
170
+
## Step 8: Configure the Gemfile with the Version Conditional
171
+
172
+
`next_rails` adds a `next?` helper to your Gemfile. Use it to pin a different version of the dependency you are upgrading.
84
173
85
174
**Do not dual-boot `config.load_defaults`.** It is post-upgrade work (rails-upgrade Step 10 / rails-load-defaults skill).
86
175
@@ -132,7 +221,7 @@ else
132
221
end
133
222
```
134
223
135
-
### Handling Related Gem Version Differences
224
+
### Handling related gem version differences
136
225
137
226
If other gems also need different versions to stay compatible:
138
227
@@ -148,9 +237,37 @@ end
148
237
149
238
See `references/gemfile-examples.md` for more patterns.
150
239
151
-
---
240
+
## Step 9: Identify and Pin Incompatible Gems
241
+
242
+
`BUNDLE_GEMFILE=Gemfile.next bundle install` often fails because the target Rails version pulls in incompatible majors of other gems (Rack, sprockets, and so on). Surface these conflicts **before** attempting the next-side install.
243
+
244
+
1.**Run `bundle_report compatibility` up front.**`next_rails` ships a `bundle_report` CLI that flags which gems need version pinning for the target Rails:
Example: `bundle_report compatibility --rails-version=7.1`. The output lists gems whose current pins are incompatible with the target Rails, along with known-compatible versions.
251
+
252
+
2.**If conflicts still arise during `BUNDLE_GEMFILE=Gemfile.next bundle install`**, read bundler's conflict output to identify the offending gem.
152
253
153
-
## Step 5: Install Dependencies for Both Versions
254
+
3.**Look up a compatible version** via [RailsBump](https://railsbump.org/) or the gem's CHANGELOG.
255
+
256
+
4.**Pin the next-compatible version inside the `if next?` block:**
257
+
258
+
```ruby
259
+
if next?
260
+
gem 'rails', '~> 7.1.0'
261
+
gem 'some_gem', '~> 3.0'
262
+
else
263
+
gem 'rails', '~> 7.0.0'
264
+
gem 'some_gem', '~> 2.5'
265
+
end
266
+
```
267
+
268
+
5.**Re-run**`BUNDLE_GEMFILE=Gemfile.next bundle install` until it resolves cleanly.
269
+
270
+
## Step 10: Install Dependencies for Both Versions
If `BUNDLE_GEMFILE=Gemfile.next bundle install` fails with a resolution error, loop back to Step 9.
169
286
170
-
## Step 6: Verify Both Dependency Sets Work
287
+
## Step 11: Verify Both Dependency Sets Boot and Run Tests
171
288
172
289
Use the project's own test runner. Detect it first: `rspec-rails` in the Gemfile and a `spec/` directory → RSpec; `test/` and no `spec/` → Minitest; a `bin/test` wrapper → prefer it; `parallel_tests` / `turbo_tests` → use their binaries.
173
290
@@ -191,66 +308,32 @@ bin/rails test
191
308
BUNDLE_GEMFILE=Gemfile.next bin/rails test
192
309
```
193
310
194
-
If the project uses `rake test` instead of `bin/rails test`, or a parallel runner (`parallel_rspec`, `parallel_test`, `turbo_tests`), substitute that binary — the`BUNDLE_GEMFILE=Gemfile.next` prefix (or the `next` CLI) works with any of them.
311
+
If the project uses `rake test` instead of `bin/rails test`, or a parallel runner (`parallel_rspec`, `parallel_test`, `turbo_tests`), substitute that binary. The`BUNDLE_GEMFILE=Gemfile.next` prefix (or the `next` CLI) works with any of them.
## Step 7: Set Up Deprecation Tracking with DeprecationTracker
315
+
With current-side deprecations already resolved in Phase 1, anything still failing on the next side is genuine two-sided breakage: an API gone on the next side whose replacement does not backport to current, a behavior change between the two versions, or a gem-version gap where the gems pinned on each side expose different APIs.
199
316
200
-
The `next_rails` gem includes `DeprecationTracker`, which captures all deprecation warnings during test runs and saves them to a JSON file. Set it up now so you have a complete deprecation inventory from the start.
317
+
Use `references/code-patterns.md` "Three-Tier Approach" to decide how to handle each one:
201
318
202
-
### Configure RSpec
319
+
-**Tier 2**, a `NextRails.next?` conditional at the call site. Fits small gaps (up to about 20 call sites).
320
+
-**Tier 3**, a backport or forwardport shim in a single file. Fits gaps that span an application layer (all controllers, all mailers) or 20+ call sites. Cleanup is a single-file delete.
203
321
204
-
Add the following to `spec/spec_helper.rb` (or `spec/rails_helper.rb`):
322
+
Do not fix deprecation warnings emitted by the **next** version here. Those belong to the preparatory phase of the _following_ upgrade (current-version deprecations for that hop), not this one's dual-boot work.
This creates `spec/support/deprecation_warning.shitlist.json` — a JSON file listing every unique deprecation warning found during the run. Review it to understand the scope of deprecations you need to address.
224
-
225
-
### Minitest
226
-
227
-
For Minitest apps, use `DeprecationTracker.track_minitest` in `test/test_helper.rb`:
Then generate the inventory with `DEPRECATION_TRACKER=save bin/rails test`. If `track_minitest` doesn't fit your setup (e.g., `minitest/parallel_fork`), fall back to the custom deprecation behavior approach in `references/deprecation-tracking.md` (Step 3).
238
-
239
-
See `references/deprecation-tracking.md` for the full workflow (updating the shitlist, preventing regressions, CI with parallel execution, and alternative approaches).
240
-
241
-
---
242
-
243
-
## Step 8: Commit Dual-Boot Setup
324
+
## Step 13: Commit Dual-Boot Setup
244
325
245
326
```bash
246
327
git add Gemfile Gemfile.next Gemfile.next.lock
247
328
git commit -m "Add dual-boot setup for upgrade"
248
329
```
249
330
331
+
Commit Tier 2 and Tier 3 fixes separately as you make them, ideally one per category.
332
+
250
333
---
251
334
252
335
## Next Steps
253
336
254
-
-Configure CI to test both versions (see `references/ci-configuration.md`)
255
-
-Start fixing breaking changes using `NextRails.next?` branching
-**CI is optional.** Local-only dual-boot is fully valid. Running both suites manually (`bundle exec rspec` and `BUNDLE_GEMFILE=Gemfile.next bundle exec rspec`) is a complete workflow. `references/ci-configuration.md` is a pointer for readers who want to automate, not a mandate.
338
+
-Continue fixing breaking changes using the three-tier approach in `references/code-patterns.md`.
339
+
-When the upgrade is complete, remove dual-boot scaffolding via `workflows/cleanup-workflow.md`(Tier 2: drop `else` branches; Tier 3: delete shim files; Gemfile: collapse the `if next?` block).
0 commit comments