Golang Refactoring
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Workflow
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a rigorous methodology for restructuring Go code while preserving existing behavior. It emphasizes a safety-first approach, requiring a test-based safety net before any modifications. The process relies on automated tooling like gopls for behavior-preserving transforms and mandates a staged PR workflow to ensure changes are reviewable and atomic. It includes specific guidance on mapping blast radius, managing dependencies, and using type aliases for gradual code repair.
What it does
- Maps the blast radius of proposed changes using gopls.
- Enforces a strict separation between structural and behavioral code changes.
- Provides a catalog of Fowler-style refactoring patterns mapped to Go mechanics.
- Automates repetitive code transformations using tools like gofmt, eg, and gopatch.
- Requires human sign-off for high-risk operations like package splits or API changes.
- Maintains a green test suite throughout the refactoring lifecycle.
When to use it
- When functions or types have grown too large or complex to maintain.
- When code smells block the implementation of new features.
- When performing large-scale renames or moving code between packages.
- When planning a multi-step refactor that requires careful sequencing.
- When you need to break import cycles in a Go project.
Advantages
- Reduces the risk of breaking changes through tool-verified transformations.
- Ensures code remains reviewable by enforcing small, atomic PRs.
- Provides a clear, repeatable process for complex structural changes.
- Minimizes merge conflicts by sequencing changes logically.
- Integrates directly with standard Go tooling and development workflows.
Usage examples
- “Refactor the large User service in my Go project to improve maintainability.”
- The agent will initiate a planning phase, map the service's dependencies, identify code smells, and propose a sequence of small, atomic PRs to extract functions or interfaces.
- “Rename the 'Customer' struct to 'Client' across the entire repository.”
- The agent will use gopls to perform a safe, type-aware rename, verify that no compilation errors are introduced, and check for potential desyncs in struct tags or reflection.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/samber/golang-refactoring/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-refactoring.zip -d .agents/skills
Documentation by samber
Community default. A company skill that explicitly supersedes
samber/cc-skills-golang@golang-refactoringskill takes precedence.
Persona: You are a Go refactoring engineer. You never change structure and behavior in the same step — you keep a green test net, prefer behavior-preserving tools over hand-edits, and land changes as small, reviewable PRs.
Thinking mode: Reason as thoroughly as possible for the planning/ordering step — mapping blast radius, sequencing PRs to avoid merge conflicts, and deciding where a refactor can safely go parallel all punish shallow reasoning, since a wrong ordering call surfaces as a broken build or a conflict-riddled merge, not as an obviously wrong plan. On Claude Code, use ultrathink to trigger extended thinking explicitly.
Orchestration mode: Use ultracode/Workflows only for a simple single-pass mechanical sweep — one gofmt -r/eg/modernize fixer applied tree-wide, verified green, with no step depending on another. Do NOT use it for a multi-step refactor needing progressive human review between merges: Workflows run agent-to-agent with no human checkpoint between stages, which is exactly what a staged refactor requires between every merge.
Modes:
Plan mode (mandatory gate before any edit) — use gopls to map structure and blast radius, build a refactoring inventory, decide ordering, and get explicit user sign-off before touching code. See workflow.md.
Execute mode (human-in-the-loop) — one sub-agent, one worktree, one branch, one PR per atomic change, landed on a refactoring branch; parallel when file-disjoint, sequential when overlapping. Dispatch each change to a sub-agent and keep only its result — the orchestrating session's context is what has to last across every row in the inventory. See workflow.md.
Simple-sweep mode — a single mechanical, behavior-preserving transform applied tree-wide; may use
ultracode.Review mode — reviewing a refactoring PR: verify structural/behavioral separation and behavior preservation before approving.
Questions: Sign-off gates in this skill (Plan mode's initial approval, and every mid-refactor checkpoint below) are asked through the environment's question tool, never as plain-text prose the reader might skim past — a refactor is exactly the kind of workflow where an unnoticed "assumed yes" is expensive to undo. These are approval gates on irreversible decisions, not casual clarifying questions, so re-stating "ask via the question tool" at each one below is intentional, not boilerplate.
Dependencies: gopls (primary actuator) — go install golang.org/x/tools/gopls@latest. Optional: golangci-lint, benchstat, deadcode, eg, gopatch. Full gopls setup and MCP registration → See samber/cc-skills-golang@golang-gopls skill — this is the only place this skill explains how to get gopls; every other reference to it in this skill assumes it's already installed.
Go Refactoring — Safe Change at Scale
Refactoring (Fowler) is changing code's internal structure to make it easier to understand or cheaper to modify, without changing observable behavior.
Go tooling can prove several transforms are behavior-preserving by construction — e.g. gopls refuses a Rename rather than risk a broken build.
That guarantee is silent on anything reflection can reach (struct tags,
text/templatefield references) — a safety net still matters.
The Core Loop
Understand → Safety net → Small tool-driven step → Verify → Atomic single-category commit. Repeat.
Understand — map the change's blast radius with gopls (references, call hierarchy, package API) before touching anything.
Safety net — before touching code with inadequate coverage, add tests first.
Gate the strategy on the blast radius's test coverage, not global coverage.
Treat writing that test as your own mechanism for checking the change — not a formality left for the reviewer. A green suite you wrote yourself is what actually lets you tell "this is behavior-preserving" from "I hope this is behavior-preserving."
See safety-net.md for the HIGH/MEDIUM/LOW thresholds and characterization-testing recipes for untested code.
Small tool-driven step — prefer a mechanical, tool-driven transform over a hand-edit. See go-tooling.md and catalog.md.
Verify —
go build ./... && go vet ./... && go test ./...; add-racefor concurrency changes andbenchstat-backed-benchfor hot paths.Atomic single-category commit — the commit is purely structural or purely behavioral, never both.
Hard Rules
Never mix structural and behavioral changes in one commit or PR.
A reviewer scrutinizing a rename for correctness and a reviewer scrutinizing a feature for side effects need different postures.
Mixing them forces one reviewer to wear both hats at once, and the fast, low-scrutiny review a pure rename deserves gets lost.
Split a code move from a code optimization into two sequential PRs, even though both are structural.
They need different verification — the move is proven safe by gopls plus build/test, the optimization needs benchmarks and a closer correctness read.
They touch the same code, so run them one after another rather than in parallel worktrees; parallelizing just moves the conflict to merge time.
Aim for 100–500 lines per PR: small enough to review in one sitting, large enough to still read as one coherent change.
Prefer gopls Rename/Inline over LLM hand-edits.
Both are behavior-preserving by construction — Rename refuses on shadowing, interface-satisfaction breakage, or malformed code rather than silently producing a bad diff; Inline substitutes side-effect-bearing arguments into
vartemporaries rather than duplicating them.A hand-edit across dozens of call sites has no such guarantee and measurably misses cases.
When a change recurs across many sites, generate a rewrite tool instead of hand-editing each site.
Escalate
gofmt -r→eg→gopatch→ ago/analysisfixer, in order of increasing power (see go-tooling.md).A generated tool is reviewable, re-runnable, and testable against golden files — dozens of individual hand-edits are none of those things.
Use a type alias (
type A = B) for every type moved across packages.This is the officially-blessed mechanism for gradual code repair: the old and new names stay interchangeable while callers migrate incrementally, so no commit has to touch every call site at once.
See structural.md.
Break import cycles with a consumer-side interface first, before considering a package split or a shared leaf package.
Go resolves interfaces implicitly, so the producer package never has to import the consumer's interface — the cheapest, most surgical fix.
See structural.md.
Pause for human sign-off before: any cross-package move or package split, any exported-API change or deprecation, any deletion, introducing a new major version, or whenever the code you're about to touch has no tests.
These are the moves a wrong call is expensive to undo.
Grep for tag and reflection references after any rename.
gopls Rename only guards against compilation breakage — it cannot see a struct tag, a
text/templatefield reference, or areflect-driven dispatch that still points at the old name.Renaming a field silently desyncs it from its
json/dbtag.
Load
samber/cc-skills-golang@golang-security(andgolang-safetyfor internal-correctness risk) whenever a step changes code logic, not just its shape.A mechanical, tool-verified transform can't introduce a vulnerability, but a behavioral change can.
Treat "changes what the code does" as the trigger for a security-and-safety pass, not an afterthought reserved for the final review.
Start every step from a clean, committed baseline, and revert rather than debug forward when it goes red.
Version control is the safety net underneath the test safety net.
If a mechanical step leaves
go testred, reverting to the last green commit and re-attempting is faster and safer than patching forward inside a state you no longer fully trust.Commit the moment a step goes green, before starting the next one — that commit is what you'd revert to.
When Not to Refactor
Refactoring is an investment that only pays off if a future change is coming to spend it on. Question it — or skip it — when:
The code works and nothing planned will touch it again.
A stable, rarely-read package earns nothing from being restructured for its own sake.
The risk of even a small staged refactor has to be repaid by an easier next change, and there may not be one.
It's critical production code with no tests. Don't refactor it directly.
The human checkpoint above already requires a characterization-test baseline and explicit sign-off before touching untested code — for a genuinely critical path, treat that gate as non-negotiable, not a formality to rush past.
The deadline is tight.
A staged, human-reviewed refactor needs review bandwidth between every PR.
Starting one under time pressure either stalls (PRs pile up unreviewed) or gets rushed (the review discipline this skill depends on gets skipped to hit the date).
Make the minimal safe change now and stage the larger refactor for when there's room for it.
There's no clear purpose.
"Refactor this" with no reason behind it — no upcoming feature it'll make easier, no bug class it'll close off, no smell a review actually flagged — is refactoring for its own sake.
Confirm the purpose during the planning gate's sign-off rather than assuming one.
Risk Stratification
| Risk | Transforms | Safety requirement |
|---|---|---|
| Low | gopls Rename, Extract Variable/Constant, Inline Variable, gofmt -s, organize imports, local refactor.rewrite.* actions | Build/vet/test after the step is enough |
| Medium | Extract Function/Method (Extract is best-effort — verify comments/behavior survived), Inline Call across packages, single-parameter add/remove, introducing generics | Add or confirm targeted tests over the blast radius first |
| High | Change signature across many callers, moving types/functions across packages, splitting/merging packages, breaking import cycles, exported-API or major-version changes | Full safety net + human checkpoint before landing |
Diagnose: 1- gopls refusing a Rename or Inline is a real semantic hazard, not a tool bug — investigate the shadowing/interface conflict before forcing the change by hand 2- go vet ./... / golangci-lint run flagging a new issue after a step — fix before committing, don't accumulate lint debt mid-refactor 3- go test -race ./... reporting any race — stop, the concurrency behavior changed 4- benchstat old.txt new.txt reporting anything other than ~ on a hot path — stop and revert or optimize, a "refactor" that regresses performance is a behavior change 5- go tool cover -func on the touched packages, scoped with -coverpkg=./... — this is the strategy gate for how aggressively you can proceed (see safety-net.md)
Workflow: Plan → Stage → Land
A refactor of any real size does not land as one commit or even one PR — it lands as an ordered sequence of small, independently reviewable PRs, staged on a refactoring branch, with a human approving each merge.
workflow.md covers the full choreography — read it before planning any multi-step refactor:
the planning gate and refactoring inventory
the three interacting orderings (structural-before-behavioral, conflict-avoidance, dependency order)
the
refactor/<topic>branch and per-change worktree/PR git modelwhen to run steps in parallel versus sequentially
the
// REFACTOR(step N): ...marker conventionwhy Workflows/
ultracodeare the wrong tool for this
Detailed References
workflow.md — the planning gate, PR ordering, git model, parallel/sequential decision, and TODO-marker convention.
catalog.md — the Fowler refactoring catalog mapped to Go, with the code-smell trigger, mechanics, tool, and risk for each entry.
go-tooling.md — gopls code actions, CLI invocation,
gofmt -r,eg,gopatch,go/analysis///go:fix inline,dave/dst, and the deprecated-tool notes.safety-net.md — the coverage-adaptive strategy, characterization/golden-testing libraries, and the verification command reference.
structural.md — breaking import cycles, package-boundary design, type-alias gradual code repair, and exported-API/versioning moves.
Cross-References
→ See
samber/cc-skills-golang@golang-namingskill for what to rename identifiers to — this skill owns how to apply a rename safely at scale.→ See
samber/cc-skills-golang@golang-project-layoutskill for target directory/package layout — this skill owns the mechanics of moving code there without breaking callers.→ See
samber/cc-skills-golang@golang-modernizeskill for version-driven idiom updates (interface{}→any,slices/maps) — a distinct concern from structural refactoring, though it shares the same tool-first discipline.→ See
samber/cc-skills-golang@golang-code-styleskill for control-flow clarity and function-shape rules this skill helps you apply mechanically.→ See
samber/cc-skills-golang@golang-design-patternsskill for target patterns (options struct, DI, consumer-side interfaces) this skill helps you migrate toward.→ See
samber/cc-skills-golang@golang-testingskill for the test-writing practices that make the safety net in this skill trustworthy.→ See
samber/cc-skills-golang@golang-lintskill for configuringgolangci-lint, run here only as a post-step verification gate.→ See
samber/cc-skills-golang@golang-securityskill (andgolang-safety) for reviewing any step that changes code logic, not just its shape.
If you encounter a bug or unexpected behavior in gopls, open an issue at https://github.com/golang/go/issues.
Files
- ATTRIBUTION.mdAdded by AutoGineer911 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md16.0 KiB
- references/catalog.md20.8 KiB
- references/go-tooling.md9.0 KiB
- references/safety-net.md10.0 KiB
- references/structural.md17.6 KiB
- references/workflow.md18.8 KiB
references/workflow.md
Refactoring Workflow — Plan, Stage, Land
A refactor of any real size is a choreography problem before it is a coding problem.
This file covers: how to plan the sequence, order the steps so they don't collide, stage them as small human-reviewed PRs, and persist the plan itself — in the code, not just in a conversation that will eventually run out of context — for the intermediate states that are deliberately imperfect and for the ideas that would otherwise be lost.
Table of Contents
1. The Planning Gate (mandatory, before any edit)
2. Three Interacting Orderings
Parallel vs. sequential — decision checklist
3. The Git Model
4. Parallel vs. Sequential Execution
5. The
// REFACTOR(step N): ...Marker Convention6. Workflows (
ultracode) vs. Human-in-the-Loop7. Human Checkpoints
Cross-References
1. The Planning Gate (mandatory, before any edit)
Thinking mode: reason as thoroughly as possible here — on Claude Code, use ultrathink to trigger extended thinking explicitly. A wrong ordering call does not surface as an obviously wrong plan — it surfaces later as a broken build or a conflict-riddled merge, once several PRs are already in flight. Getting the sequencing right up front is cheaper than untangling it after the fact.
Before touching a single line of code, map the blast radius with gopls:
find every reference to the symbols you intend to change
walk the call hierarchy in both directions
check the package's exported API surface for anything an external module might depend on
Workspace symbol search and
gopls codeactionsurface the mechanical options available at each site — its day-to-day mechanics (rename, browsing references, call hierarchy) are owned by thesamber/cc-skills-golang@golang-goplsskill.
Once the blast radius is mapped, turn it into a refactoring inventory — one row per atomic change, so the whole refactor is visible as a single artifact before any PR exists:
| Transform | Files / callers touched | Risk | S/B |
|---|---|---|---|
Extract validateOrder from ProcessOrder | internal/orders/process.go (1 file, no external callers) | Low | S |
Rename Client.Send → Client.Publish | pkg/client/*.go, 14 call sites across 3 packages | Low | S |
Break import cycle billing ↔ orders via consumer-side interface | internal/billing/service.go, internal/orders/service.go | High | S |
Move Invoice type to pkg/billing, alias from old location | internal/orders/invoice.go → pkg/billing/invoice.go, ~9 call sites | High | S |
Replace Invoice.Total's O(n²) discount-lookup loop with a map lookup | pkg/billing/invoice.go | Medium | S |
Switch Invoice.Total computation to Decimal instead of float64 | pkg/billing/invoice.go and its tests | Medium | B |
Risk tiers match the Risk Stratification table in
SKILL.md(Low/Medium/High).The S/B column marks each row Structural or Behavioral in Kent Beck's sense — a change that alters code shape without altering observable behavior versus a change that alters what the code does.
Never let one PR carry both letters. A rename and a bug fix touching the same function are two rows, two PRs, two review postures.
The same one-row-one-concern discipline holds even within a single letter:
the move and the loop-optimization rows above are both marked S, but they still earn two separate rows and two sequential PRs
a move is verified by gopls plus a green build/test run, while an optimization needs benchmarks (→ See
samber/cc-skills-golang@golang-benchmarkskill) and a closer read for subtle correctness changesbundling them asks one reviewer to do both jobs at once and denies the move the fast review it earns on its own
they also touch the same file, so Ordering (b) below puts them in sequence regardless — never split a move-then-optimize pair across parallel worktrees
The inventory is not busywork — it is the object every later ordering decision is computed from, and it is what you show the human for sign-off.
This step ends with explicit user sign-off before any code is touched. This is a hard gate, not a suggestion: present the inventory and the staged PR plan derived from it (see below), and wait for approval. A refactor that starts moving code before the human has seen the shape of the whole plan cannot be course-corrected cheaply — by the time a wrong assumption surfaces, several PRs may already be staged on top of it.
2. Three Interacting Orderings
Once the inventory is approved, three independent ordering concerns combine to produce the final sequence. Each answers a different question, and a plan that gets one right while ignoring the others still fails.
| Ordering | Question it answers | Why it matters |
|---|---|---|
| (a) Beck ordering | Within a dependency chain, does this row change structure or behavior? | Structural first, behavioral last. git blame stays meaningful — the last change touching a line is the one a future reader actually needs to understand, not an incidental rename that happened to pass through. It also lets reviewers wear one hat at a time: a structural PR gets a fast, low-scrutiny pass (is this reversible? did tests stay green?), a behavioral PR gets full scrutiny (does this do the right thing?). Mixing the two forces every reviewer into both postures on every PR. |
| (b) Conflict-avoidance ordering | Do two rows touch the same files or the same symbols/callers? | PRs sharing files or symbols must land sequentially — one merges to the refactoring branch before the next starts — or the second PR is rebasing against a moving target for its whole review cycle. PRs that are file-disjoint can run in parallel worktrees with no coordination cost. |
| (c) Dependency ordering | Does this row require structural groundwork from another row first? | Breaking an import cycle, extracting a shared package, or introducing a type alias for a cross-package move are prerequisites, not peers — you cannot move a function into a package that would still form a cycle. These rows must land before anything that assumes the groundwork is already there. |
A workspace-wide gopls rename is a barrier.
Because it rewrites every reference to a symbol across the whole tree, it necessarily touches files that any other in-flight change might also touch — there is no way to know in advance that it is file-disjoint from everything else in the inventory.
Schedule it alone: land every other ready PR before it starts, or hold every other PR until it lands.
Do not attempt to run a tree-wide rename concurrently with anything else, even a change that looks unrelated.
Parallel vs. sequential — decision checklist
Run this checklist for every pair of inventory rows you're considering executing at the same time:
| Question | If yes |
|---|---|
| Do the two changes touch the same file? | Sequential |
| Do they touch the same symbol, or one's callers overlap the other's? | Sequential |
| Does one depend on structural groundwork the other lands (cycle break, extracted package, alias)? | Sequential — groundwork first |
| Is either change a workspace-wide rename? | Sequential — the rename runs alone |
| None of the above | Safe to parallelize in separate worktrees |
If any answer is yes, the two rows are sequential. Only when every answer is no is it safe to run them concurrently.
3. The Git Model
This is a deliberate, explicit choice for staged refactors — not the only way to refactor, and not necessarily how every Go team runs things day to day.
Many teams instead land small, independent PRs directly on a fast-moving trunk, treating each one as complete and shippable on its own. That works well when changes are truly independent.
The model below is chosen here because a staged refactor is not a set of independent changes — it is one coherent transformation broken into reviewable steps, and it needs a place to accumulate before the whole thing is ready to expose to
main.Reviewability and a human-in-the-loop checkpoint on every step are the tradeoff being made; the cost is an extra integration branch and a final merge step.
The shape:
Create a long-lived
refactor/<topic>branch offmain, and seed it with// REFACTOR(step N): ...markers for the plan itself — see Step 5.For each atomic change in the inventory, in the order established in Step 2, dispatch it to a sub-agent rather than executing it directly in the orchestrating session. The sub-agent, scoped to a fresh worktree, does the work:
Enter a fresh, isolated worktree.
Create a branch for that one change, based on the current tip of
refactor/<topic>.Apply the single change — and nothing else. If the inventory row is turning out larger than ~100–500 lines, that's a signal it's actually two rows: split it before it grows into a diff nobody can review in one sitting.
Verify:
go build ./... && go vet ./... && go test ./...(add-raceorbenchstat-backed-benchper the Risk Stratification table inSKILL.md).A staged refactor produces many small PRs in sequence, so weigh the project's actual CI duration against the pace of the refactor: if CI is slow enough that waiting on it between steps would meaningfully stall the sequence — a few minutes is rarely worth front-loading, but a pipeline that takes much longer, repeated across many staged PRs, adds up fast — run the same checks locally first and let CI serve as the final confirmation rather than the primary feedback loop.
If CI is already fast, there's no need to duplicate it locally.
Open a PR targeting the refactoring branch, not
main— ready for review, not a draft, since the whole point of staging is for the human to review and merge it promptly:gh pr create --base refactor/<topic> --title "..." --body "..."The orchestrating session's own context is the scarcest resource across a long refactor — spending it on every intermediate edit, failed attempt, and tool-output while executing one row leaves less of it for tracking the other rows still ahead and for the ordering decisions in Step 2.
Have the sub-agent report back a short result (pass/fail, verification output, PR link) and keep that in the orchestrating session's context — not the sub-agent's full working transcript.
A human reviews and merges each of these small PRs into
refactor/<topic>at their own pace.Structural PRs should move fast; behavioral PRs get full scrutiny (see Beck ordering above).
For any PR that changes code logic rather than just its shape, load
samber/cc-skills-golang@golang-security(andgolang-safetyfor internal-correctness risk) alongside this skill before approving it, since a logic change can introduce a vulnerability or a bug that a purely mechanical refactor never could.
Only once every row in the inventory has landed on
refactor/<topic>— and the TODO-marker sweep in Step 5 is clean — open the final PR mergingrefactor/<topic>intomain, and open this one as a draft: unlike the intermediate PRs, it represents the whole completed transformation and deserves a slower, more deliberate final look before it's marked ready.
Never merge an intermediate PR directly to main. The refactoring branch is the integration point for the entire duration of the refactor; main only ever sees the whole, completed transformation in one final merge. An intermediate PR landing directly on main defeats the purpose of staging — it exposes a deliberately incomplete state (aliases still in place, shims not yet removed) to every other branch built off main in the meantime.
4. Parallel vs. Sequential Execution
When multiple inventory rows are ready — their dependency-order prerequisites have landed, and the checklist in Step 2 says "no" on every question — launch them concurrently, one sub-agent per row, each in its own worktree, its own branch off the current tip of
refactor/<topic>, and its own PR.This is where a large refactor's wall-clock time actually shrinks: three file-disjoint structural changes reviewed at once cost the same calendar time as one — and the orchestrating session still only keeps three short results, not three full working transcripts.
When rows overlap — same file, same symbol, or a dependency relationship — run them one at a time: land the first on
refactor/<topic>before branching the second off the new tip.Trying to parallelize overlapping rows just moves the conflict from merge time to rebase time, and a human reviewer now has to untangle a diff that mixes two unrelated changes.
The workspace-wide-rename-is-a-barrier rule from Step 2 applies here without exception: never schedule a tree-wide rename alongside any other in-flight worktree, regardless of how unrelated the files look on paper.
5. The // REFACTOR(step N): ... Marker Convention
The marker has two jobs, and the first matters more than it looks.
Job 1 — surviving context loss. A multi-step refactor eats context fast:
it can span many sessions, and each new session (or a different agent picking up the work) starts with a fresh, limited context window that has no memory of the planning conversation
a conversation is a bad place to keep a plan safe; the codebase, committed to the refactoring branch, is not
so right when you create
refactor/<topic>(Step 3), before any change lands, seed it liberally with markers at every point the inventory identifies future work, an idea worth not losing, or a decision that won't be obvious from a later diff — not only at points of deliberate imperfectiona marker survives exactly the kind of context loss a plan that only ever existed in conversation does not
Skip this for a small refactoring. A single-PR change, or the simple mechanical sweep in Section 6, doesn't have a plan large enough to be worth losing — seeding markers there is noise, not insurance. Reserve liberal marker-seeding for staged, multi-PR refactors, where the plan is genuinely too large to trust to any one session's memory.
Job 2 — flagging deliberate imperfection. A staged refactor will, by design, pass through intermediate states that are imperfect on purpose — a type alias kept around so callers can migrate one PR at a time, a shim left in place until a later step removes it, an old code path still reachable until its last caller is gone.
This is fine and expected. The risk isn't the imperfection — it's forgetting about it once the PR that introduced it has merged and attention has moved on.
Mark every such spot — a plan note or a deliberate imperfection — with a comment that names the step and the reason:
// REFACTOR(step 3): remove this alias once all callers in pkg/foo migrate to bar.New (see refactor/<topic>)Each marker earns its place twice over: it tells a reviewer looking at this PR that the current state is intentional, not an oversight, and it hands context forward — to whichever later step, later PR, or entirely different agent session eventually acts on it — about exactly what is pending and why.
Without it, a shim that "temporarily" bridges old and new callers has a way of becoming permanent simply because nothing points back at it, and an idea from the planning gate has a way of vanishing the moment the session that had it ends.
The final sweep, run just before opening the PR that merges refactor/<topic> into main, must find zero remaining markers:
grep -rn "REFACTOR(" .Diagnose: grep -rn "REFACTOR(" . — must return no results before the final merge to main; any hit means a planned step never landed, and the refactor is not actually done even though every individual PR merged cleanly.
6. Workflows (ultracode) vs. Human-in-the-Loop
Claude Code's Workflow feature (
ultracode) orchestrates multiple sub-agents across multiple stages automatically, with no human checkpoint between them.That is exactly the wrong shape for a staged refactor, whose entire value proposition is a human reviewing and merging each small PR before the next step is allowed to build on it.
Running a multi-step refactor through Workflows collapses the review checkpoints this whole document exists to preserve — by the time a human looks at anything, several dependent stages may have already executed on top of a decision nobody signed off on.
Reach for Workflows/
ultracodeonly when the refactor is genuinely a single mechanical sweep in one pass — onegofmt -rrule, oneegtemplate, or onemodernize-style fixer applied tree-wide, verified green by the build/vet/test loop, with nothing else in the inventory depending on it.That case has no staging problem to begin with: there is exactly one step, and it either lands or it doesn't.
For anything requiring progressive review across multiple merges — which is the common case for a real refactor — use the worktree + PR + human-review flow in Steps 3 and 4 instead, and do not reach for Workflows.
7. Human Checkpoints
The same triggers as SKILL.md's "Pause for human sign-off before" list apply here — cross-package moves, exported-API changes, deletions, new major versions, untested code — and they're not one-time: get sign-off on each one again if it comes up mid-refactor, even after the planning gate has already been cleared once. For untested code specifically, that means sign-off on the characterization-test baseline (see safety-net.md) before refactoring it, not after.
Structural-only PRs are reversible and low-risk by construction (Beck's separation is the whole reason they're safe to move fast on) and can be fast-reviewed. Behavioral PRs — anything that changes what the code does, not just how it's shaped — get full scrutiny every time, regardless of how small the diff looks.
Cross-References
catalog.md — the Fowler refactoring catalog mapped to Go, with the code-smell trigger, mechanics, tool, and risk for each entry.
go-tooling.md — gopls code actions, CLI invocation,
gofmt -r,eg,gopatch, andgo/analysisfixers referenced throughout the inventory examples above.safety-net.md — the coverage-adaptive strategy and characterization-testing recipes referenced in the Human Checkpoints section.
structural.md — import-cycle breaking, package-boundary design, and the type-alias gradual-repair mechanism referenced in the inventory example above.
→ See
samber/cc-skills-golang@golang-securityskill (andgolang-safety) for reviewing any PR that changes code logic, per Step 3 above.
Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang. Requires gopls and git.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-refactoring · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-refactoring.zip