Problem
The InteractiveRunner currently passes config as a parameter through multiple method calls, creating unnecessary coupling and inconsistent patterns compared to other structs in the codebase.
Current issues:
- Config parameter passed through 9+ method calls in InteractiveRunner
- Inconsistent with other structs (
Runner, Migrator, PreflightChecker) which store config as fields
- Makes method signatures verbose and error-prone
- Harder to maintain and extend
Current pattern:
runner := migration.NewInteractiveRunner(*nonInteractive)
if err := runner.Run(cfg); err != nil { // Config passed as parameter
Proposed Solution
Refactor InteractiveRunner to store config as a struct field, consistent with existing architecture patterns.
Desired pattern:
runner := migration.NewInteractiveRunner(*nonInteractive, cfg)
if err := runner.Run(); err != nil { // No config parameter needed
Implementation Details
Files to modify:
1. internal/migration/interactive.go:
- Add
config *config.Config field to InteractiveRunner struct
- Update constructor:
NewInteractiveRunner(nonInteractive bool, cfg *config.Config) *InteractiveRunner
- Remove
cfg *config.Config parameter from all methods:
Run(cfg *config.Config) → Run()
setProgressFile(cfg *config.Config, ...) → setProgressFile(...)
handlePreMigrationSteps(cfg *config.Config, ...) → handlePreMigrationSteps(...)
runMigration(cfg *config.Config, ...) → runMigration(...)
handlePostMigrationSteps(cfg *config.Config, ...) → handlePostMigrationSteps(...)
handleMigrationError(cfg *config.Config, ...) → handleMigrationError(...)
getLastProcessedID(cfg *config.Config, ...) → getLastProcessedID(...)
selectNewCategories(cfg *config.Config, ...) → selectNewCategories(...)
runDryRun(cfg *config.Config, ...) → runDryRun(...)
- Replace all
cfg references with r.config in method bodies
2. cmd/xenforo-to-gh-discussions/main.go:
// Before
runner := migration.NewInteractiveRunner(*nonInteractive)
if err := runner.Run(cfg); err != nil {
// After
runner := migration.NewInteractiveRunner(*nonInteractive, cfg)
if err := runner.Run(); err != nil {
3. Update any existing tests that use InteractiveRunner
Benefits
✅ Consistency: Aligns with existing patterns used by Runner, Migrator, PreflightChecker
✅ Cleaner interfaces: Methods focus on their logic, not parameter passing
✅ Maintainability: New methods automatically have config access
✅ Less error-prone: No risk of forgetting to pass config or passing wrong instance
✅ Single source of truth: Config stored once, accessible everywhere
Scope
- Small, focused refactoring affecting only
InteractiveRunner
- No breaking changes to public APIs outside the migration package
- No changes needed to other structs (they already follow desired pattern)
Acceptance Criteria
Problem
The
InteractiveRunnercurrently passes config as a parameter through multiple method calls, creating unnecessary coupling and inconsistent patterns compared to other structs in the codebase.Current issues:
Runner,Migrator,PreflightChecker) which store config as fieldsCurrent pattern:
Proposed Solution
Refactor
InteractiveRunnerto store config as a struct field, consistent with existing architecture patterns.Desired pattern:
Implementation Details
Files to modify:
1.
internal/migration/interactive.go:config *config.Configfield toInteractiveRunnerstructNewInteractiveRunner(nonInteractive bool, cfg *config.Config) *InteractiveRunnercfg *config.Configparameter from all methods:Run(cfg *config.Config)→Run()setProgressFile(cfg *config.Config, ...)→setProgressFile(...)handlePreMigrationSteps(cfg *config.Config, ...)→handlePreMigrationSteps(...)runMigration(cfg *config.Config, ...)→runMigration(...)handlePostMigrationSteps(cfg *config.Config, ...)→handlePostMigrationSteps(...)handleMigrationError(cfg *config.Config, ...)→handleMigrationError(...)getLastProcessedID(cfg *config.Config, ...)→getLastProcessedID(...)selectNewCategories(cfg *config.Config, ...)→selectNewCategories(...)runDryRun(cfg *config.Config, ...)→runDryRun(...)cfgreferences withr.configin method bodies2.
cmd/xenforo-to-gh-discussions/main.go:3. Update any existing tests that use
InteractiveRunnerBenefits
✅ Consistency: Aligns with existing patterns used by
Runner,Migrator,PreflightChecker✅ Cleaner interfaces: Methods focus on their logic, not parameter passing
✅ Maintainability: New methods automatically have config access
✅ Less error-prone: No risk of forgetting to pass config or passing wrong instance
✅ Single source of truth: Config stored once, accessible everywhere
Scope
InteractiveRunnerAcceptance Criteria
InteractiveRunnerstruct fieldr.configinstead of parameter