Reframe NextRails.next? teaching: Gemfile primacy + three-tier pattern#19
Reframe NextRails.next? teaching: Gemfile primacy + three-tier pattern#19JuanVqz wants to merge 11 commits into
Conversation
b9bd51a to
26c8521
Compare
5cf6ec7 to
3a76fc8
Compare
etagwerker
left a comment
There was a problem hiding this comment.
@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?
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.
d733c18 to
b083f04
Compare
I'm open to talk about this. Ping me when you can. |
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'sSKILL.mdPattern section andreferences/code-patterns.mdjumped straight into app-codeNextRails.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_railsv1.5.0 (released 2026-04-02) shipped parallel-CI-nativeDeprecationTrackersupport. The previousreferences/deprecation-tracking.mddocumented 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.mdPattern — first illustration is now the Gemfileif next? ; gem 'rails', '~> 7.1.0' ; else ; gem 'rails', '~> 7.0.0' ; endversion pin (the canonical, always-required conditional). App-code branching is introduced as the exception. Theignored_columnsexample is retained as the Tier 2 illustration paired with arespond_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
NextRails.next?conditional at the call siteignorablegem'signore_columns→ nativeignored_columns=at Rails 4.2 → 5.0 (from Replace deprecation examples with genuine breaking changes #17).ActionController::Parameters#valuesbackport at Rails 7.0 → 7.1, verified against Don't delegate ActionController::Parameters#values to hash rails/rails#44816.params.valueschanged fromArray<Hash>toArray<ActionController::Parameters>in 7.1; a single initializer-level shim makes the 7.0 side return the 7.1 shape so all call sites stay identical across both versions.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-siteelse-branch removal (Tier 2). Tier 1 needs nothing. Grep pattern provided for finding shim files; verification rubric distinguishes Tier-3 shim files (guard wraps amodule/classredefinition) 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:
references/deprecation-tracking.md, removing duplication).Gemfile.next.next_railsat Gemfile root.next_rails --init.DeprecationTracker. Interactive branch: local/single-process gets the plain config; parallel CI gets thenode_index:config and thedeprecations merge --delete-shardsfan-in step.DEPRECATION_TRACKER=save).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 bynext_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: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 bothsetup-workflow.mdandreferences/deprecation-tracking.md.Also added a last-resort grep fallback at the bottom of
deprecation-tracking.mdfor apps whereDeprecationTrackercannot 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
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 ontomainif needed.Out of scope (deferred or rejected)
ignored_columnsconditional from Replace deprecation examples with genuine breaking changes #17.next_rails— require v1.5.0+; older users should upgrade.