Skip to content

Reframe NextRails.next? teaching: Gemfile primacy + three-tier pattern#19

Draft
JuanVqz wants to merge 11 commits into
mainfrom
feature/dual-boot-reframe
Draft

Reframe NextRails.next? teaching: Gemfile primacy + three-tier pattern#19
JuanVqz wants to merge 11 commits into
mainfrom
feature/dual-boot-reframe

Conversation

@JuanVqz

@JuanVqz JuanVqz commented Apr 23, 2026

Copy link
Copy Markdown
Member

Closes #18.

Follow-up to #17. That PR closed #2 with a minimum-scope example swap (replaced the fixture_path / serialize coder: deprecation-only examples with a genuine two-sided breaking change). During review of #17, a larger structural problem surfaced: the skill's SKILL.md Pattern section and references/code-patterns.md jumped straight into app-code NextRails.next? branching as the default teaching, which overstates how often the reader should reach for it. Rails's deprecate-then-remove policy means the replacement API almost always backports to the current version, so unconditional migration is the correct fix for most adjacent-minor boundaries. This PR implements the reframe explicitly.

Separately: next_rails v1.5.0 (released 2026-04-02) shipped parallel-CI-native DeprecationTracker support. The previous references/deprecation-tracking.md documented a 25-line manual Ruby merge script that is now obsolete. This PR modernizes that file to use the v1.5.0 pattern (node_index: + deprecations merge --delete-shards).

Summary of changes

Gemfile-primacy reframe

  • CLAUDE.md — added invariant Add CHANGELOG.md and align SKILL.md with rails-upgrade #8: "prefer a single backport/forwardport shim over scattered conditionals when the version gap spans an application layer or affects roughly 20+ call sites".
  • dual-boot/SKILL.md Pattern — first illustration is now the Gemfile if next? ; gem 'rails', '~> 7.1.0' ; else ; gem 'rails', '~> 7.0.0' ; end version pin (the canonical, always-required conditional). App-code branching is introduced as the exception. The ignored_columns example is retained as the Tier 2 illustration paired with a respond_to? WRONG counter-example.
  • dual-boot/references/code-patterns.md — rewritten around the three-tier decision framework with cleanup semantics per tier.

Three-tier decision framework

Tier Technique When
1 Unconditional migration Both Rails versions accept the new form (typical Rails deprecation case).
2 NextRails.next? conditional at the call site New API doesn't exist on current (or old API raises on next). Smaller gaps (up to ~20 call sites).
3 Backport / forwardport shim Same gap spans an application layer (all controllers, all mailers) or affects ~20+ call sites.

Cleanup covers all three tiers

dual-boot/workflows/cleanup-workflow.md — added a new Step 3 "Delete Backport / Forwardport Shim Files (Tier 3 cleanup)". Existing Step 2 covers per-call-site else-branch removal (Tier 2). Tier 1 needs nothing. Grep pattern provided for finding shim files; verification rubric distinguishes Tier-3 shim files (guard wraps a module/class redefinition) from Tier-2 call-site branches (guard wraps a single call or config).

Two-phase setup workflow

dual-boot/workflows/setup-workflow.md — restructured from a single-phase setup into explicit Phase 1 (preparatory, no Rails version hop yet) and Phase 2 (dual-boot, the Rails version hop):

Phase 1:

  1. Verify deprecation warnings not silenced (now a pointer to references/deprecation-tracking.md, removing duplication).
  2. Check for existing Gemfile.next.
  3. Add next_rails at Gemfile root.
  4. Run next_rails --init.
  5. Configure DeprecationTracker. Interactive branch: local/single-process gets the plain config; parallel CI gets the node_index: config and the deprecations merge --delete-shards fan-in step.
  6. Capture deprecation inventory (DEPRECATION_TRACKER=save).
  7. Fix current-side deprecations unconditionally (Tier 1 only, no NextRails.next? yet).

Phase 2:
8. Configure Gemfile with if next? version conditional.
9. Identify and pin incompatible gems using bundle_report compatibility --rails-version=<target> (a command provided by next_rails).
10. Install dependencies for both versions (lockfile-copy trick preserved).
11. Verify both sides boot and run tests.
12. Fix two-sided breakage using Tier 2 / Tier 3.
13. Commit.

next_rails v1.5.0 modernization

dual-boot/references/deprecation-tracking.md — replaced the 25-line manual Ruby merge script with the v1.5.0 parallel-CI-native pattern:

