What does a `// Deprecated:` comment on an exported Go function do, and what happens to code that still calls it?
answer
- a comment, not a keyword
- its own paragraph in the doc comment
- importers compile exactly as before
- one word, a colon, a replacement
- removal is the breaking part
basics
~20 sA paragraph beginning "Deprecated:" in an identifier's doc comment marks it as no longer recommended. Nothing changes at compile time: existing calls still build and run. Documentation tools and editors surface the notice and the replacement it names.
solid answer
~40 sGo has no deprecation keyword, annotation or compiler directive — it is a doc-comment convention. You add a separate paragraph to the doc comment of a function, type, method, field, variable or whole package, beginning with `Deprecated:` and saying what to use instead. `go doc` shows it, editors and analysis tools surface it, and the compiler ignores it completely: every importer keeps building. That is the point. Marking a symbol deprecated is an additive, non-breaking change, so you can steer callers off a bad API without breaking a single downstream build; *removing* the symbol is the breaking change, and it happens later. In a library other teams pin, a useful notice names the replacement, gives the reason, and states the release in which the symbol actually disappears.
code
go · 7 lines// Dial connects to addr and returns a Client ready for use.
//
// Deprecated: use DialContext, which honours cancellation.
// Dial will be removed in the v1.9 release.
func Dial(addr string) (*Client, error) {
return DialContext(context.Background(), addr)
}go deeper
Be ready to write the marker correctly from memory: a separate paragraph at the end of the doc comment, starting with Deprecated: and naming what to use instead.
Explain that the compiler ignores it entirely. It is a documentation convention that tools and editors read, which is exactly why marking is additive and removing is not.
Show how you use it in a shipped library: add the replacement first, reimplement the old path on top of it, mark, and track who still calls it before you delete anything.
Own the removal policy. A deprecation with no stated release and no owner is permanent surface you keep paying for, so treat the deadline as part of shipping the marker.
## The mechanism Go deliberately has no `@Deprecated` annotation, no `[[deprecated]]` attribute and no compiler flag for this. Deprecation is a **documentation convention**, recognised by the toolchain's documentation and analysis tools and by editors, and completely invisible to the compiler. The rule is: add a paragraph to the identifier's doc comment that begins with `Deprecated:` followed by text explaining what to use instead. A paragraph means it is separated from the rest of the comment by a blank comment line, and it conventionally goes last: ```go // Dial connects to addr and returns a Client ready for use. // // Deprecated: use DialContext, which honours cancellation. // Dial will be removed in the v1.9 release. func Dial(addr string) (*Client, error) { ... } ``` It works on any documented item: a function, a method, a type, a struct field, a constant, a package-level variable, and a whole package (put the paragraph in the package doc comment, above the `package` clause). ## What it does and does not do It does **not** produce a compiler warning, does not fail `go build`, and is not enforced by `go vet`. A downstream module that upgrades to the tag introducing the marker sees exactly the same build it saw before. What changes is the *documentation*: `go doc` renders the notice, package documentation sites strike the symbol through and hide it from the default listing, and editors typically show the call with a strike-through and surface the text on hover. Static-analysis setups can be configured to report calls to deprecated symbols, but that is a choice a consuming team makes, not something Go imposes. ## Why this is the central tool for evolving a package When other teams import and pin your library, the only changes you can make freely are **additive** ones — things that cannot stop an existing importer from compiling. Adding a function, adding a type, adding a method to your own struct, adding an option: safe. Removing anything exported, renaming it, or changing its signature: a break, and every importer's build fails the moment they upgrade. The deprecation marker is what lets you make progress inside that constraint. The sequence is: 1. Add the replacement (`DialContext`), which is purely additive. 2. Reimplement the old symbol in terms of the new one, so behaviour cannot drift between the two paths. 3. Mark the old symbol `Deprecated:` and name the replacement in the text. 4. Leave both working for a stated window. 5. Remove the old symbol only in a release where a break is allowed. Step 2 matters more than people expect: a deprecated function that keeps its own copy of the logic is a second implementation you now have to fix bugs in twice. ## Writing a notice that actually works A notice that says only `Deprecated: do not use` is close to useless. Three things make the difference: - **A named replacement.** The reader is at the call site, mid-task; give them the exact symbol to switch to, and if the migration is not a rename, one line about the difference. - **A reason.** "Does not honour cancellation", "is not safe for concurrent use", "cannot report partial failures". This is what convinces someone to spend the afternoon. - **A removal plan.** A version or a date. Without one, the symbol never goes: nobody migrates under no deadline, and the maintainer keeps paying for two code paths indefinitely. A deprecation with no owner and no removal release is simply permanent surface area with a rude comment attached. ## Common misunderstandings - *"It breaks callers."* It cannot. It is a comment. - *"There's a `//go:deprecated` directive."* There is not. Compiler directives in Go are things like `//go:embed` and `//go:noinline`; deprecation is not one of them. - *"The toolchain removes it eventually."* Nothing is automated. Removal is a human decision and a breaking change. - *"Put it anywhere in the comment."* Tools look for a paragraph that *begins* with `Deprecated:`; burying the word mid-sentence means nothing renders it as a deprecation.
- Can you deprecate a whole Go package rather than a single symbol?Yes. Put the `Deprecated:` paragraph in the package doc comment — the comment block immediately above the `package` clause, conventionally in a doc.go file. Documentation tooling then marks the entire package, which is how you retire a package you cannot delete yet. The import still compiles; only the documentation changes.
- Does the Go compiler or go vet fail a build that calls a deprecated function?No. Neither the compiler nor `go vet` treats deprecation as an error or a warning; the build is byte-for-byte the same decision it was before the marker existed. Enforcement, if a consuming team wants it, comes from analysis tooling they choose to run in their own CI, not from the Go toolchain.
- Besides the word itself, what should a deprecation notice contain?Three things: the exact replacement symbol, a one-line reason the old one is wrong, and the release in which it will be removed. The replacement makes the migration mechanical, the reason motivates it, and the removal version creates the deadline. Without a stated removal, deprecated symbols live forever and you maintain two code paths indefinitely.
saying these in an interview costs you the question
- Claims Go has a @Deprecated annotation or compiler attribute
- Thinks marking a symbol deprecated breaks importers' builds
- Puts the Deprecated: text inside the function body
- Invents a //go:deprecated compiler directive
- Deprecates without naming a replacement or a removal release