Go Refactoring
Refactoring changes structure while preserving observable behavior. If a requested change also alters behavior, separate the structural step from the behavioral step so each can be reviewed and verified on its own.
Understand the Change
Before editing:
- State the purpose: which upcoming change, duplication, coupling, or maintenance problem the refactor should address.
- Trace declarations, references, callers, interface satisfaction, package imports, tests, generated files, reflection, templates, tags, and public consumers in the affected area.
- Establish the behavior that must remain stable, including errors, side effects, ordering, concurrency, wire formats, performance-sensitive paths, and exported APIs.
- Inspect nearby architecture and prior migrations. Prefer an existing repository pattern over a new abstraction.
- Choose a safety net proportional to the blast radius; read references/safety-net.md.
For a small local change, this can be brief. For a multi-package or public-API refactor, produce an ordered inventory of independently verifiable steps before implementation. Do not create branches, pull requests, or tracking files unless the user requests that workflow.
Separate Change Types
Keep these concerns distinct when practical:
- mechanical rename or move;
- structural extraction or boundary change;
- behavior change or bug fix;
- optimization;
- broad formatting or generated-output churn.
This separation is about evidence and reviewability, not a mandatory commit count. A small coherent user-requested change may still be one patch when the distinctions remain clear.
Choose the Smallest Useful Transform
Common options are summarized in references/catalog.md. Prefer:
- a local rename before a package-wide vocabulary rewrite;
- extracting a meaningful unit before introducing a framework or pattern;
- a narrow consumer interface before moving shared concrete types;
- a same-package file split before creating a new package;
- an additive compatibility path before a breaking exported-API change;
- deleting obsolete indirection once callers no longer need it.
Do not refactor stable code solely to match a generic style preference. Avoid speculative interfaces, helper packages, layers, and generic abstractions.
Use Language-Aware Tools Carefully
Use project-available language-aware tooling for references, rename, imports, and code actions. For repeated rewrites, choose a syntax- or type-aware transformation that is reviewable and repeatable. See references/go-tooling.md.
Tool output still requires review. It may not account for:
- reflection, templates, string-based lookup, tags, and external configuration;
- generated code and its source definitions;
- consumers outside the workspace;
- behavior encoded only in integration tests or operational assumptions.
Never force a refused semantic rename with blind text replacement without first understanding the conflict.
Structural Boundaries
Read references/structural.md before moving types or functions across packages, splitting packages, breaking import cycles, or evolving an exported module API.
These changes may require a user decision because they alter ownership or compatibility. Surface that decision before choosing a target package or breaking path; do not assume permission to publish, deprecate, version, or remove an external API.
Apply and Verify Incrementally
For each step:
- Make one coherent structural change.
- Format and inspect the diff.
- Run the narrowest fast checks while iterating.
- Run the repository's complete relevant test and static-analysis suite before declaring success.
- Add race, integration, cross-platform, generation, or benchmark checks when the preserved behavior requires them.
- Stop and reassess if a supposedly structural change alters outputs, errors, API shape, concurrency, or measured performance.
Use references/workflow.md for sequencing a larger refactor without prescribing a particular Git hosting model.
Deliverable
Report the structural goal, affected surface, behavior-preservation evidence, compatibility choices, verification performed, and any residual migration steps. Do not call a partial compatibility shim “complete” if old callers or cleanup work remain.