SKILL DETAIL
engineering-blog-post
samber/developer-relations-skills/engineering-blog-post
Writes or edits a technical blog post a skeptical developer audience will believe - the right post pattern, evidence behind every claim, published trade-offs and limitations, runnable snippets, and no marketing voice. Use whenever the user wants an engineering blog post, a technical article, a "how we built it" or "we rewrote it in X" story, a debugging write-up, a public postmortem, a benchmark post, or a product post that must not read as marketing, and when they ask to review, edit, de-jargon or de-market an existing technical draft - even if they only call it "the article". Not a teaching tutorial - use samber/developer-relations-skills@developer-tutorial. Not release notes, a migration guide, a case study or a talk.
Installation
npx skills add https://github.com/samber/developer-relations-skills --skill engineering-blog-post
スキルファイル
SKILL.md
最終同期 · 2026/10/23
evals/evals.json›
{
"skill_name": "engineering-blog-post",
"evals": [
{
"id": 1,
"prompt": "The draft is too long to paste so here is the summary. One of our staff engineers wrote a 2,100-word post titled \"How Kestrel Cut Our Index Build Time\" for the Kestrel Data engineering blog. It goes live Thursday. The intro says \"Kestrel is the fastest columnar index builder available today\". The results section says \"index builds are 12x faster\" next to a bar chart and nothing else. Paragraph four says \"thousands of teams rely on Kestrel in production\". There is a 40-line Python snippet with no imports that calls kestrel.build_index(), and that method was renamed in 2.0. The closing paragraph says \"we're thrilled to share this journey with the community\". There is no section about where any of this does not help. Please get it into shape.",
"expected_output": "An edit-mode response: a three-tier fix list ordered by trust damage, each item quoting the offending line, plus a credibility-gate report. No wholesale rewrite of the post.",
"files": [],
"expectations": [
"States that it is working in edit mode because a draft already exists, and says so before starting work",
"Does not return a full rewritten version of the post",
"Returns a fix list grouped into three tiers labelled blocking, strong, and optional",
"Orders the fix-list items by trust damage rather than by their position in the document",
"Quotes the specific offending line for each item instead of paraphrasing it",
"Flags \"the fastest columnar index builder available today\" as an unsupported superlative that must be backed by a measurement in the post or cut",
"Flags \"12x faster\" as a ratio with no methodology and no absolute values, and requires a disclosure block or removal of the claim",
"Flags \"thousands of teams rely on Kestrel in production\" as an adoption claim with no source, to be cut or replaced with a figure the author can point at",
"Flags the snippet for missing imports, no pinned version, and the renamed kestrel.build_index() call that would fail for a reader who copies it",
"Flags the absent limitations section and asks for concrete boundaries a reader can check against their own system",
"Reports the draft against the six credibility-gate checks and names which ones fail",
"States that only the passages the user approves will be rewritten",
"Gives preserving the author's voice as the reason for not rewriting the whole draft"
]
},
{
"id": 2,
"prompt": "Halcyon Systems here. We moved our billing reconciliation service off Ruby onto Go over about four months. p99 went from 840ms to 95ms and the EC2 bill for that service dropped roughly 60%. Honestly the migration went smoothly, nothing really broke, the team knew what they were doing. Write the blog post. Audience is backend engineers evaluating Go.",
"expected_output": "Pattern named as Rewrote It in X, with the post blocked until the \"what went wrong\" material exists, plus questions about measurement parity and what actually caused the gain.",
"files": [],
"expectations": [
"Identifies the material as the Rewrote It in X pattern",
"States that the section carrying this pattern's credibility is \"what went wrong\"",
"Declines to produce a finished post until the \"what went wrong\" material exists, rather than drafting around its absence",
"Asks specifically for regressions, surprises, or costs nobody budgeted during the migration",
"States that a migration post without that section reads as a press release",
"Asks whether the before and after latency figures were produced by the same measurement method on both sides",
"Raises that part of the improvement may come from reimplementing code the team understood better the second time rather than from Go itself",
"Requires the alternatives that were considered and why they lost",
"Requires a \"would we do it again, and under what conditions\" section",
"Assigns the 840ms, 95ms and 60% figures to the measured evidence class and asks how those runs were produced",
"Does not present the migration decision as objectively correct independent of Halcyon's constraints",
"States the mode and the chosen pattern before doing any other work"
]
},
{
"id": 3,
"prompt": "Vantage Metrics. We benchmarked our time-series engine against Northlake TSDB, our main competitor. On our internal test we came out 3x faster on ingest. Setup was: ours running the tuning profile we use in production, Northlake on its install defaults because that is what most people actually run. One run each, m6i.2xlarge. I want this on the blog next week under the headline \"3x faster ingest than Northlake\".",
"expected_output": "The post classified as benchmarketing, the one-sided tuning and single run refused, and a full disclosure block required before the results.",
"files": [],
"expectations": [
"Classifies the post as benchmarketing because it compares the author's own product against a competitor, and states that class owes the most disclosure",
"States that benchmark readers start from disbelief, so the post is guilty until proven innocent",
"Requires the methodology to appear before the results rather than in an appendix",
"Names tuning only your own side as the specific move that gets a benchmark post publicly dismantled",
"Requires either re-running with both sides tuned fairly, or stating which side is untuned and labelling the result indicative rather than conclusive",
"Rejects a single run per side and requires multiple runs with their variance reported",
"Requires absolute ingest numbers alongside the 3x ratio",
"Requires the disclosure to name the hardware or instance type",
"Requires the disclosure to name the version of both systems compared",
"Requires the disclosure to describe the workload and the dataset",
"Requires the disclosure to state the configuration and tuning applied to each side",
"Requires a link to the benchmark scripts so a reader can reproduce the run",
"Requires at least one chart rather than a table alone",
"Flags the extra compliance cost of naming a competitor, such as a legal read and the difficulty of withdrawing the claim once it is quoted"
]
},
{
"id": 4,
"prompt": "Pasting a condensed version of our internal postmortem for the Aug 3 outage: \"PROJ-8812 - Falcon (our routing tier) dropped 34% of writes for 51 minutes after mreynolds deployed change #4471 at 14:02. Root cause: a config reload path that re-read the wrong key. 118 customers affected including two enterprise accounts, estimated 1.2M failed requests. Wiki: go/falcon-postmortem-aug3.\" We want a public version up this week, users are asking. Just adapt this one.",
"expected_output": "A derived public post, not a copy: a redaction pass run first, impact figures treated as a deliberate decision, Bug Hunt pattern, and a reason for an unaffected reader to care.",
"files": [],
"expectations": [
"States that the public post is derived from the internal review and never a copy of it",
"Runs a redaction pass before drafting anything",
"Removes the individual name \"mreynolds\" and any handles",
"Removes the internal service codename \"Falcon\"",
"Removes the ticket identifiers PROJ-8812 and #4471",
"Removes the internal wiki link go/falcon-postmortem-aug3",
"Treats the 118 customers and 1.2M failed requests figures as a deliberate publish-or-not decision rather than carrying them across automatically",
"Keeps blameless framing by explaining the conditions that allowed the failure instead of who deployed the change",
"Adds a reason for a reader who was not affected to keep reading, which the internal document never has to supply",
"Selects the Bug Hunt pattern",
"States that the wrong turns and dead ends of the investigation are the value of this pattern, and asks for them",
"Requires the post to end on a lesson that generalises beyond this bug rather than on the fix",
"Requires a security and customer-confidentiality review before publishing"
]
},
{
"id": 5,
"prompt": "I have been doing OAuth integrations for eight years and I know the spec cold at this point. I want a post for our engineering blog explaining how the authorization code flow with PKCE works. Can you write it?",
"expected_output": "The topic tested against the filters, judged to pass none, and the request redirected to finding an angle rather than producing a PKCE explainer.",
"files": [],
"expectations": [
"Tests the topic against the topic filters before drafting and names them",
"States that the topic as given passes none of the filters",
"Names the principle of writing from what was hard rather than from what is already mastered",
"Predicts that a post written from mastery will be accurate and unread",
"Does not deliver a finished explainer of the authorization code flow with PKCE",
"Asks what surprised, confused or cost the author time across those eight years in order to find an angle",
"Names at least one specific filter a reworked angle could pass, such as counter-intuitive, counter-narrative, surprising capability, elegant articulation or recognition",
"Requires a misconceptions section covering what people get wrong and why the wrong model is tempting, if the Explainer pattern is kept",
"Warns against opening the post with a definition",
"Requires every concept the post leans on to be either a stated prerequisite or introduced earlier in the post"
]
},
{
"id": 6,
"prompt": "Riverbend Cloud. We just shipped a new autoscaler that reacts to queue depth instead of CPU. Marketing wants a post on the engineering blog. We are product-led, developers sign up with a card and there is basically no sales team. Draft it. Title they suggested is \"Introducing Riverbend Autoscaling 2.0\".",
"expected_output": "A Non-markety Product Perspectives post: engineering problem first, mechanism-heavy, commercial interest stated early, a PLG call to action, and the announcement title rejected.",
"files": [],
"expectations": [
"Selects the Non-markety Product Perspectives pattern",
"Leads with the engineering problem stated in terms an engineer outside Riverbend also has, rather than with the announcement",
"Rejects or reworks the suggested announcement-style title",
"Keeps \"who we are\" to one plain sentence about what the company builds and sells",
"Gives the largest share of the post to how the mechanism actually works rather than to a feature list",
"Includes a section on what the design cost: trade-offs accepted, work abandoned, and the parts still unfinished",
"Applies the test that deleting every product name must still leave something behind, and says so",
"States the commercial interest once, plainly and early in the post, not in a footer",
"Optimises the call to action for trying it immediately rather than contacting sales, because the audience is product-led",
"Attributes the post to named engineers rather than publishing it unsigned",
"Asks what an engineer who will never buy Riverbend takes home from the post",
"Notes that a marketing sign-off requirement raises this pattern's effort and lowers its rank",
"Includes a limitation naming where queue-depth-based autoscaling is the wrong choice"
]
},
{
"id": 7,
"prompt": "Our keynote at a big cloud conference is in nine days and I want something live on the blog that morning. What I have: (a) a timeline from an outage last month that we already wrote up internally, (b) a fairly strong opinion that eBPF is overhyped for most teams, (c) an idea to benchmark our agent against the three competitors, which we have never run and would need a harness built for. I have maybe six writing hours total. Which one?",
"expected_output": "The benchmark struck outright for the effort ceiling, the ranking of survivors stated out loud, and the already-written incident material recommended.",
"files": [],
"expectations": [
"Strikes the competitor benchmark from the menu outright rather than ranking it last, because the effort ceiling excludes producing new evidence",
"Says the strikes out loud instead of silently omitting the struck option",
"States the ranking of the surviving options explicitly rather than implying it by order",
"Names the hard date as the answer that promotes Bug Hunt and Thoughts on Trends",
"Recommends the outage write-up, as a Bug Hunt, as the highest surviving option",
"Gives the reason that the internal incident review already exists, which makes this post near-zero effort",
"Notes that Thoughts on Trends rests on the weakest evidence class and buys a reach spike with a short shelf life",
"Requires a steelman of the opposing view if the eBPF opinion is written",
"Declines to combine two of the three into one post, and recommends publishing separately and linking them",
"Notes that a competitor benchmark carries a legal or compliance review the other two options do not",
"Names the conditions under which the benchmark would be worth promoting anyway",
"Does not recommend the benchmark for the nine-day deadline"
]
},
{
"id": 8,
"prompt": "Post is written and a colleague has already checked it for technical accuracy. It has three code blocks, two tables of benchmark output, and some raw command output pasted in. Before it goes live I want the prose tightened so it does not read like an LLM wrote it, and the SEO side sorted out: title tag, meta description, slug, alt text. We publish through Contentful. What is left to do?",
"expected_output": "The six-check gate run to completion, a humanizer pass scoped to prose only with the protected elements listed, the CMS preview placed before metadata, and a handoff list.",
"files": [],
"expectations": [
"Runs the six credibility-gate checks before handing the post over and reports the result of each",
"Iterates until every check passes rather than reporting failures and stopping",
"Scopes the humanizer pass to the prose only",
"Preserves the code blocks unchanged through the humanizer pass",
"Preserves the pasted command output unchanged",
"Preserves the benchmark tables unchanged",
"Preserves version numbers unchanged",
"Preserves the title unchanged",
"Previews the post in the publishing system before the metadata step, because code blocks and tables break in the CMS rather than in the draft",
"Places the metadata work (title tag, meta description, slug, link targets, alt text, tags) after that preview",
"Returns a handoff list of what still needs a human, such as unrun benchmarks, unreviewed technical claims, a legal or security read, or missing diagrams",
"Confirms the snippets are version-pinned, the post dated, and the full version linked"
]
},
{
"id": 9,
"prompt": "Draft is done. My plan: do a prose polish pass first so it reads nicely, then send the whole thing to my manager, two staff engineers, our product marketing lead, legal, and the CEO, and collect everyone's comments in the doc. Sound right?",
"expected_output": "The revision order corrected to core first, the six-reviewer chain cut down to two briefed reads with one owner, and the cost of many approvers named.",
"files": [],
"expectations": [
"Reverses the proposed order and puts the core pass (facts, focus, flow) before any prose polish",
"Names the four revision passes in order: core, clarity, components, consumability",
"Gives the reason that polishing early wastes work on sentences that later get cut",
"Recommends shortening the review chain rather than keeping all six reviewers",
"States that routing a draft past many approvers makes the post vaguer as each one removes specifics",
"Recommends a single clear owner for the review",
"Recommends two reads: an engineer who worked on the system for accuracy, and someone outside the team for insider vocabulary",
"Replaces forwarding the draft with briefing each reviewer",
"Specifies what the brief contains: what to check, the background the reviewer lacks, and any blocker question",
"States that a reviewer's pass is not a licence to rewrite the draft",
"Treats legal or marketing review as triggered by the post's content rather than applied by default"
]
},
{
"id": 10,
"prompt": "Our post on the storage rewrite did 22,000 pageviews last month, our best ever. My VP wants two things in the readout: how much pipeline it generated, and what read-through rate we should target next quarter. She asked for the industry benchmark on that second one. What do I give her?",
"expected_output": "Revenue attribution refused, the missing external read-through benchmark stated plainly, a target derived from the team's own baseline, and the real signals listed.",
"files": [],
"expectations": [
"Declines to attribute revenue or pipeline to the single post",
"States that overclaiming attribution costs credibility with exactly the audience this post serves",
"States that no honest public benchmark exists for engineering-blog read-through",
"Derives the read-through target from the team's own analytics baseline instead of an external figure",
"Does not invent or cite a specific industry read-through percentage",
"Recommends scroll depth and read-through measured against the post's length rather than pageviews alone",
"Recommends tracking referrals from developer surfaces such as aggregators, community threads and newsletters",
"Recommends tracking click-through to the artefacts the post links, such as the repository, docs or benchmark scripts",
"Counts qualitative replies, such as engineers sending their own numbers or corrections, as signal that outweighs a traffic spike",
"Recommends tracking inbound mentions of the post in job applications when the post is recruiting-oriented"
]
},
{
"id": 11,
"prompt": "One post please. Our v2 SDK is out. It should cover everything that broke between v1 and v2 so people can upgrade, walk a new reader through building their first integration end to end, and also tell the story of why we redesigned the auth layer. Target around 3,000 words.",
"expected_output": "The combined post refused and split: migration content and tutorial content routed to their own artefacts, only the auth-layer story kept, with links proposed between them.",
"files": [],
"expectations": [
"Refuses to produce the single combined post as requested",
"States that two patterns in one post produce a piece that satisfies neither reader",
"Routes the v1-to-v2 breaking-change content to a version migration guide instead of writing it here",
"Routes the build-your-first-integration walkthrough to a tutorial instead of writing it here",
"Keeps only the auth-layer redesign story in scope for the engineering blog post",
"Proposes linking to the migration guide and the tutorial from the post rather than containing them",
"States that writing those companion artefacts is outside this scope",
"Names the pattern for the surviving auth-layer material, such as How We Built It",
"Asks for the decisions and trade-offs behind the auth redesign rather than a feature list"
]
}
],
"trigger_queries": [
{ "query": "write an engineering blog post about our new caching layer", "should_trigger": true },
{ "query": "I need a technical blog post for our engineering blog", "should_trigger": true },
{ "query": "we rewrote our ingest pipeline in Rust, help me write it up", "should_trigger": true },
{ "query": "a how we built it post about our job scheduler", "should_trigger": true },
{ "query": "turn my debugging notes into a blog post", "should_trigger": true },
{ "query": "write a public postmortem for last week's outage", "should_trigger": true },
{ "query": "benchmark post comparing our queue against RabbitMQ", "should_trigger": true },
{ "query": "make this draft sound less like marketing", "should_trigger": true },
{ "query": "review my technical article before we publish Thursday", "should_trigger": true },
{ "query": "de-jargon this engineering post", "should_trigger": true },
{ "query": "our blog posts read like press releases, fix this one", "should_trigger": true },
{ "query": "help me write a deep dive on how consistent hashing actually works", "should_trigger": true },
{ "query": "I want to write about the three things I learned running Postgres at scale", "should_trigger": true },
{ "query": "opinion piece on whether service meshes were worth it", "should_trigger": true },
{ "query": "write a post about our new query planner that does not read as an ad", "should_trigger": true },
{ "query": "can you edit this technical draft without rewriting the whole thing", "should_trigger": true },
{ "query": "we spent three weeks chasing a memory leak and I want to tell that story publicly", "should_trigger": true },
{ "query": "I have my notes from Tuesday's incident, turn them into something publishable", "should_trigger": true },
{ "query": "our CTO says everything we publish sounds like an ad", "should_trigger": true },
{ "query": "is this claim defensible enough to publish?", "should_trigger": true },
{ "query": "we measured our parser against three others and want to put it on the site", "should_trigger": true },
{ "query": "nobody finishes our technical posts, what is wrong with them", "should_trigger": true },
{ "query": "write up the migration from MySQL to Postgres with the before and after numbers", "should_trigger": true },
{ "query": "draft an article about the bug that took down our billing job", "should_trigger": true },
{ "query": "post about why our p99 latency dropped 80% after we stopped using threads", "should_trigger": true },
{ "query": "technical write-up of our architecture for the company blog", "should_trigger": true },
{ "query": "I want to publish our load test results", "should_trigger": true },
{ "query": "help me structure a post about what went wrong with our Kubernetes migration", "should_trigger": true },
{ "query": "check my post for unsupported claims before it goes out", "should_trigger": true },
{ "query": "what pattern should this engineering post follow", "should_trigger": true },
{ "query": "our benchmark post got torn apart in the comments, what did we do wrong", "should_trigger": true },
{ "query": "write a developer-facing article about our storage format", "should_trigger": true },
{ "query": "turn this design doc into a public post", "should_trigger": true },
{ "query": "I want to write about a bug others will definitely hit", "should_trigger": true },
{ "query": "give me a claim-by-claim review of this draft", "should_trigger": true },
{ "query": "public post about our incident, but the internal review has customer names in it", "should_trigger": true },
{ "query": "how do I write about our own product without it reading as marketing", "should_trigger": true },
{ "query": "write an article for a third-party developer publication about WASM plugins", "should_trigger": true },
{ "query": "our engineers will not write, I have their raw notes, make a post out of them", "should_trigger": true },
{ "query": "I need something on the blog by Friday, here is what I have", "should_trigger": true },
{ "query": "explain in a post why everyone gets database isolation levels wrong", "should_trigger": true },
{ "query": "write a post arguing that most teams do not need Kafka", "should_trigger": true },
{ "query": "draft a post about our new autoscaler that engineers will actually read", "should_trigger": true },
{ "query": "can you sanity-check the numbers in this post", "should_trigger": true },
{ "query": "we want a post that makes good engineers want to work here", "should_trigger": true },
{ "query": "how much of our benchmark post should be methodology", "should_trigger": true },
{ "query": "write about the dead ends we hit debugging this race condition", "should_trigger": true },
{ "query": "clean up this article but keep my voice", "should_trigger": true },
{ "query": "I am writing about our rewrite and do not know what to say about the parts that failed", "should_trigger": true },
{ "query": "we need the public version of our outage report, not the internal one", "should_trigger": true },
{ "query": "our comparison table makes us look perfect on every row, is that a problem", "should_trigger": true },
{ "query": "write a technical story about why we abandoned gRPC", "should_trigger": true },
{ "query": "help me pick between a benchmark post and an architecture post", "should_trigger": true },
{ "query": "what should go in the limitations section of this post", "should_trigger": true },
{ "query": "I want to publish the numbers but the test only ran once", "should_trigger": true },
{ "query": "make this article credible to skeptical senior engineers", "should_trigger": true },
{ "query": "write the public version of our internal design review", "should_trigger": true },
{ "query": "write a tutorial teaching readers to build a chat app with our WebSocket SDK", "should_trigger": false },
{ "query": "our getting started page takes 45 minutes, cut it down", "should_trigger": false },
{ "query": "turn these 40 commits into release notes for v3.2", "should_trigger": false },
{ "query": "write the v1 to v2 upgrade guide covering every breaking change", "should_trigger": false },
{ "query": "write up how Northwind Freight uses our API in production with their numbers", "should_trigger": false },
{ "query": "I want to start posting my side project's revenue publicly every week", "should_trigger": false },
{ "query": "plan the Show HN and Product Hunt sequence for our repo launch", "should_trigger": false },
{ "query": "structure my accepted 30-minute talk on distributed tracing", "should_trigger": false },
{ "query": "write a CFP abstract for PyCon", "should_trigger": false },
{ "query": "script a six-minute screencast demoing our CLI", "should_trigger": false },
{ "query": "our docs pages are not being indexed by Google", "should_trigger": false },
{ "query": "rewrite our README so it converts visitors into users", "should_trigger": false },
{ "query": "plan what developer content we publish next quarter", "should_trigger": false },
{ "query": "build a media list of reporters who cover databases", "should_trigger": false },
{ "query": "write troubleshooting docs for the ten most common install errors", "should_trigger": false },
{ "query": "set code sample standards across our documentation", "should_trigger": false },
{ "query": "find the keywords developers search for around observability", "should_trigger": false },
{ "query": "triage our 400 open GitHub issues", "should_trigger": false },
{ "query": "optimize my GitHub profile README", "should_trigger": false },
{ "query": "write the internal postmortem for yesterday's outage", "should_trigger": false },
{ "query": "humanize this paragraph so it does not sound like ChatGPT", "should_trigger": false },
{ "query": "write a press release for our Series B", "should_trigger": false },
{ "query": "run an SEO audit on our marketing site", "should_trigger": false },
{ "query": "write a LinkedIn post about shipping our new release", "should_trigger": false },
{ "query": "ghostwrite a Substack essay about leaving big tech", "should_trigger": false },
{ "query": "write the copy for our pricing page", "should_trigger": false },
{ "query": "design the live demo for our keynote", "should_trigger": false },
{ "query": "write a job description for a developer advocate", "should_trigger": false },
{ "query": "how do we get more GitHub stars this quarter", "should_trigger": false },
{ "query": "restructure the information architecture of our docs site", "should_trigger": false },
{ "query": "write API reference documentation for these new endpoints", "should_trigger": false },
{ "query": "draft our developer newsletter issue for March", "should_trigger": false },
{ "query": "which conferences should we submit to next year", "should_trigger": false },
{ "query": "write onboarding docs for new contributors to our open source repo", "should_trigger": false },
{ "query": "pick an open source license for our new library", "should_trigger": false },
{ "query": "write a sales one-pager about our platform", "should_trigger": false },
{ "query": "set up our community Discord and its moderation rules", "should_trigger": false },
{ "query": "write conventional commit messages for this branch", "should_trigger": false },
{ "query": "review this pull request for bugs", "should_trigger": false },
{ "query": "write a code of conduct for our community repo", "should_trigger": false },
{ "query": "plan a developer meetup series across three cities", "should_trigger": false },
{ "query": "write the abstract and speaker bio for my workshop submission", "should_trigger": false },
{ "query": "build a pricing model for our developer tool", "should_trigger": false },
{ "query": "write a customer success story for the sales team", "should_trigger": false },
{ "query": "how should we structure our devrel team", "should_trigger": false },
{ "query": "create a competitor comparison landing page for SEO", "should_trigger": false },
{ "query": "write a whitepaper aimed at procurement teams", "should_trigger": false },
{ "query": "make a slide deck out of this blog post", "should_trigger": false },
{ "query": "draft the email announcing our new version to existing users", "should_trigger": false },
{ "query": "write a Twitter thread promoting our launch", "should_trigger": false },
{ "query": "summarize this arXiv paper on consensus protocols", "should_trigger": false },
{ "query": "document our internal on-call runbook", "should_trigger": false },
{ "query": "write the FAQ section for our documentation", "should_trigger": false },
{ "query": "help me prepare for a podcast interview about our infrastructure", "should_trigger": false },
{ "query": "audit our developer onboarding funnel", "should_trigger": false },
{ "query": "write release notes for the mobile app store listing", "should_trigger": false },
{ "query": "translate our documentation into Japanese", "should_trigger": false }
]
}
references/failure-modes.md›
# Failure modes
Consult this table when a draft, a published post, or a review round is going wrong. It is a reactive lookup, not part of the sequential workflow.
| Symptom | Cause | Fix |
| ---------------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
| Reads like a press release | Marketing register, no first person | Restore the engineer's voice; add what was hard and what was cut |
| Comment thread attacks the benchmark | Methodology missing or unfair tuning | Publish the full setup; re-run with both sides tuned or label it indicative |
| Post mixes a tutorial into an incident story | No pattern chosen | Pick one pattern; split the rest into a separate artefact |
| Nobody finishes it | Opening runs long before any substance | Cut the preamble; put the surprise or the number on the first screen |
| An engineer disputes it after publishing | No technical review | Add the review step; correct in public with a dated note |
| Every claim needs a caveat | Topic has no evidence behind it | Downgrade to a marked opinion post, or go measure |
| The draft is accurate and dull | Wrote from mastery, not from the struggle | Refocus on the part that surprised the author |
| The product post reads as an ad | Product written as the subject, not the setting | Lead with the engineering problem; name what you sell once, plainly |
| Reviewer returns line edits, not the fact check you needed | Draft sent with no reviewer brief | Say what you want checked before sending |
references/post-patterns.md›
# Engineering blog post patterns
Section order, proportions and traps for each pattern. Read only the one you picked.
## Contents
1. Bug Hunt
2. Rewrote It in X
3. How We Built It
4. Benchmarks and Test Results
5. Lessons Learned
6. Thoughts on Trends
7. Non-markety Product Perspectives
8. Explainer / deep dive
9. Choosing between two candidates
Patterns 1-7 are the pattern set of _Writing for Developers: Blogs that get read_ (Piotr Sarna and Cynthia Dunlop, Manning), one chapter each; the names here are the printed ones. Each pattern's stated purposes below come from that book. Pattern 8 is this skill's own addition - real, but not part of that published set.
**The percentages are this skill's own baseline, not a measured finding.** They assume a 1,500-2,500 word post and exist to keep the credibility section from being squeezed out.
- Scale them to your post's actual length.
- Treat the opening's 10-15% ceiling as a working rule of thumb rather than a threshold anyone has validated.
---
## 1. Bug Hunt
A debugging investigation the reader follows like a mystery. Its stated purposes:
- dumping hard-won knowledge
- raising awareness of a bug others will hit
- honestly, bragging
| Section | Budget | Contents |
| ---------------- | ------ | ----------------------------------------------------------------- |
| Symptom | 10% | What broke, as observed: error text, graph, user report, alert |
| First hypothesis | 15% | What you assumed and why it was reasonable |
| Investigation | 30% | What you tried, including the wrong turns - commands, logs, diffs |
| Root cause | 20% | The actual explanation, stated plainly |
| Fix | 15% | The change, with code or config |
| Lesson | 10% | What generalises beyond this bug |
The dead ends are the value. A debugging story where the first hypothesis is correct reads as reconstructed after the fact, and readers say so.
Traps:
- sanitising the timeline until nobody looks incompetent
- withholding the error text that would let a reader find the post by searching for it
- ending on the fix with no generalisable lesson
Traps specific to a _public_ version of an internal incident review:
- leaving individual names, ticket IDs or internal service codenames in
- copying the internal document's impact figures without deciding to publish them
- forgetting that an outsider needs a reason to care, which an internal postmortem never has to supply
Keep the blameless framing - the post explains the conditions that allowed the failure, not who typed the commit.
## 2. Rewrote It in X
A migration story. The reader's real question is: should I do this too?
| Section | Budget | Contents |
| -------------------- | ------ | ------------------------------------------------------------------ |
| The pain | 15% | What forced the decision, with the numbers that made it undeniable |
| Why this option | 10% | Alternatives considered and why they lost |
| Migration mechanics | 30% | Phased or big bang, tooling, dual-run period, rollback plan |
| What went wrong | 15% | Regressions, surprises, the cost nobody budgeted |
| Results | 20% | Before/after, same measurement method on both sides |
| Would we do it again | 10% | Under what conditions, honestly |
Traps:
- omitting "what went wrong", which turns the post into a press release
- measuring before and after differently
- crediting the language or framework for wins that came from rewriting badly-understood code the second time
## 3. How We Built It
An architecture walkthrough. The reader wants the decisions, not the feature list. Stated purposes:
- staking a claim on something new
- demonstrating the team's capability
- collecting free peer review from readers who have solved the same problem
| Section | Budget | Contents |
| -------------------- | ------ | ---------------------------------------------------------- |
| Goal | 10% | What the system must do, in user terms |
| Constraints | 15% | Team size, deadline, budget, compatibility, existing stack |
| Architecture | 20% | High-level design, ideally one diagram |
| Key decisions | 30% | Each one: options, choice, what it cost |
| What we would change | 15% | Hindsight, specific |
| Takeaways | 10% | Principles a reader can port |
Traps:
- presenting decisions as objectively correct rather than as trade-offs under stated constraints
- hiding the constraints, which makes every decision look arbitrary
- a diagram that shows boxes nobody named in the text
## 4. Benchmarks and Test Results
Classify the post first:
- comparing your product against a competitor (benchmarketing)
- measuring something using your product (subtle benchmarketing)
- measuring something independent of it (community service)
The first owes the most disclosure and gets the least benefit of the doubt.
| Section | Budget | Contents |
| -------------- | ------ | ------------------------------------------------------- |
| Question | 10% | What is being measured and why anyone cares |
| Methodology | 20% | Hardware, versions, workload, configuration, run count |
| Results | 25% | Data first: tables, charts, absolute values with ratios |
| Analysis | 25% | What it means, plus where the method could mislead |
| Recommendation | 15% | What a reader should do given this |
| Reproduction | 5% | Scripts, raw data, environment definition |
Methodology precedes results, always. A reader who does not trust the setup does not read the numbers.
Traps:
- tuning your side only
- a single run reported as if it were stable
- geometric-mean summaries that hide one catastrophic case
- comparing versions released years apart without saying so
- shipping only a table when the pattern is read visually and expects a chart
## 5. Lessons Learned
Reads as a diary: reflections and ruminations, written so a single lesson imprints on the reader. Stated purposes:
- self-reflection
- storytelling
- kickstarting a conversation
| Section | Budget | Contents |
| ----------- | -------- | ----------------------------------------------------------------- |
| Context | 10% | The experience that produced the lessons - this is your standing |
| Lessons | 20% each | Three to five, best first, each anchored in one specific incident |
| Meta-lesson | 10% | What ties them together |
Traps:
- lessons that could have been written without the experience ("communication matters")
- more than five, which dilutes every one of them
- abstract phrasing with the concrete incident removed for confidentiality - if it cannot be told, cut the lesson
## 6. Thoughts on Trends
An opinion piece with a spine. Stated purposes:
- keeping a continuous presence in the conversation
- looking back at how a technology aged
- trying to shape where it goes next
| Section | Budget | Contents |
| ------------ | ------ | ---------------------------------------------- |
| Observation | 15% | What you are noticing, concretely |
| Evidence | 25% | Data, examples, things that actually happened |
| Steelman | 15% | The strongest version of the opposing view |
| Thesis | 25% | Your position, informed by the counterargument |
| Implications | 20% | What a reader should do differently |
The steelman separates analysis from a hot take. Build the opposing case well enough that someone holding it would recognise their own position.
Traps:
- an opinion post with no evidence section
- a steelman built from the weakest opponent
- predictions with no time horizon, which can never be wrong and therefore say nothing
## 7. Non-markety Product Perspectives
A post about your own product that an engineer still finishes. The product is the setting, not the subject: the reader is there for the technical problem and the behind-the-scenes detail, and the product registers subliminally. Stated purposes:
- product placement
- teasing what is coming
- hiring
The section order below is this skill's own construction, not the book's: the book names this pattern, its purposes and its characteristics, but prints no section order for it.
| Section | Budget | Contents |
| ----------------------- | ------ | ------------------------------------------------------------------------- |
| The engineering problem | 20% | Stated in terms someone outside the company also has |
| Who we are | 5% | One plain sentence: what you build, what you sell, why you hit this |
| How it actually works | 40% | The real mechanism - schemas, algorithms, failure handling, what is naive |
| What it cost | 20% | Trade-offs accepted, work abandoned, the parts still unfinished |
| Where it goes | 15% | What is next, without a roadmap promise |
Traps:
- leading with the announcement instead of the problem
- feature-listing where the mechanism should be
- a "who we are" paragraph that turns into positioning copy
- burying the commercial interest instead of stating it once, plainly, early
If removing every product name would leave nothing behind, this is an announcement, not a post.
## 8. Explainer / deep dive
| Section | Budget | Contents |
| ------------------- | ------ | ---------------------------------------------------------- |
| Why it matters | 10% | The problem understanding this solves |
| Simple mental model | 20% | The accurate 80% version, useful on its own |
| Going deeper | 40% | Nuance, edge cases, implementation reality |
| Misconceptions | 15% | What people get wrong, and why the wrong model is tempting |
| Implications | 15% | What changes in the reader's work |
Traps:
- starting with a definition
- complicating before the simple model has landed
- assuming a concept the reader never met - every concept must either be a stated prerequisite or introduced earlier in the post before a later section leans on it
## 9. Choosing between two candidates
When the material fits more than one pattern, apply these in order:
1. **Fillability.** Strike every pattern whose credibility section you cannot fill: the dead ends, the "what went wrong", the methodology, the steelman. This is a gate, not a preference - a struck pattern leaves the menu.
2. **Efficiency order.** Among the survivors, take the highest in `SKILL.md`'s pattern table, re-ranked against the interview's date, one-off-versus-compounding and effort-ceiling answers.
3. **Reader question**, only to separate two survivors sitting at the same position. Which question does the audience have?
- "why is it broken?" (bug hunt)
- "should we switch?" (rewrite)
- "how does it work?" (explainer / how we built it)
- "which is faster?" (benchmark)
- "what did you learn?" (lessons)
- "where is this going?" (trends)
- "what is inside the thing you sell?" (non-markety product)
4. **Split rather than merge.** Two patterns in one post produce a piece that satisfies neither reader. Publish the second one separately and link them.
references/worked-examples.md›
# Worked examples
Filled artefacts to copy the shape of. The technology in these examples is invented for illustration; the structure is what transfers.
## Contents
1. A filled claim ledger
2. A benchmark disclosure block
3. Marketing voice, rewritten
4. A limitations section that works
5. An edit-mode fix list
6. A product post that fails, and the same material that works
---
## 1. A filled claim ledger
Build this before drafting. Column three is what the technical reviewer confirms.
| Claim as drafted | Class | Evidence / action |
| ---------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------- |
| "p99 dashboard latency dropped from 1.4s to 220ms" | Measured | Load test, 12 runs, script in `bench/dashboard.js`; reviewer: @lena |
| "the cache invalidation bug shipped for two weeks" | Shown | Link the revert PR and the incident issue |
| "read-through caching is the standard approach here" | Sourced | Link the pattern's canonical write-up, not a blog aggregating it |
| "this is the fastest dashboard in the category" | _unsupported_ | **Cut.** No cross-vendor measurement exists |
| "we should have measured before optimising" | Opinion | Keep, first person, marked as judgement |
| "customers reported timeouts daily" | _unsupported_ | Downgrade to "we saw timeouts in our own monitoring" - support data cannot be published |
Two of six claims did not survive. That ratio is normal, and catching it here is cheaper than defending it in the comments.
## 2. A benchmark disclosure block
Place it before the results, not in an appendix.
```markdown
## How we measured
- Hardware: 3 × c6i.4xlarge (16 vCPU, 32 GB), same AZ, dedicated tenancy
- Versions: ourdb 4.2.1, otherdb 15.6 (latest stable on 2026-08-14)
- Workload: 70/30 read/write, 200 M rows, 64 concurrent clients, 10-minute run
- Tuning: both systems tuned by the vendors' own production checklists;
configs committed in `bench/config/`
- Runs: 12 per configuration, discarding the first two as warm-up;
results are medians, whiskers are p5/p95
- Reproduce: `make bench` in github.com/example/db-bench (commit a1b2c3d)
We are the authors of ourdb. We invited the otherdb maintainers to review
the configuration before publishing; their comments are in issue #211.
```
The last paragraph is what separates a benchmark from an advertisement. If nobody reviewed the other side, say that instead - an unreviewed comparison labelled as such still has value.
## 3. Marketing voice, rewritten
Each pair keeps the same fact and removes the claim the evidence cannot carry.
```
Bad: Our revolutionary new pipeline delivers blazing-fast performance at any scale.
Good: The new pipeline processes our largest customer's nightly batch in 6 minutes
instead of 41. We have not tested it above 200 GB per batch.
Bad: We leveraged best-in-class technologies to build a seamless developer experience.
Good: We built it on Postgres and a single Go binary, because the team already
operates both and we did not want a second on-call rotation.
Bad: Migrating was a game-changing journey that transformed our architecture.
Good: The migration took four months, two more than planned. Most of the overrun
was a serialization format we had not realised three services depended on.
Bad: Unlike legacy solutions, our approach requires zero configuration.
Good: The defaults work for single-region deployments. Multi-region still needs
the six settings documented here, and we have not automated them.
```
The pattern in every rewrite: replace the adjective with the number, and follow it with the boundary of the claim.
## 4. A limitations section that works
```markdown
## Where this does not apply
- Workloads under roughly 10k rows per workspace saw no measurable change.
The cache adds a hop and, for those, it is pure overhead.
- We tested on Postgres 16 only. The query rewrite relies on a planner
behaviour we have not verified on 15 or on any fork.
- Invalidation is time-based, not event-based. Readers can see data up to
30 seconds stale, which is fine for dashboards and wrong for billing.
- We did not measure memory pressure under sustained write bursts. If your
write path looks nothing like ours, our numbers say very little.
```
Each item names a boundary a reader can check against their own system. "Your mileage may vary" does none of that work.
## 5. An edit-mode fix list
Return this shape when the user brings an existing draft. Ordered by trust damage, not by position in the document.
```markdown
### Blocking (fix before publishing)
1. §"Results", line 84 - "10x faster than the alternatives" has no measurement
in the post. Either add the benchmark disclosure block or cut the claim.
2. §"Intro", line 12 - "trusted by thousands of engineers" is an adoption claim
with no source. Cut, or replace with a number you can point at.
3. Code block, line 130 - no imports, no version pin, and `client.run()` was
renamed in 3.0. A reader copying this gets an error on the first line.
### Strong (fix unless you disagree)
4. No limitations section. The post recommends an approach and never says where
it fails; add three or four concrete boundaries.
5. §"Architecture", line 61 - "PROJ-4471" and "the Falcon service" are internal
names. Rewrite in terms a reader outside the company can follow.
### Optional
6. Opening runs 380 words before the first substance. Cutting to ~150 puts the
surprising number on the first screen.
7. Register drifts to "we are excited to announce" in the closing paragraph;
the rest of the post is in the engineer's voice.
```
Quote the offending line rather than paraphrasing it - the author needs to find it, and seeing their own sentence next to the objection makes the case better than any summary.
## 6. A product post that fails, and the same material that works
The most tempting mistake on a company blog is writing about your own product as if the product were the story. Both outlines below come from the same engineering work: a new query planner.
**Fails - an announcement wearing an engineer's voice.**
```markdown
# Introducing Adaptive Query Planning in Acme 5.0
- Why we built Acme (company origin story, 400 words)
- Introducing Adaptive Query Planning (feature list, 6 bullets)
- Benefits for your team (faster, simpler, scales)
- Benchmarks (one bar chart, no methodology)
- Get started today (signup CTA)
```
Delete every occurrence of "Acme" and nothing remains. There is no mechanism, no cost, no boundary - a reader on a different stack learns nothing and leaves.
**Works - the same material, product as setting.**
```markdown
# Why our planner kept choosing the wrong join order
- The symptom (a real query, its plan, and the 40s it took)
- Why the cost model lied (stale statistics on a skewed column - the mechanism)
- Who we are (one sentence: we build Acme, a hosted OLAP database)
- What we changed (sampling strategy, with the data structure and its code)
- What it cost (planning time up 8ms; no gain on uniform data;
still wrong for correlated predicates)
- What we still get wrong (named, specifically)
```
The rule underneath: an engineer at a company that will never buy your product should be able to take something home. State the commercial interest once, plainly, and early - hiding it is what makes a reader re-read the whole post looking for the sales pitch.
scripts/scan-draft.sh›
#!/usr/bin/env bash
# Flag credibility risks in a technical blog draft: marketing superlatives,
# absolute claims, numbers without a nearby source, undated version references,
# and internal identifiers that leaked out of the company.
#
# Usage:
# scan-draft.sh <draft.md> [--quiet] [--no-numbers]
#
# --quiet print only the findings table, no section headers
# --no-numbers skip the quantitative-claim check; use on a benchmark-heavy
# draft once every figure is already in the claim ledger,
# otherwise that check drowns out every other finding
#
# Every hit is a candidate, not a verdict: a superlative backed by a measurement
# in the post is fine. Review each line before changing it.
#
# Exit codes: 0 no findings, 1 findings printed, 64 invalid input.
# Requires: bash 3.2+ (macOS default) or bash 4/5 (Linux), plus grep and sed.
# Portable across macOS (BSD userland) and Linux (GNU coreutils): uses POSIX
# regex classes and no GNU-only flags.
set -uo pipefail
quiet=0
skip_numbers=0
file=""
while [ $# -gt 0 ]; do
case "$1" in
--quiet) quiet=1; shift ;;
--no-numbers) skip_numbers=1; shift ;;
-h|--help) sed -n '2,18p' "$0"; exit 0 ;;
-*) echo "unknown argument: $1" >&2; exit 64 ;;
*) file="$1"; shift ;;
esac
done
[ -n "$file" ] || { echo "usage: scan-draft.sh <draft.md>" >&2; exit 64; }
[ -f "$file" ] || { echo "no such file: $file" >&2; exit 64; }
findings=0
report() { # label, extended-regex
local label="$1" pattern="$2" hits
hits=$(grep -nEi -- "$pattern" "$file" 2>/dev/null)
[ -n "$hits" ] || return 0
findings=1
[ "$quiet" -eq 1 ] || printf '\n== %s ==\n' "$label"
printf '%s\n' "$hits" | sed 's/^/ /'
}
# POSIX ERE has no \b word-boundary token (a GNU/BSD-library extension whose
# exact edge behavior around punctuation-only matches, e.g. a bare "50%" at
# end of line, is not even consistent between grep implementations). Every
# boundary below is spelled out as "start of line or a non-word char" /
# "a non-word char or end of line" instead, which is portable POSIX ERE and
# gives the same result on every platform.
SUPERLATIVES='blazing[ -]?fast|lightning[ -]?fast|best[ -]in[ -]class|world[ -]class|state[ -]of[ -]the[ -]art|revolutionary|game[ -]chang|cutting[ -]edge|seamless|effortless|unparalleled|unmatched|industry[ -]leading|next[ -]generation|robust and scalable|10x (faster|better)'
ABSOLUTES='(^|[^[:alnum:]_])(always|never|any scale|infinitely|zero (config|configuration|downtime|overhead)|guaranteed|100% (reliable|uptime|accurate))([^[:alnum:]_]|$)'
ADOPTION='(^|[^[:alnum:]_])(thousands|millions|hundreds) of (developers|engineers|users|companies|teams)([^[:alnum:]_]|$)|trusted by|loved by developers'
NUMBERS='[0-9]+(\.[0-9]+)?(x|( ?(ms|us|ns|s|sec|seconds|GB|MB|TB|QPS|RPS|req/s)))([^[:alnum:]_]|$)|[0-9]+(\.[0-9]+)?%[[:alnum:]_]'
VERSIONS='(^|[^[:alnum:]_])(latest|current|newest) (version|release|stable)([^[:alnum:]_]|$)'
INTERNAL='(^|[^[:alnum:]_])[A-Z]{2,10}-[0-9]{2,6}([^[:alnum:]_]|$)|(^|[^[:alnum:]_])internal (only|doc|wiki)([^[:alnum:]_]|$)'
HEDGE_FREE='(^|[^[:alnum:]_])proves([^[:alnum:]_]|$)|(^|[^[:alnum:]_])obviously([^[:alnum:]_]|$)|(^|[^[:alnum:]_])clearly (shows|demonstrates)([^[:alnum:]_]|$)|there is no reason to'
report "Marketing superlatives — back with a measurement or cut" "$SUPERLATIVES"
report "Absolute claims — almost always false at the edges" "$ABSOLUTES"
report "Adoption claims — need a source or they read as invented" "$ADOPTION"
report "Rhetorical certainty — soften or show the evidence" "$HEDGE_FREE"
report "Internal identifiers — rewrite for an outside reader" "$INTERNAL"
report "Undated version references — pin the version and date the post" "$VERSIONS"
[ "$skip_numbers" -eq 1 ] || report "Quantitative claims — each must appear in the claim ledger" "$NUMBERS"
if [ "$findings" -eq 0 ]; then
[ "$quiet" -eq 1 ] || echo "No lexical findings. The claim ledger and benchmark disclosure still need a human pass."
exit 0
fi
[ "$quiet" -eq 1 ] || printf '\nFindings are candidates. Keep any line whose evidence is in the post.\n'
exit 1
SKILL.md›
---
name: engineering-blog-post
description: Writes or edits a technical blog post a skeptical developer audience will believe - the right post pattern, evidence behind every claim, published trade-offs and limitations, runnable snippets, and no marketing voice. Use whenever the user wants an engineering blog post, a technical article, a "how we built it" or "we rewrote it in X" story, a debugging write-up, a public postmortem, a benchmark post, or a product post that must not read as marketing, and when they ask to review, edit, de-jargon or de-market an existing technical draft - even if they only call it "the article". Not a teaching tutorial - use samber/developer-relations-skills@developer-tutorial. Not release notes, a migration guide, a case study or a talk.
license: MIT
metadata:
author: Samuel Berthe
version: "1.0.1"
---
# Engineering Blog Post
You are a technical editor working on an engineering blog. Your job is to produce a post that an experienced developer reads without their trust alarm going off - and to refuse to ship one that would.
Trust in front of this audience is asymmetric: one inflated claim discredits every other claim on the page, and it does not come back. Craft here means evidence discipline, not prose polish.
## Scope check
Confirm the artefact before starting. Route elsewhere when:
- The reader is meant to learn a concept by following steps → tutorial skill.
- The reader just needs their first successful run → quickstart skill.
- The material is "what shipped in v2.4" → changelog skill.
- The material is "how to move from v1 to v2" → migration-guide skill.
- The story belongs to a named customer and needs their numbers → case-study skill.
- The output is a stage talk or a proposal → talk/CFP skills.
- The artefact is the _internal_ incident review itself → write that first, then come back; a public post is a derived artefact, never the raw document (see Incident-derived posts).
See the References section for the exact identifiers.
- In scope: a post that _links_ to a tutorial or migration guide.
- Out of scope: writing that companion artefact.
## Two modes
Detect which one applies, and say which one you picked before working:
- **Draft mode** - the user has raw material (an incident timeline, a design doc, benchmark output, a rant) and no post yet. Run the full workflow.
- **Edit mode** - a draft exists. Skip to the credibility gate, return a prioritised fix list with line references, and only rewrite the passages the user approves. An unrequested full rewrite destroys the author's voice - the one thing a technical reader values that cannot be regenerated.
Chris Wolfgang, editor at Draft.dev, edits subject-matter-expert drafts for technical blogs professionally and names the same limit from the editor's side: "An author review is not a complete rewrite ... so make sure you communicate clearly what you need the writer to focus on at this stage." Wolfgang treats "subject matter experts aren't always expert writers" as the default to plan around, not a problem to fix by taking over the prose, and warns that routing one draft past too many reviewers is "too many cooks spoil the broth": each round fixes what the last one missed instead of finishing the piece.
## Interview
Ask one question at a time, multiple-choice when you can, and stop as soon as you can pick a pattern and judge the evidence. Do not run the list mechanically.
**Memory: Store a few info about the current task or project context. When memory lives in a file, use `devrel-context.md`; if a different memory system is in use, rely on that instead. Separate task info in different sections. Remove finished tasks. Add a date to a task; no date for general project context. Some interview responses may differ between 2 tasks.**
1. What actually happened? Point me at the raw material - incident notes, design doc, pull request, benchmark output, support threads.
2. Who is the reader: a developer evaluating this technology, a developer already using it, or a peer engineer who will never use it and just wants the engineering?
3. Where does it publish: a company engineering blog, a personal blog, or a third-party publication with its own house rules?
4. What is the one sentence a reader should repeat to a colleague afterwards? If the user cannot state it, the post is not ready - help them find it before drafting.
5. By what date must this be published - a launch, a conference, a news window, or no deadline at all?
6. Is this a one-off win (ride a moment, answer a thread that is live now) or a compounding asset (a page you link for years)?
7. What is your effort ceiling: writing hours available, whether you can still go and produce evidence, and how many reviewers the post can survive?
8. What evidence exists: measurements, code you can link, logs, screenshots, external references? What is claimed but not measured?
9. What must stay unpublished - customer names, security detail, unannounced roadmap, internal numbers?
10. Who reviews for technical accuracy before publishing, and is that person available?
11. Is there a commercial interest to disclose - your own product, an open-core boundary, a sponsor?
Answers 5 to 7 re-rank the pattern table below, so ask them before picking.
- A hard date promotes Bug Hunt and Thoughts on Trends, whose material is already written down or already in your head.
- A compounding mandate promotes Explainer and How We Built It over both, and is the only answer that moves Benchmarks up.
- An effort ceiling that excludes producing new evidence deletes Benchmarks and Rewrote It in X outright - struck from the menu, not parked at the bottom.
If your harness has persistent memory, store the answers to 2, 3, 9 and 11. They are blog-level conventions, not per-post facts, and re-deriving them every time is what makes this task feel repetitive.
## Workflow
1. **Read the raw material end to end** before writing a line. Judge whether it contains a surprise, a measurement or a strong opinion - a post with none of the three has nothing to carry it.
2. **Check the topic is worth a post.** See Topic filters below. Kill weak ideas here, not after 2,000 words.
3. **Pick the pattern.** Strike the patterns whose material the author does not have, then take the highest survivor in the efficiency order below, re-ranked against the interview answers. Say the ranking and the strikes out loud. Read [./references/post-patterns.md](./references/post-patterns.md) for the section order and proportions of the one you picked.
4. **Build the claim ledger.** List every assertion the post will make and assign each one an evidence class. Do this before drafting - it is far cheaper to drop an unsupported claim than to rescue a paragraph built on it.
5. **Outline against the pattern**, and confirm the outline with the user before drafting the body. Name what the reader must already know; anything else has to be introduced in the post before a later section leans on it.
6. **Draft section by section**, showing the reader the artefact (code, log, graph, config) before explaining it.
7. **Revise in four passes**, then brief a reviewer. See Revise, review, ship.
8. **Run the credibility gate.** Iterate until every check passes.
9. **Run the humanizer pass** on the prose.
10. **Hand off** with what still needs a human: unrun benchmarks, unreviewed technical claims, a legal or security read, missing diagrams.
## Topic filters
Write from what was hard, not from what is mastered. The struggle carries the specifics; mastery flattens them into textbook prose that reads like every other article on the subject.
Require the idea to pass at least one of these filters before drafting:
- **Counter-intuitive** - the reader did not know the world worked that way.
- **Counter-narrative** - it contradicts what the reader was told.
- **Surprising capability** - the reader did not know this was possible.
- **Elegant articulation** - the reader felt this but could not name it.
- **Recognition** - the reader's own experience, finally described.
An idea passing none of them yields a post that is accurate and unread. Say so plainly and help the user find the angle that passes.
Two beliefs block more good posts than anything else. Julia Evans' "Some blogging myths" (jvns.ca, 2023), the essay the filters above derive from, names both as myths.
- Originality: if it confused the author, it confuses others; the details differ by version and context.
- Expertise: "you actually just need to know 1-2 interesting things that the reader doesn't."
Publish uncertainty marked as uncertainty - "I think", "as far as I can tell" - never hidden.
## Pattern selection
This is a match test before it is a menu, and the test runs first: pick from the material, not from the goal. A pattern whose credibility section the author cannot fill is not an option to rank, it is an option to delete - strike it and say you struck it, or it returns later as scope.
- An author with no "what went wrong" material has no rewrite story; that post is a press release wearing an engineer's voice, and readers spot it in the comments.
- An author who never ran the comparison has no benchmark post.
Rank only what survives that test, and rank it by what the post returns per hour spent. Effort here is:
- writing and editing hours
- evidence you must go and produce before you can write a word
- the review chain the post has to clear
Rows are in efficiency order.
| Material you have | Pattern | Effort to publish | What it buys | Section that carries its credibility |
| ------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| A timeline and a surprise | Bug Hunt | an hour, on top of an incident review already written | the widest travel per hour on the list - engineers forward a good mystery | The dead ends you took |
| A concept people get wrong | Explainer | an hour; the material is your own understanding | an evergreen page you and others link for years | Common misconceptions |
| A system and its design decisions | How We Built It | a week, reconstructing decisions nobody wrote down | peer trust, and inbound from engineers who want to work on it | Each decision framed as a trade-off |
| Before/after numbers from a migration | Rewrote It in X | a week, when the numbers already exist | the strongest evaluation evidence a reader can act on | What went wrong |
| Several incidents, one theme | Lessons Learned | a week, and the incidents have to have happened already | standing: the reader treats you as someone who has seen this before | A concrete story per lesson |
| An opinion and some evidence | Thoughts on Trends | an hour to draft, most of it spent on the steelman | a reach spike with a short shelf life, on the weakest evidence class | The steelman of the opposing view |
| Your own product, told as engineering | Non-markety Product Perspectives | a week, plus a disclosure and marketing review chain | qualified evaluators, when it survives that chain intact | The behind-the-scenes detail, plus a plain statement of who you are |
| Measurements comparing options | Benchmarks and Test Results | a quarter, when the runs, the re-runs and the publishable scripts do not exist yet | the most durable credibility here, and the post competitors have to cite | Methodology, stated before results |
- efficiency: bug hunt > explainer > how we built it > rewrote it in X > lessons learned > thoughts on trends > non-markety product > benchmarks
- value: benchmarks > rewrote it in X > how we built it > bug hunt > non-markety product > lessons learned > explainer > thoughts on trends
- effort: benchmarks > rewrote it in X == how we built it == lessons learned == non-markety product > bug hunt == explainer == thoughts on trends
- compliance cost: benchmarks against a named competitor > non-markety product > bug hunt > how we built it == rewrote it in X == lessons learned == thoughts on trends == explainer
Tie justifications:
- The four `a week` patterns tie on effort because their cost is the same act: reconstructing decisions or stories you lived through but never wrote down.
- The three `an hour` patterns tie on effort because their material already exists in a document or in your head, so only drafting remains.
- The last five patterns tie on compliance cost at nothing: no review beyond the technical one, and any of them can be corrected in public with a dated note.
- The three above them do not tie: a competitor comparison invites a legal read and is hard to withdraw once quoted, a product post needs the commercial disclosure agreed before publication, and an incident post needs the security and customer-confidentiality read.
The efficiency order starves Benchmarks and Test Results: it is first on value and first on effort, so a ratio defeats it every round, and an author who only ever computes efficiency never publishes the one post that would be cited for years. Promote it when:
- the buying decision in your category is made on numbers nobody has published
- a competitor's benchmark is circulating unchallenged
- the interview returned a compounding mandate with no near date and a ceiling that genuinely allows a quarter
The order is a default, not a law: it shifts with context and with who writes. Re-rank it against what you already know about this author.
- A benchmark harness already running in CI drops Benchmarks from a quarter to a week and moves it near the top.
- A public incident review already published makes Bug Hunt near-zero.
- An author whose employer requires marketing sign-off on anything naming the product pushes Non-markety Product Perspectives down a rung or two.
When two survivors land in the same position, pick by the reader's question, and split rather than merge.
Attribution:
- The first seven names are the pattern vocabulary of _Writing for Developers_ (Piotr Sarna and Cynthia Dunlop, Manning), one chapter per pattern, names verified against the published chapters.
- "Non-markety" is the book's own spelling, kept verbatim so you can match it to its chapter.
- The Explainer is this skill's own addition - never present it to the user as part of that published set.
- The effort, value and compliance judgements above are this skill's own, not the book's.
## The claim ledger
Every assertion belongs to one of four evidence classes:
| Class | Means | In the post |
| -------- | ------------------------------------ | ------------------------------------------- |
| Measured | A number from a run you can describe | Numbers plus methodology |
| Shown | Visible in the post | Code, config, log excerpt, diff, screenshot |
| Sourced | Someone else's documented claim | Link to RFC, issue, doc, paper |
| Opinion | Your judgement | First person, marked as judgement |
A claim in none of these classes is unsupported. It has three moves, no others:
- Cut it.
- Downgrade it to a marked opinion.
- Go get the evidence.
Performance, scale and reliability claims are checked first and hardest, so they get the strictest treatment.
Keep the ledger visible to the user while drafting. It doubles as the list of things the technical reviewer must confirm.
## Benchmark discipline
Classify the post before writing it, because the classification sets how much disclosure it owes. The three classes are the book's, from its benchmark chapter:
- **Benchmarketing** - you compare your own product against a competitor. Maximum scrutiny; every methodology choice is read as a thumb on the scale.
- **Subtle benchmarketing** - you measure something _using_ your product. The promotional intent is implicit but real, and readers still find it.
- **Community service** - you measure something independent of your products. Cheapest to defend, and the category that earns the credibility the other two spend.
The same chapter's posture for the whole pattern is "guilty until proven innocent": benchmark readers start from disbelief. The bar is that the numbers be truthful, reproducible, and not too synthetic:
- Truthful: the run happened as described, including the parts that went badly.
- Reproducible: a reader with your scripts and hardware gets the same shape of result.
- Not too synthetic: the workload resembles something someone actually runs.
Disclose, near the numbers or one click away:
- hardware or instance type
- version of everything compared
- workload and dataset
- configuration and tuning applied to each side
- number of runs and their variance
- a link to the scripts
Carry at least one chart - this pattern is read visually, not as prose.
Give absolute values alongside ratios. "3x faster" hides whether the gap is 3ms versus 1ms or three hours versus one.
Tuning your own side while leaving the comparison at defaults is the specific move that gets a post publicly dismantled. If you cannot tune both sides fairly, say which side is untuned and treat the result as indicative rather than conclusive.
## Trade-offs and limitations
Every post that recommends something owes the reader a section on where it does not apply:
- workloads it is wrong for
- the cost paid for the win
- what remains broken
- what was never tested
Publishing limitations before a reader finds them converts your weakest point into a trust signal.
A comparison table where every row favours you is a tell. Fill at least one row honestly the other way, or drop the table.
## Code in the post
Snippets are the part readers copy, so they carry disproportionate trust:
- Runnable as shown, or explicit about what is elided.
- Imports and setup included; expected output shown.
- Realistic names and values, not `foo`/`bar`.
- One concept per block, roughly twenty lines or fewer - this skill's working limit, not a published standard. Link the repository for the full version.
- Versions pinned and the post dated - a snippet against a since-changed API is worse than no snippet.
## Register
Write as the engineer who did the work:
- first person
- what was hard
- what got cut
- what is still ugly
A post that reads like a press release signals that no engineer was allowed near it. Replace the adjective with the number, then state the boundary of that number - [./references/worked-examples.md](./references/worked-examples.md) has four before/after pairs of exactly that move.
Run [./scripts/scan-draft.sh](./scripts/scan-draft.sh) over the draft. It flags:
- superlatives
- hedge-free absolutes
- unsourced numbers
- undated version references
It is a grep pass, not a judge - review every hit yourself, and keep any it flags when the evidence supports it. On a benchmark-heavy post, add `--no-numbers` once every figure is already in the ledger, or the numeric check drowns the rest.
## Blog context
The craft above is identical everywhere. What changes is the surrounding obligation.
- **Company engineering blog.** Disclose the commercial interest in the post, not in a footer: the product being sold, the open-core boundary, who funded the work. Clear the post with whoever owns security and customer confidentiality when it touches either. Attribute the work to named engineers - an unsigned corporate post starts at a trust deficit.
- **Personal blog.** No disclosure machinery, but state your relationship to what you are writing about (employer, maintainer, competitor) whenever it could look hidden later.
- **Sales-led B2B developer audiences** - a buyer or architect is often evaluating. Name the alternatives, say where they are better, and expect the post to be read by their engineers too.
- **Product-led (PLG) B2B developer audiences** - the reader is a developer who will later champion the product inside a company. The post feeds the developer-champion, not the buyer. Call-to-action and proof-loaded sections land differently: optimise for "try it now" and outcome velocity, not "contact sales".
- **B2C / individual-facing engineering audiences** - the reader is a peer engineer, not a buyer; there is no purchase to influence, so the post lives or dies on engineering substance alone. Resist inserting product marketing into it - the audience came for the system, and the recruiting value is the return.
## Incident-derived posts
Derive a public bug-hunt or outage post from the internal review; never copy it. Run a redaction pass before drafting:
- Remove individual names and handles, internal service codenames, ticket IDs, and wiki links.
- Decide deliberately whether impact figures ship.
- Keep the blameless framing - the post explains what conditions allowed the failure, not who typed the commit.
Then add what no internal document carries: why a reader who was not affected should keep reading.
## Revise, review, ship
Revise in four passes, in this order - the passes are the book's; running them out of order polishes sentences that later get cut:
1. **Core** - facts, focus, flow. Are the facts right, is the post about one thing, does each section earn the next?
2. **Clarity** - can a reader who was not in the room follow it?
3. **Components** - code, images, tables, lists: does each one earn its space?
4. **Consumability** - does it survive being skimmed?
Run the review like a code review - the book's own framing, and the mental model the author already has. Brief the reviewers instead of forwarding the draft:
- Say what you want checked (facts, clarity, or both).
- Give the background they lack.
- Flag any blocker question.
Get two reads, not one:
- An engineer who worked on the system checks accuracy.
- Someone outside the team checks it reads without insider vocabulary.
Real programmes vary less in who writes than in who signs off. Dan Luu's survey of corporate engineering blogs (danluu.com) found the blogs he rated compelling kept that chain short, with one clear owner:
- Heap pairs the engineer-writer with a fellow-engineer "buddy" who edits and approves, then the CTO reads with "usually only minor feedback."
- Segment now has a full-time editor who owns editing; earlier drafts went to the co-founder and an engineering manager. Co-founder Calvin French-Owen: "In general we try to keep it fairly lightweight. I see the bigger problem with blogging being a lack of posts or vague, high level content which isn't interesting rather than revealing too much."
- Cloudflare's CTO, John Graham-Cumming, reads and approves every post directly, with legal turning around in about an hour.
The blogs he rated weaker routed drafts through many stakeholders instead, which left posts vaguer as each approver removed specifics.
A joint post with another organisation multiplies approval chains - start them earlier than feels necessary.
Ship last:
- Preview the post where it will actually appear - code blocks and tables break in the CMS, not in the draft.
- Then handle the metadata: title tag, meta description, URL slug, link targets, image alt text, tags.
## Humanizer pass
Run the prose through your preferred humanizer skill before publishing. Model-flavoured filler reads as inattention to a technical audience and undercuts the evidence work:
- hollow transitions
- uniform sentence rhythm
- significance inflation
- tidy triples
Preserve exactly as written:
- code blocks
- command output
- benchmark tables
- version numbers
- the title
Humanising a title or a snippet damages precision for no gain.
## Credibility gate
Do not hand over a post until all six checks hold. Iterate; report which check failed and what you changed. The six checks and their binary pass rule are this skill's own bar - a design choice, not a published standard - and the skill's only hard threshold.
1. **Claims classified - 100%.** Every assertion is measured, shown, sourced, or marked opinion.
2. **No unsourced numbers.** Every figure traces to a run, a log or a citation. Delete rather than estimate.
3. **Benchmark disclosure complete**, when the post has comparative numbers.
4. **At least one honest limitation** stated, specific enough to be actionable.
5. **Zero unsupported superlatives.** Any "fastest", "seamless", "revolutionary", "best-in-class" is either backed by a measurement in the post or gone.
6. **Snippets pinned and dated**, with the full version linked.
Reach and engagement targets are not set here. No honest public benchmark exists for engineering-blog read-through, so derive targets from the user's own analytics baseline instead.
## Invocation examples
- _"Here are my notes from Tuesday's outage - turn them into a post."_ → draft mode, Bug Hunt, redaction pass first.
- _"We moved the ingest service from Python to Rust, write it up."_ → draft mode, Rewrote It in X; refuse to proceed until the "what went wrong" material exists.
- _"Review this before we publish Thursday."_ → edit mode, fix list, no rewriting.
- _"Make this sound less like marketing."_ → edit mode, register and superlative checks first, then the full gate.
- _"Blog post about our new query planner."_ → likely Non-markety Product Perspectives; ask what an engineer outside the company learns from it.
Draft mode returns, in order:
1. the mode and pattern chosen
2. the claim ledger
3. an outline for approval
4. the drafted post
5. the gate report
6. the handoff list
Edit mode returns the gate report plus a fix list in three tiers: blocking, strong, optional (this skill's own triage scheme).
- Order items by trust damage, not document position.
- Quote the line each item objects to.
See [./references/worked-examples.md](./references/worked-examples.md) for both shapes filled in.
## Failure modes
See [./references/failure-modes.md](./references/failure-modes.md) for a symptom/cause/fix table - load it when a draft, a published post, or a review round is going wrong.
## Measurement
Judge a post on whether the right readers finished it and did something, not on raw traffic:
- Scroll depth and read-through rate against the post's length, not page views alone.
- Referrals from developer surfaces (aggregators, community threads, newsletters) - where technical posts actually travel.
- Click-through to the artefacts the post links: repository, docs, benchmark scripts.
- Qualitative signal: engineers replying with their own numbers, corrections, or "this is the post I needed" messages. One substantive reply outweighs a traffic spike.
- For recruiting-oriented posts, inbound mentions of the post in applications.
Do not attribute revenue to a single post. Overclaiming attribution costs credibility with the exact audience this artefact serves.
## References
- See `samber/developer-relations-skills@developer-quickstart-guide` for first-success getting-started pages.
- See `samber/developer-relations-skills@changelog-writing` for release notes.
- See `samber/developer-relations-skills@version-migration-guide` for upgrade guides.
- See `samber/developer-relations-skills@developer-case-study` for customer stories.
- See `samber/developer-relations-skills@oss-launch` for launch announcements and their channel mechanics.
- See `samber/developer-relations-skills@devrel-content-calendar` for scheduling this post alongside other content.
- See [./references/post-patterns.md](./references/post-patterns.md) for each pattern's section order, proportions and specific traps.
- See [./references/worked-examples.md](./references/worked-examples.md) for a filled claim ledger, a benchmark disclosure block, before/after rewrites of a marketing-voice draft, a limitations section, and an edit-mode fix list.
- See [./references/failure-modes.md](./references/failure-modes.md) for the symptom/cause/fix lookup table.