RSpec.configure do |config|
  if ENV["DEPRECATION_TRACKER"]
    DeprecationTracker.track_rspec(
      config,
      node_index: ENV["CI_NODE_INDEX"]
    )
  end
end
# Save phase (each parallel node)
DEPRECATION_TRACKER=save CI_NODE_INDEX=$NODE bundle exec rspec <subset>

# Merge phase (runs once after all nodes finish)
deprecations merge --delete-shards

# Compare phase (each parallel node)
DEPRECATION_TRACKER=compare CI_NODE_INDEX=$NODE bundle exec rspec <subset>

Platform-specific env var mapping included (CircleCI → CIRCLE_NODE_INDEX, Buildkite → BUILDKITE_PARALLEL_JOB, GitLab → CI_NODE_INDEX, Semaphore → SEMAPHORE_JOB_INDEX). Minimum version requirement (next_rails >= 1.5.0) documented in both setup-workflow.md and references/deprecation-tracking.md.

Also added a last-resort grep fallback at the bottom of deprecation-tracking.md for apps where DeprecationTracker cannot be configured (loses structure, compare mode, and regression gating — use the tracker when possible).

Workflow separation in the basic-setup walkthrough

dual-boot/examples/basic-setup.md — added a "Before You Start: Resolve Current-Version Deprecations" section that frames current-version deprecation resolution as a Tier 1 prerequisite, not a step inside dual-boot fixing. Step 6 rescoped to genuine two-sided breakage only. Explicit: do not fix next-version deprecations here — those belong to the preparatory phase of the next upgrade.

Other

  • CI setup is now explicitly documented as optional — local-only dual-boot is equally valid via manual two-suite invocation.
  • All pointers to deprecation-resolution workflow cite dual-boot's own references/deprecation-tracking.md, keeping this PR self-contained and forward-compatible whether or not rails-upgrade PR #31 lands.

Parent branch

