Go 1.26's Source-Level Inliner: A Q&A Guide to API Migration and Refactoring
Go 1.26 introduces a redesigned go fix subcommand and a powerful new tool called the source-level inliner. This inliner helps developers automate API migrations and refactorings by rewriting function call sites directly in source code. Unlike traditional compiler inlining, which optimizes at build time, source-level inlining produces durable, human-readable changes. In this Q&A, we explore what the source-level inliner is, how it works, and how you can use it to keep your Go code modern.
What is the source-level inliner and how does it differ from compiler inlining?
The source-level inliner is a tool that replaces a function call with a copy of the function’s body, substituting arguments for parameters directly in your Go source files. This transformation is permanent and modifies your codebase, unlike compiler inlining which operates on an ephemeral intermediate representation to generate faster machine code. The compiler’s inlining is invisible to developers, while source-level inlining produces a clear, editable change that you can review and commit. For example, if you inline a call to a small sum function, the result is a concrete expression rather than a call site. This makes the inliner ideal for refactoring and API migration because the outcome is both correct and transparent.

How does the source-level inliner work under the hood?
The algorithm behind the source-level inliner was originally developed in 2023 and is now integrated into both gopls and the go fix command. It takes a function call, copies the called function’s body, and replaces all parameter references with the actual arguments. Crucially, it handles subtle correctness issues like variable shadowing, side effects, and proper scoping. For instance, if an argument contains a side-effecting expression, the inliner ensures it is evaluated exactly once. The tool also respects Go’s syntax and adds necessary parentheses to preserve evaluation order. This meticulous approach makes the transformation safe for automatic application, whether invoked via an IDE or the command line.
How can developers use the source-level inliner for API migration with go fix?
The go fix command in Go 1.26 includes the source-level inliner as one of its analyzers. This allows package authors to define self-service migrations by marking functions with a special directive, such as //go:fix inline. When a developer runs go fix on their project, the tool automatically inlines calls to those marked functions, effectively modernizing the codebase. For example, if you deprecate a helper function and provide a replacement, you can annotate the old function to be inlined. Then go fix will replace all usages with the inlined body, which might include the new recommended pattern. This shifts the burden of manual updates from developers to automated tooling, reducing errors and saving time.
What is the role of the source-level inliner in gopls refactorings?
Before appearing in go fix, the source-level inliner was already a key component of gopls, the Go language server. It powers interactive refactorings such as “Inline call” (available in VS Code’s Source Action… menu). The inliner is also used internally for other refactorings like “Change signature” and “Remove unused parameter.” When you request a signature change, gopls often needs to update call sites to match the new parameter list. The inliner helps by safely rewriting those calls while preserving semantics. Because it handles edge cases like complex arguments and closures, these refactorings can be applied confidently. Thus, the inliner is a foundational building block that enables many higher-level automated code transformations in the Go ecosystem.

How does the inliner ensure correctness when transforming code?
Correctness is paramount for any automatic refactoring. The source-level inliner employs a set of careful checks to avoid introducing bugs. First, it analyzes the called function’s body for any potential conflicts with the caller’s scope—for example, if a local variable in the inlined body hides a variable in the caller, the inliner renames it. Second, it preserves evaluation order of arguments: if an argument is an expression with side effects (like a function call or increment), the inliner inserts a temporary variable to ensure it runs exactly once. Third, it respects Go’s statement and expression boundaries, adding parentheses where needed. Finally, the inliner works only on functions that are safe to inline—those without complex control flow like panic/recover or excessive size. These safeguards make the transformation reliable for both interactive use and batch commands like go fix.
What benefits do self-service modernizers bring to the Go community?
The term “self-service modernizers” refers to the ability for any library or package author to create automated migrations using the source-level inliner. Previously, only the Go team could add bespoke modernizers to go fix for new language or library features. With the inliner as a generic building block, package authors can now annotate their deprecated functions and let users run go fix to update their code. This scales maintenance efforts: a single library update can instantly propagate to all consumers without manual labor. It also encourages best practices by making it easy to migrate to newer APIs. The inliner is the first fruit of a broader effort to empower the community to create safe, automatic code transformations, reducing technical debt and keeping Go projects up to date.
Related Articles
- 6 Key Insights into Information-Driven Imaging System Design
- Stack vs Heap: Smarter Slice Allocation in Go
- How We Built a Conversational Ads Manager Using Claude Code Plugins and the Spotify Ads API
- Structured Prompt-Driven Development: A New Approach to AI-Assisted Software Engineering
- How to Streamline Your Development Workflow with GitHub Copilot's New Desktop App
- 10 Reasons IBM Bob Is Redefining Enterprise AI Development
- 7 Python Tricks to Flatten a List of Lists Like a Pro
- 7 Key Updates About the Python Insider Blog Migration