The open-swaggo docs described an API that didn't exist
A top-to-bottom accuracy pass on the open-swaggo docs — deleting a fictional CLI and fake config fields, fixing every example — plus the same LLM-friendly endpoints.
Last week I went through the go-migration docs line by line and made every example compile. This week I did the same for open-swaggo — and it was worse.
go-migration’s docs had drifted: right shape, wrong details. open-swaggo’s docs had drifted into fiction. Whole pages documented an aspirational API that was never built — a CLI that doesn’t exist, config fields that were never defined, a code generator for a language the package doesn’t support. A reader following the Getting Started guide couldn’t get a single endpoint to compile, because the functions in the example weren’t real.
So I rewrote the whole site against the actual Go source: Getting Started, Examples, Adapters, API Reference, Features, Guides, FAQ, Troubleshooting, and the landing page.
The examples used an API that was never built
The core failure was a docs site written against a design, not against the code. Here are the corrections that mattered most.
Creating and mounting docs
The old flow passed endpoints into the constructor and mounted through a package-level function. Neither signature exists.
// None of this compiles docs := openswag.New(cfg, endpoints...) openswag.Mount(mux, "/docs", docs)
// Endpoints are added; mounting is a method. docs := openswag.New(cfg) docs.AddAll(endpoints...) docs.Mount(mux, "/docs")
Responses are a map, not a slice
The status code is the map key — there was never a StatusCode field.
Responses: []openswag.Response{
{StatusCode: 200, Description: "OK", ContentType: "application/json"},
} // Status code is the map key — no StatusCode field. Responses: map[int]openswag.Response{ 200: {Description: "OK", Schema: UserResponse{}}, }
Title and Version live on Info
openswag.Config{
Title: "My API",
Version: "1.0.0",
UIConfig: ...,
} openswag.Config{
Info: openswag.Info{Title: "My API", Version: "1.0.0"},
UI: ...,
} Adapter argument order
The framework adapters take (router, docs, basePath), and MountGroup
doesn’t take a base path at all.
swaggin.Mount(r, "/docs", docs) swaggin.MountGroup(api, "/docs", docs)
// (router, docs, basePath); MountGroup takes no basePath. swaggin.Mount(r, docs, "/docs") swaggin.MountGroup(api, docs)
Authentication by scheme name
Security is a list of registered scheme names, not inline config structs.
Security: []auth.Scheme{
auth.BearerAuth(auth.BearerAuthConfig{...}),
} // Scheme is registered once; reference by name. Security: []string{openswag.SecurityBearerAuth} // docs.BuildSpec().AddSecurityScheme(...)
Deleting the fiction
Some of the old content didn’t describe a real thing at all, so it had to go rather than be corrected:
- A fake CLI.
open-swaggo init,open-swaggo gen, andgo install ...@latestall implied a command-line tool. open-swaggo is a library — installation isgo get, full stop. - PHP code snippets. The real snippet generator emits curl, JavaScript, Go, and Python. There is no PHP. Those examples were removed.
- A multi-tenant example built entirely on functions that don’t exist — rewritten against the real API.
- Fictional types and fields, including
Config.{Security, Console, Playground, Endpoints, BasePath},schema.Generate,ParseTags/TagInfo,examples.NewGenerator,snippets.SnippetConfig/LangPHP,auth.*Config{},auth.OAuth2(),WithCredentialStore, anddocs.AddEndpoint/GenerateSpec.
Calling things by their real names
A pile of functions were documented under names that were close, but wrong:
| Documented (wrong) | Actual API |
|---|---|
| schema.Generate(v) | schema.FromType(v) |
| docs.AddEndpoint(...) | docs.Add(...) |
| docs.GenerateSpec() / docs.Spec() | docs.SpecJSON() / docs.BuildSpec() |
| versioning.Differ{...}.Diff(...) | versioning.NewDiffer().Compare(...) |
| examples.NewFaker(seed) | examples.NewFaker() |
What the docs now actually cover
Cutting fiction left room to document the real capabilities that had never made the page:
Endpoint.QueryParamsandPathParams— structs withform/paramtags, the actual way you declare parameters.- The security constants —
openswag.SecurityBearerAuth,SecurityApiKey, and friends. - The real schema tags —
json,example,format,swagger,description. - The methods that exist —
SpecJSON(),BuildSpec(),GetUIConfig().
Docs for humans and machines
The other half of this update mirrors what go-migration got: the docs now speak to LLMs directly, instead of forcing a model to scrape rendered HTML and hallucinate the API (which, given how wrong the old text was, would have been catastrophic anyway).
/llms.txt— a compact index of every docs page, the convention agents look for first./llms-full.txt— the entire documentation as one clean Markdown file.- Raw Markdown per page — every page is reachable as its source, e.g.
/docs/getting-started/installation.mdx. - A “Copy as Markdown” button — copies the page as clean Markdown, with JSX components flattened.
- An “Open in LLM” dropdown — open the page straight in ChatGPT or Claude, plus a “View raw Markdown” option.
The flattening is the interesting bit. The pipeline runs remark with a custom
plugin that collapses MDX components — Tabs, Cards, and the rest — into
plain Markdown, so the output reads cleanly to a model instead of arriving as a
soup of component tags.
The principle is the same one from the go-migration write-up: the machine reads the same honest source you do. If the human-facing example compiles, so does the one an assistant copies — because they’re the same bytes.
Why I keep writing these
Two packages in, the pattern is clear enough to name: documentation is a promise, and an unverified promise quietly becomes a lie. A spec generator whose own docs can’t generate a spec is the most embarrassing version of that — and it was the version I was shipping until this week.
If you’re using open-swaggo and an example doesn’t compile, that’s now a bug. I’d like to hear about it on the repo.