Branched off feature/issue-2-better-examples (PR #17). Merge order: #17 first, then rebase this branch onto main if needed.

Out of scope (deferred or rejected)

  • Multi-hop upgrades — stays out. Follows the FastRuby.io methodology: one adjacent-minor at a time.
  • Deprecation-tracking as separate skill scope — flagged for later consideration; not a blocker.
  • README.md Key Principle expansion — stays at the ignored_columns conditional from Replace deprecation examples with genuine breaking changes #17.
  • v1.4.x fallback for next_rails — require v1.5.0+; older users should upgrade.

@JuanVqz JuanVqz self-assigned this Apr 23, 2026
@JuanVqz JuanVqz marked this pull request as draft April 23, 2026 01:32
@JuanVqz JuanVqz force-pushed the feature/issue-2-better-examples branch 4 times, most recently from b9bd51a to 26c8521 Compare April 24, 2026 21:41
@JuanVqz JuanVqz force-pushed the feature/dual-boot-reframe branch from 5cf6ec7 to 3a76fc8 Compare April 24, 2026 21:55
Base automatically changed from feature/issue-2-better-examples to main April 25, 2026 12:19

@etagwerker etagwerker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuanVqz I think we need to have a longer conversation about this skill.

When I envisioned this skill, this is what I had in mind:

===

Why Dual-Boot?

Dual-booting is a core part of the FastRuby.io upgrade methodology:

  • Quickly switch between dependency sets for debugging
  • Run test suites against both versions
  • Deploy backwards-compatible changes to production before the version bump
  • Catch compatibility issues early in CI
  • Allows you to gradually deploy versions of key dependencies (e.g. 10% of the traffic will now use the target version of Rails)

===

In this PR I see a whole section about this:

Before You Start: Resolve Current-Version Deprecations

I believe that goes beyond the scope of this skill.

My vision for this skill is to keep it as tight as possible to this:

  • Quickly switch between dependency sets for debugging
  • Run test suites against both versions
  • Catch compatibility issues early in CI
  • Allows you to gradually deploy versions of key dependencies (e.g. 10% of the traffic will now use the target version of Rails)

So maybe we need to drop this to avoid contributor and Claude's confusion:

  • Deploy backwards-compatible changes to production before the version bump

I'd be fine if we make this skill more about instructing Claude how to use next_rails in development, test, CI, and production, and not so much about fixing deprecation warnings (I think this should be in the rails upgrade skill)

Thoughts?

JuanVqz added 11 commits April 28, 2026 13:20
Codifies the Tier-3 preference from the upcoming three-tier framework in
`dual-boot/references/code-patterns.md`. When a version gap affects many
call sites (~20+) or spans an application layer (all controllers, all
mailers), a single backport/forwardport shim in one file is cheaper to
read, cheaper to maintain, and cheaper to remove at cleanup than
scattered `NextRails.next?` conditionals at each call site.

This invariant is the rule the rest of this PR enforces.
- Open with "Gemfile version pin is the canonical — and often only —
  `next?` conditional" to frame app-code branching as the exception,
  not the default.
- Add a three-tier decision table (unconditional / conditional / shim)
  with cleanup implications for each tier. Lower tier wins unless it
  actually breaks.
- Tier 2 keeps the `ignorable` → `ignored_columns=` example from PR #17.
- Tier 3 restores the `ActionController::Parameters#values` 7.0 → 7.1
  backport (verified against rails/rails#44816) as the canonical shim
  example, with backport/forwardport direction guidance and caveats.
- Keep Ruby, Gemfile-ternary, and general-pattern sections untouched.
- Drop the Sidekiq example permanently (see PR #17 rationale — was
  deprecation-only on Sidekiq 6.5+).

Enforces invariant #8 (prefer single shim over scattered conditionals)
added in the previous commit.
Flip the Pattern section so the first illustration is the Gemfile
version pin (the canonical, always-required `next?` conditional)
instead of an app-code branch. Introduce the three-tier approach
(Tier 1 unconditional migration preferred, Tier 2 call-site
conditional, Tier 3 backport/forwardport shim) by name and point
to `references/code-patterns.md` for the full decision framework.

Keep the `ignored_columns` example as the Tier 2 illustration paired
with the `respond_to?` WRONG counter-example. Rephrase "When to
Apply" so it's explicit that most dual-boots only use `next?` in the
Gemfile; app-code branching is reserved for genuine two-sided breakage.

Also sync Workflow 1 and Workflow 4 summaries to match the new step
numbering in `setup-workflow.md` (two-phase restructure) and
`cleanup-workflow.md` (new Tier-3 shim-removal step). Those files
change in following commits.
Existing Step 2 covers per-call-site `NextRails.next?` / `else`
branch removal (Tier 2 cleanup). Tier 3 cleanup — deleting entire
shim files like `config/initializers/ac_parameters_values_backport.rb`
— was not covered and is added as a new Step 3, renumbering the
remaining steps upward.

The new step:
- Contrasts per-call-site branches (Step 2) with whole-file shims.
- Provides a grep pattern to find shim files by their module-level
  guard (`^if\s*!?NextRails\.next\?\s*$`).
- Gives a verification rubric: guard-wraps-module-redef = shim;
  guard-wraps-call-or-config = Tier 2 (already handled).
- Instructs to delete the whole file, not just the guard — the file
  exists solely for the shim.

Codifies invariant #8 in the cleanup procedure.
Add a "Before You Start: Resolve Current-Version Deprecations" section
that frames current-version deprecation resolution as a Tier 1,
unconditional prerequisite, not a step inside the dual-boot walkthrough.
Warns against wrapping plain deprecations in `NextRails.next?` (dead
branching, invariant #7) and points to `references/deprecation-tracking.md`
for `DeprecationTracker` setup, with a note to configure it before the
first test run to avoid a double pass.

Rescope Step 6 with a new intro that scopes it to genuine two-sided
breakage (API gone, behavior change, gem-version gap) and routes readers
to `references/code-patterns.md` "Three-Tier Approach". Explicit:
do not fix next-version deprecations here — they belong to the
preparatory phase of the next upgrade.

Keep the 4.2 → 5.0 scenario and the `ignored_columns` example.
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.
Replace the ~25-line manual Ruby merge script (obsolete as of v1.5.0)
with the parallel-CI-native pattern:

- `node_index:` option on `track_rspec` / `track_minitest`. Can be
  passed explicitly (`node_index: ENV["CI_NODE_INDEX"]`) or
  auto-detected from standard CI env vars (`CIRCLE_NODE_INDEX`,
  `BUILDKITE_PARALLEL_JOB`, `SEMAPHORE_JOB_INDEX`, `CI_NODE_INDEX`).
- `deprecations merge --delete-shards` CLI command for the fan-in
  step that combines per-node shards into the canonical shitlist.
- `DeprecationTracker.merge_shards(path, delete_shards: true)`
  programmatic form for custom CI flows.
- `--next` flag for handling the next-Rails shitlist separately.

The three-phase CI workflow (save per node → merge once → compare per
node) is documented with runnable examples. Platform-specific env var
mapping covers CircleCI, Buildkite, GitLab CI, Semaphore, and generic
CI.

Also adds:
- Minimum `next_rails >= 1.5.0` callout at the top of Step 2, plus
  an updated Limitations note.
- "Last-resort fallback" section at the bottom with
  `bundle exec rspec 2>&1 | grep 'DEPRECATION WARNING' | sort -u`
  for apps where `DeprecationTracker` won't configure (loses
  structure, compare mode, and regression gating — use the tracker
  when possible).
- `deprecations merge --delete-shards` row in the Quick Reference.

Preserves: Why This Matters, silenced-deprecations detection
(Step 1), custom-deprecation-behavior approach for Minitest/parallel-fork
and older Rails (Step 3, all four Rails version variants retained),
Limitations, Maintenance, Quick Reference.

Implements plan §12.
Step 5 currently shows only `bundle exec rspec` for running tests on
both sides. Add a short note that Minitest apps substitute
`bin/rails test` (or `rake test`), and show the Minitest equivalent
block alongside the RSpec one. Point to `workflows/setup-workflow.md`
Step 11 for the full test-runner detection logic (RSpec, Minitest,
`bin/test` wrapper, `parallel_tests`, `turbo_tests`).
…oach'

Six pointers across SKILL.md, CLAUDE.md, basic-setup.md, and
setup-workflow.md cite the section as "Three-Tier Approach" (quoted
exact form). The heading had the longer "...for Application Code"
suffix. Rename the heading to match the pointers rather than update
all six callers. The Gemfile version-pin case is already under its own
section ("The Canonical Case: Gemfile Version Pin") so the
"for Application Code" qualifier was redundant.
Step 6 of the Workflow 1 summary showed `DEPRECATION_TRACKER=save
bundle exec rspec` only. Other surfaces already cover Minitest:
`setup-workflow.md` Step 6 has the substitution note;
`examples/basic-setup.md` Step 5 shows both RSpec and Minitest blocks
(commit 69e9bc0); `references/deprecation-tracking.md` documents
`track_minitest`. Mirror the `setup-workflow.md` Step 6 parenthetical
here so the SKILL.md summary doesn't imply RSpec is the only option.
…grep as heuristic

- Rename "canonical" → "foundational" for Gemfile pin to clarify it's
  the required conditional, not a pattern to follow everywhere.
- Add caveat to SKILL.md "When to Apply": Tier 1 (unconditional migration)
  assumes Rails's adjacent-minor deprecate-then-remove policy; non-adjacent
  hops or third-party gems may have more genuine two-sided breaks.
- Clarify cleanup-workflow.md Step 3: grep for shim files is a heuristic
  starting point; every match requires manual verification.
@JuanVqz JuanVqz force-pushed the feature/dual-boot-reframe branch from d733c18 to b083f04 Compare April 28, 2026 19:20
@JuanVqz

JuanVqz commented May 1, 2026

Copy link
Copy Markdown
Member Author

@JuanVqz I think we need to have a longer conversation about this skill.

When I envisioned this skill, this is what I had in mind:

===

Why Dual-Boot?

Dual-booting is a core part of the FastRuby.io upgrade methodology:

  • Quickly switch between dependency sets for debugging
  • Run test suites against both versions
  • Deploy backwards-compatible changes to production before the version bump
  • Catch compatibility issues early in CI
  • Allows you to gradually deploy versions of key dependencies (e.g. 10% of the traffic will now use the target version of Rails)

===

In this PR I see a whole section about this:

Before You Start: Resolve Current-Version Deprecations

I believe that goes beyond the scope of this skill.

My vision for this skill is to keep it as tight as possible to this:

  • Quickly switch between dependency sets for debugging
  • Run test suites against both versions
  • Catch compatibility issues early in CI
  • Allows you to gradually deploy versions of key dependencies (e.g. 10% of the traffic will now use the target version of Rails)

So maybe we need to drop this to avoid contributor and Claude's confusion:

  • Deploy backwards-compatible changes to production before the version bump

I'd be fine if we make this skill more about instructing Claude how to use next_rails in development, test, CI, and production, and not so much about fixing deprecation warnings (I think this should be in the rails upgrade skill)

Thoughts?

I'm open to talk about this. Ping me when you can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants