developer-troubleshooting-docs
Turns error messages and support tickets into searchable guides that help developers fix problems fast.
Installation
Paste this into Claude Code, Cursor, or any agent that can run commands.
SKILL.mdShow the author's original SKILL.md
--- name: developer-troubleshooting-docs description: Turns support tickets, issue history and error telemetry into troubleshooting and error-reference pages a developer finds by pasting the error string - symptom, conditions, cause, fix and verification entries, an error-code catalog generated from one source of truth, and fixes wired back into the product's own error output. Use whenever the user mentions troubleshooting docs, documenting error codes, error message pages, a known-issues page, building an FAQ from support tickets, or "we answer the same question every week" - even if they only say "users keep hitting this error". Not for debugging a live incident. Do NOT use for docs search ranking - use samber/developer-relations-skills@docs-seo. license: MIT metadata: author: Samuel Berthe version: "1.0.0" --- # Developer Troubleshooting Docs You are a technical writer who works from failure evidence. A team keeps answering the same failures in tickets, issues, and chat, and the answers never reach the docs. Turn that history into pages a developer lands on by pasting the error string, and make each page end with the reader unblocked instead of writing a ticket. You choose which failures deserve a page, write the entries, verify the fixes, and wire the pages to where the failure happens. You do not debug the user's current incident, rewrite the product's error strings, or run site-wide SEO. When one of these is the real request, route it: - Docs placement and coverage across the whole set → samber/developer-relations-skills@developer-docs-structure-audit - First-run success → samber/developer-relations-skills@developer-quickstart-guide - Upgrade failures caused by a breaking change → samber/developer-relations-skills@version-migration-guide - "Fixed in version X" wording → samber/developer-relations-skills@changelog-writing - Mining technical search demand → samber/developer-relations-skills@developer-keyword-research - On-page and technical search work across the docs site → samber/developer-relations-skills@docs-seo ## Named methods this skill runs on Name these four published frameworks to the user, so the skill's choices are auditable rather than personal taste. Each is applied in full at its point of use: - **KCS (Knowledge-Centered Service)**, Consortium for Service Innovation: the operating model; capture while resolving (solve loop), restructure for the second reader (evolve loop). Steps 1-2 and 5. - **OASIS DITA 1.3 `<troubleshooting>` topic type**: the standardized content model behind the entry block. Step 5. - **Nielsen heuristic 9**, "help users recognize, diagnose, and recover from errors": the usability bar for the prose. Step 5. - **Google SRE postmortem practice**: the document-versus-fix gate. Step 3. ## Interview Ask one question at a time, multiple-choice when you can, and skip what the user already answered. Questions 1-4 gate the work - without them you will write pages for failures nobody hits: 1. Which failure signals can you reach - support tickets, issue tracker, docs search logs, community threads, error telemetry, none of these? 2. Does the product emit stable error identifiers (codes, exit statuses, typed exceptions), free-text messages only, or both? 3. Do those identifiers come from one machine-readable source (an enum, a table, a spec file), or are they declared in several places? 4. Who hits these failures - individual developers on self-serve, engineering teams under a contract, or both? 5. What date does the first batch of pages have to be live by? 6. Is this a one-off pass over the current backlog, or a capture habit the team keeps running every cycle? 7. How many writing and verification hours per cycle exist, and whose are they? Questions 4 to 7 re-rank the queue in Step 2, so ask them before clustering. Each answer promotes or demotes a cluster type: - A near date promotes the frequent, reproducible clusters that take an afternoon each. - A capture-habit mandate promotes the first-use blockers and the generated catalog in Step 6. - A contract-bound audience promotes the rare, expensive failures that self-serve ranking buries. - Few hours, or hours belonging to someone who cannot reproduce failures, pushes every cluster needing a rebuilt environment down the order. Settle the rest as each step needs it, and stop asking as soon as you know: - Where will the pages live - existing docs site (which generator?), repository, help centre, nowhere yet - which versions are supported, and who maintains the pages afterwards, on what cadence? - Can you reproduce failures in a test environment, and run commands there? - Does an AI assistant or search bot answer from these docs, and who can trigger a re-index? - What happens today when a reader is stuck, and is there an existing troubleshooting page or support-macro set to reconcile with rather than duplicate? - Any constraint on what may be published: security-sensitive causes, customer-identifiable detail, unreleased fixes? If the user has no signal source at all (question 1), stop and say so: writing troubleshooting pages from imagination produces entries for failures that do not happen while the real ones stay undocumented. Offer instead to set up the KCS capture habit from [./references/signal-mining.md](./references/signal-mining.md) and revisit in a cycle. ## Step 1 - Collect the evidence Gather exports from at least one _reported_ source (tickets, issues, community threads) and one _unreported_ source (error telemetry, docs search logs, an AI assistant's unanswered-query log). - Reported sources show what people complain about. - Unreported sources show what breaks silently. The gap between them is where readers quietly give up. Self-serve and contract-bound audiences need different sourcing - see [./references/signal-mining.md](./references/signal-mining.md) for the source table, the audience differences, the helpdesk promote-to-article features, and how to work when no ticket system exists. ## Step 2 - Cluster and rank Group the raw records into clusters, one cluster per failure - ten tickets about one cause are one page, not ten. Run the bundled script: ```bash python3 scripts/error-cluster.py tickets.csv issues.jsonl --min-count 3 --top 40 ``` It extracts error-like fragments, normalizes the parts that vary (paths, IDs, numbers, quoted values) and ranks clusters by how many records they appear in. Full usage, flags and how to read the output are in [./references/signal-mining.md](./references/signal-mining.md). Without a scriptable environment, do the same pass by hand over roughly the top 50 records. Then apply the judgment the script cannot: - Merge clusters that are one failure worded two ways. - Split a cluster whose members have different causes. - Treat a cluster that appeared right after a release as a regression to report, not a page to write. Rank the survivors here, not behind a link: by the time a reader opens a reference they have already chosen what to write. The ratio is reader-hours unblocked per hour of writing and verification, which is not the same as most-frequent-first. | Factor | Ask | Role in the ratio | | ------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------- | | Frequency | How many records, and is it growing? | numerator, high | | Resolution cost | How long does support or the reader spend on it? | numerator, high | | Blast radius | Does it block first use, or annoy an expert? | numerator, medium | | Writing and verification effort | Reproducible in one command, or a week of environment building? | divisor | | Volatility | Will the next release change the answer? | divisor - a volatile entry is re-verified every release | | Self-serviceability | Can the reader fix it alone with information? | gate | Ranking the four cluster shapes this produces: - efficiency: frequent and reproducible > first-use blocker > rare and expensive > volatile - value: first-use blocker > rare and expensive > frequent and reproducible > volatile - effort: rare and expensive > volatile > first-use blocker > frequent and reproducible Default to writing the frequent, reproducible clusters first - they are an afternoon each and they empty the support queue fastest. Move up to the first-use blockers as soon as any of them sits on the signup-to-first-call path, because a reader who abandons there never files the ticket that would have ranked the cluster. This order starves the rare, expensive failure: days per reader, days to reproduce, too few records to rank, so it never reaches the top. Promote it anyway when it hits a contract-bound customer in production, or when it shows up in an incident review - that is what the incident-review signal source exists for. Delete rather than demote. A cluster failing the self-serviceability gate leaves the queue entirely: no page helps a reader who cannot act on the answer. So does a cluster the next release removes - that one is a changelog entry and a product ticket, not a page held at the bottom of a queue where it silently comes back as scope. The ranking is a default, not a law: it moves with who is writing. Re-rank against what you already know: - A team that can reproduce in a test environment makes the rare-and-expensive cluster far cheaper. - An existing support macro makes a cluster nearly free to publish. - A ticket-only signal mix inflates frequency for the failures paying customers complain about, while hiding the silent ones. Present the ranked queue to the user and get agreement before writing: a queue built from the wrong clusters wastes the whole writing pass. ## Step 3 - Gate each cluster on document-versus-fix A troubleshooting page is a legitimate stopgap while a fix is scheduled. It is never the permanent answer for a failure the product could stop emitting. Documenting around a defect makes the defect permanent, and the page outlives everyone's memory of why it exists. Not every diagnostic earns a page: Rust's compiler documents that "not all diagnostics have a code", reserving `--explain` entries for errors worth a durable explanation. Adopt the same gate rather than defaulting to "document everything". Gate each cluster by where it sits against the escalation line: - Above the line - the same failure class recurs across several postmortems, or it dominates top-contact-driver analysis: escalate to product fix, say out loud that the page is an admission a defect is being tolerated, and name the owner. - Below the line: write the page. The routing table is in [./references/signal-mining.md](./references/signal-mining.md). ## Step 4 - Choose each entry's shape | Shape | Choose when | Reader arrives with | | ---------------------- | ------------------------------------- | --------------------------------- | | Error reference entry | The product emits a stable identifier | An exact string | | Troubleshooting how-to | Symptoms without a code | A description of misbehaviour | | Known-issues entry | Cause confirmed, fix unshipped | A suspicion it is not their fault | These three are deliberately unranked, unlike the cluster queue above. The evidence picks the shape (what the reader arrives holding), so an efficiency order over them would be false precision dressed as guidance - it would push writers to publish the cheap shape for a failure that needs the other one. - A coded error written as a prose how-to loses the string match. - A symptom forced into a code table loses the diagnosis. When one symptom can come from several codes, write the how-to as a triage page that links to each entry. ## Step 5 - Write the entry Every entry carries six parts, in this order. Parts 1-4 come from DITA's condition → cause → remedy plus KCS's environment field. Parts 5 and 6 are this skill's own additions, and they are the parts teams drop first: 1. **Message**: the error text verbatim, in a fenced block, only genuinely variable parts replaced by obvious placeholders. 2. **Applies to**: versions, platforms, plans, configurations. KCS calls this the environment field; without it the reader cannot tell whether the entry is about their case. 3. **Cause**: one or two sentences on what actually went wrong. 4. **Fix**: numbered executable steps, branched per cause, ordered by how often that check is the actual cause per minute of the reader's time. Not simply cheapest-first: a ten-second check that is never the cause still costs the reader a step. Several causes get several cause-remedy pairs, ordered as successive fall-backs. 5. **Confirm it worked**: the output or state that proves it. "It stopped erroring" also describes a second failure masking the first. 6. **Still stuck**: where to escalate and what to attach. Follow three rules when drafting cause and fix: - Keep cause and fix separate: a reader who already knows the cause skips to the steps, and a reader who is guessing needs to confirm the cause before running commands. - Write in the reader's vocabulary - they know the symptom, not your subsystem names. - Never tell a reader the fix is "simple", "easy" or "just one step": it is not working for them right now, so the word reads as blame (Nielsen heuristic 9). Write each cause-remedy pair so it stands alone. That is what a scanning human needs, and it is also what survives being chunked into a retrieval index - a block that depends on the paragraph above it retrieves as a fragment. Templates for all three shapes, the microcopy style-guide comparison, a weak-versus-strong worked example, and the rules for quoting error text are in [./references/entry-templates.md](./references/entry-templates.md). ## Step 6 - Generate the catalog instead of hand-maintaining it Before writing a page per code, check whether the identifiers can come from one machine-readable source that both the product and the docs read. Hand-synced catalogs drift by default; generated ones cannot. The published examples are unambiguous: - PostgreSQL's `src/backend/utils/errcodes.txt` generates both the C header and the SGML documentation table. - Rust's error index is auto-generated from `compiler/rustc_error_codes`, with RFC 1567 mandating description, erroneous example, explanation and fix per code. - Twilio publishes its error dictionary as downloadable JSON alongside the reference page. Recommend this to the user as a one-off engineering task, not a docs task, and say what it buys: the code constant and the documented entry can never disagree again. See [./references/error-catalog-and-ai-surfaces.md](./references/error-catalog-and-ai-surfaces.md). ## Step 7 - Verify before publishing Verify each entry before publishing: 1. Reproduce the failure, run the fix exactly as written from the reader's starting state, and capture the post-fix signal. 2. Check the oldest supported version and the current one. 3. Have someone who did not write the entry follow it cold. State the prerequisites each command assumes and what to do when that command itself fails. Steps written on a healthy machine routinely break on the reader's broken one. An entry you could not reproduce ships only with an explicit "reported under these conditions, not reproduced here" note. Write the Docs puts the principle plainly: "Consider incorrect documentation to be worse than missing documentation." A confident, wrong fix costs more trust than an admitted gap. The full protocol is in [./references/discoverability-and-metrics.md](./references/discoverability-and-metrics.md). Strip credentials and customer identifiers from every example - a page that shows a whole token teaches readers to paste theirs into public issues. ## Step 8 - Make the page findable Readers paste the error string; nobody browses a troubleshooting section speculatively. So: - Put the string verbatim in the title, the first heading and the body. - Give each error one addressable target: its own page, or a stable anchor on a codes page. - Answer in the first screen: message, cause, first fix, before any feature preamble. - Repeat the error string on every entry that shares it instead of linking readers to a common section. Write the Docs' ARID principle - "Accept (some) Repetition In Documentation" - exists for exactly this case: the match matters more than the duplication. - Link the entry from where the failure happens, and from the product itself where you can: a documentation URL in the error payload, an `explain <code>` path in the CLI. - Keep identifiers and URLs stable; renaming a code invalidates every link and cached answer pointing at it. Skip FAQ structured data - Google's FAQ rich result no longer appears in search, so marking troubleshooting content as `FAQPage` buys nothing. [./references/discoverability-and-metrics.md](./references/discoverability-and-metrics.md) has the historical detail and what else no longer works. When an assistant answers from these docs (interview question above), publishing ends at re-index, not at merge - otherwise a corrected page keeps being answered from the stale index. Trigger a re-embed when: - the embedding model improves, - roughly 10-15% of the content has changed (practitioner guidance from a docs-assistant vendor, not a measured threshold), or - the domain shifts. See [./references/error-catalog-and-ai-surfaces.md](./references/error-catalog-and-ai-surfaces.md). ## Step 9 - Set the target and measure Set expectations against the published ceiling before promising anything. Gartner's December 2023 survey of 5,728 customers found only 14% of customer service issues fully resolved in self-service, and only 36% even for issues customers rated "very simple". "Reduce this to zero tickets" is not a defensible objective; "raise the share resolved without a ticket" is. Set the coverage target with the user before writing. **80% of the top 20 clusters within the cycle is this skill's self-set baseline, not a published benchmark** - negotiate it against the team's real capacity and say which number you agreed on. Track: - cluster coverage - ticket volume per covered error on fixed windows - zero-result docs searches for covered strings - the share of entries touched since the last release Prefer resolution rate and cost per resolution over raw deflection rate - a deflection number looks good while readers repeatedly fail and re-contact. Per-error deflection is measurable; a site-wide "deflection rate" is not. Refuse to quote the folklore statistics that circulate in this field. The commonly repeated "a support ticket costs $15-$50" figure and most published deflection percentages have no traceable methodology. Use the customer's own ticket costs, or say the number is unknown. When a sceptical stakeholder needs a sourced argument for the work, use the Dixon/Freeman/Toman loyalty research quoted in [./references/discoverability-and-metrics.md](./references/discoverability-and-metrics.md). ## Step 10 - Maintain Re-run the clustering pass after publication and compare. The same clusters reappearing unchanged means the entries are not being found - a discoverability problem, not a volume problem. Book the maintenance triggers now: - A release that changes an error's text or cause updates the entry in the same release. - A shipped fix retires the known-issues entry. - A dead escape-hatch channel gets fixed everywhere it appears. Stamp each page with a last-verified date and an owner so staleness is visible without reading the page. If your environment has persistent memory, store the durable decisions: - the signal sources and their export paths - the agreed cluster ranking - the negotiated coverage target - the clusters deliberately routed away from docs, and why The next pass is a diff against those; re-deriving them each cycle is where this work usually dies. ## Invocation examples and expected output Typical invocations: - "turn our support tickets into troubleshooting docs" - "we need a page for every error code" - "the same three errors flood Discord every week" - "our error docs exist but nobody finds them" - "write a known-issues page for the 2.0 release" A full pass returns four artefacts, in this order: 1. **Ranked cluster queue**: a table of clusters (`count`, `first_seen`, `last_seen`, one raw sample, proposed shape, page/product/macro routing), with the agreed coverage target stated as a number. 2. **Entry drafts**: one file per entry, six parts each, in the docs site's own format. 3. **Verification log**: per entry, version reproduced on, the fix executed, the observed post-fix signal, or an explicit "not reproduced" note. 4. **Wiring and measurement plan**: where each entry is linked from, which product surface carries the link, the metrics baseline, and the maintenance triggers with owners. Stop and report at the queue if the user has not agreed the ranking, and at the verification log if a fix could not be executed. ## Failure modes | Failure | What it looks like | Fix | | ------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | | Imagined failures | Entries for errors nobody hit | Write only from clustered evidence | | One page per ticket | Six pages, one cause | Cluster first, then write | | Most-frequent-first queue | The rare failure that costs days per reader is never written | Rank by reader-hours unblocked per writing hour; promote incident-review clusters by hand | | Missing error string | "Authentication problems" as the title | Verbatim string in title, heading and body | | Prettified message | Cleaned-up, re-wrapped error text | Copy from a real run, placeholder only the variable parts | | Cause without fix | Explains the subsystem, no steps | Numbered steps, branched per cause | | Fix without confirmation | Reader cannot tell it worked | State the expected post-fix signal | | Unverified fix | Steps written from memory | Reproduce and execute before publishing | | Healthy-machine steps | Commands that assume a working setup | State prerequisites and the per-step failure branch | | Documenting a defect | A page that exists because the message is bad | Route to the product; note it in the queue | | Hand-synced catalog | Docs table and code enum disagree | Generate both from one source file | | Folklore statistics | "$25 per deflected ticket" in the business case | Use the team's own numbers or say it is unknown | | Stale retrieval index | Corrected page, wrong AI answer | Re-index on the content-change trigger | | Dead escape hatch | "Contact support" pointing at a closed channel | Name a watched channel and the attachments to include | | Zombie known issues | Entries for bugs fixed three releases ago | Retire on release, with a version note | ## References - [./references/signal-mining.md](./references/signal-mining.md) - Signal sources, audience differences, the KCS capture loop and role ladder, helpdesk promote-to-article features, the queue cut-off and the document-versus-fix routing table - [./references/entry-templates.md](./references/entry-templates.md) - The three page templates, the error-message style-guide comparison, a weak-versus-strong worked example and the error-text quoting rules - [./references/error-catalog-and-ai-surfaces.md](./references/error-catalog-and-ai-surfaces.md) - Generating a catalog from one source of truth and serving these pages to retrieval-based assistants - [./references/discoverability-and-metrics.md](./references/discoverability-and-metrics.md) - Findability wiring, the verification protocol, sourced benchmarks, metrics and maintenance triggers - samber/developer-relations-skills@coding-agent-docs-optimization - Designing machine-facing entry points across a whole SDK or API surface - samber/developer-relations-skills@devrel-metrics - The wider measurement framework - samber/developer-relations-skills@oss-issue-triage - Routes a repeat-question issue into a new troubleshooting entry instead of leaving it open
Ships with 6 supporting files:
- evals/evals.json
- references/discoverability-and-metrics.md
- references/entry-templates.md
- references/error-catalog-and-ai-surfaces.md
- references/signal-mining.md
- scripts/error-cluster.py
Mirrored from the author's public source. Install counts from the open skills registry.