To refactor a legacy module in Go with Atlas, enumerate the callers before you touch anything. Atlas maps the package's exported surface with the lsp tool's documentSymbol operation, runs findReferences on every exported symbol to find the callsites go test alone would not reveal, then restructures with apply_patch, which anchors each hunk on context lines and fails with "Failed to find context" if the file has drifted. You pin behavior first by running go test through the bash tool and recording a green baseline, and you re-run go test after each hunk lands rather than once at the end. go mod and gofmt keep the module and its formatting honest throughout.
How do I find every caller of a legacy Go package before refactoring it?
Map the Go package's public surface with the lsp tool's documentSymbol operation, then run findReferences on each exported symbol. In a legacy Go module with 18 exported functions and 4 interfaces, documentSymbol enumerates them in one call, and findReferences turns each into an authoritative callsite list from the language server.
The risk in a Go refactor is silent breakage at a callsite you did not know about. Go makes this worse than most languages in one specific way: an exported type can satisfy an interface it never names, so a struct in internal/store can be wired into a handler in cmd/api purely by shape. Grep will not find that relationship, and neither will reading the package. The lsp tool's findReferences will, because the language server resolves the actual type graph. Run it on every exported symbol that documentSymbol returned, not just the ones you plan to change, because a refactor that moves a method between files can change which interface a type satisfies. Atlas indexes code by AST declarations using tree-sitter, so the symbols come back whole.
Why does Atlas run go test before it changes any Go code?
Atlas pins behavior first: run the existing tests with the bash tool and record the green baseline before changing anything. A legacy Go module with a flaky table-driven test in store_test.go is a module where a post-refactor failure is ambiguous, and 5 minutes spent establishing a baseline saves an hour of blaming the wrong change.
Run go test through Atlas's bash tool across the packages you are about to touch and read the output before you edit a line. If a test was already red, you now know it, and you know not to attribute it to the refactor. If the module has no tests at all, that is also information, and the honest move is to add a table-driven test in Go's standard style first, so there is something to hold the behavior still. The goal of a refactor is a structural change with no behavioral change, and the only way to make that claim credible in Go is a go test run that was green before and is green after.
How does apply_patch keep a Go refactor from corrupting a file?
Atlas restructures Go code with apply_patch, which anchors each hunk on 2 things, its context lines and its old_lines, and fails with "Failed to find context" when the file has drifted. A patch written against internal/store/pg.go before an earlier hunk reshuffled its imports refuses to apply rather than landing in the wrong place.
That failure mode is the point. A Go refactor of a legacy module is a long sequence of small structural moves: extract an interface, thread a context.Context through, split one 900-line file into three. Each move invalidates the line numbers the next one assumed. apply_patch's context anchoring turns that from a silent corruption into a loud error, and Atlas computes a unified diff for every file edit and surfaces it for approval before writing, so you see what a hunk will do before it does it. Atlas also snapshots file changes as git patches so edits can be diffed and rolled back, which means a hunk that lands and then breaks go test is one revert away from gone.
How often should I run go test during a Go module refactor?
Re-run go test with Atlas's bash tool after each hunk lands, not once at the end. A legacy Go refactor with 7 apply_patch hunks that only gets tested once produces a single red run and 7 suspects. Testing per hunk produces the failing hunk by name and cuts triage to nothing.
The Go toolchain is fast enough that per-hunk testing is affordable in a way it is not in every language: go test on a single package is usually a matter of seconds, so scope the run to the package you just patched rather than the whole module. Run gofmt on the touched files so that a structural change does not also produce a diff full of alignment churn, and let go mod tell you whether extracting a package introduced a dependency edge you did not intend. If go mod tidy wants to move something in go.sum, that is a signal worth reading, not noise to accept blindly.
How do I track a partially migrated Go module so it is not mistaken for a finished one?
Track the remaining callsites in a todowrite list. Atlas's todowrite tool exists so that a Go module halfway through a refactor, with 3 of 11 callsites migrated to the new interface, cannot be mistaken for a finished one when the session ends or the context rolls over.
The findReferences output from step one is the natural source of the list: one todowrite entry per callsite, marked pending until the patch lands and go test is green for its package. In Go this discipline matters because a half-migrated module still compiles. The old exported function and the new interface can coexist happily, the build stays green, and nothing tells you that cmd/worker is still calling the deprecated path. The todowrite list does. When every entry is done, run go test across the module, run gofmt, and let Atlas read git status and the diff and stage the commit on your behalf.
Step by step
- 01Run atlas in a module with a go.mod and let Atlas read your packages, interfaces, and go.sum dependencies.
- 02Map the legacy module's public surface with the lsp tool's documentSymbol operation to enumerate every exported function, type, and interface in one call.
- 03Run the lsp tool's findReferences operation on each exported symbol to get the authoritative callsite list, including the implicit interface satisfaction that grep cannot see in Go.
- 04Pin behavior first: run go test through Atlas's bash tool across the affected packages and record the green baseline before changing anything.
- 05Restructure with apply_patch, which seeks each hunk's context and old_lines and fails with "Failed to find context" if the file has drifted since the patch was written.
- 06Re-run go test after each hunk lands, scoped to the package you just patched, rather than running the full module once at the end.
- 07Run gofmt on the touched files so the structural diff is not buried in alignment churn, and check whether go mod reports a dependency edge you did not intend.
- 08Track the remaining callsites in a todowrite list, one entry per findReferences hit, so a half-migrated Go module (which still compiles) is never mistaken for a finished one.
Frequently asked questions
- how to refactor a legacy go package without breaking callers
- Enumerate the callers first. Atlas maps the package with the lsp tool's documentSymbol operation, then runs findReferences on each exported symbol to get the language server's authoritative callsite list. Only then does it restructure with apply_patch, with go test green between each hunk.
- why does grep miss callers in go refactoring
- Go types satisfy interfaces implicitly, by shape rather than by declaration, so a struct can be wired into a handler without ever naming the interface. Grep cannot see that edge. The lsp tool's findReferences resolves the real type graph and does.
- what does failed to find context mean in atlas apply_patch
- Atlas's apply_patch seeks each hunk's context and old_lines before writing. "Failed to find context" means the target file drifted since the patch was written, usually because an earlier hunk reshuffled it. The patch refuses to apply rather than landing in the wrong place.
- should i run go test after every change during a refactor
- Yes. Run go test through Atlas's bash tool after each apply_patch hunk, scoped to the package you patched. A single run at the end of 7 hunks gives you one red result and 7 suspects, while per-hunk runs name the culprit immediately.
- how do i keep track of a partially migrated go module
- Use a todowrite list with one entry per callsite from findReferences, pending until its patch lands and go test is green. A half-migrated Go module still compiles, so nothing else will tell you that cmd/worker is still calling the deprecated path.
- does atlas run gofmt and go mod during a refactor
- Atlas runs gofmt on the touched files through its bash tool so the structural diff is not buried in alignment churn, and it reads go.mod and go.sum so a package extraction that introduces an unintended dependency edge is visible rather than silent.
- can i undo a go refactor step that atlas applied
- Yes. Atlas snapshots file changes as git patches, so any edit can be diffed and rolled back. A hunk that lands and then turns go test red is one revert away from gone, without unwinding the hunks that were correct.
Try Atlas in your terminal
The terminal-native AI coding agent. Free core, single binary.
Install AtlasRelated guides
Refactor a Legacy Module with Atlas in 2026
How to refactor a legacy module with Atlas in 2026: findReferences maps every callsite, apply_patch refuses to apply against a drifted file, and bash proves behavior.
Atlas for Go in 2026
Atlas, the terminal-native AI coding agent, empowers Go developers in 2026 with intelligent code understanding, safe refactoring, and robust testing capabilities.
Add a Regression Test for a Bug Fix in Go with Atlas (2026)
Lock a Go bug fix in place in 2026: Atlas writes a failing TestXxx, proves it red with go test -run, patches the code with edit, and re-runs go test ./... to prove it green.
Trace a runtime bug from a stack trace in Go with Atlas (2026)
Go from a Go panic stack trace to the responsible line in 2026 with Atlas: read each frame at its offset, grep the error string, and walk callers with lsp findReferences.
Run the test suite and triage the failures in Go with Atlas (2026)
Triage a wall of red go test output with Atlas in 2026: bash truncates at 2000 lines or 50 KB, saves the full log, and turns distinct root causes into a todowrite list.
Write Unit Tests for Untested Go Code with Atlas in 2026
Add real Go tests to an untested package in 2026: Atlas enumerates exported symbols with lsp, copies your table-driven conventions, writes the _test.go file, and runs go test.
Onboard to an Unfamiliar Go Codebase with Atlas in 2026
Onboard to an unfamiliar Go codebase in 2026. Atlas reads your go.mod, packages, interfaces, and go.sum dependencies, then ranks files with codebase_search.
Document a Go Module with a README Using Atlas (2026)
Atlas writes a Go module README from source in 2026: documentSymbol enumerates the exported API, codebase_search finds real callers, and go test verifies samples.