<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>README — communication</title>
<link>https://readme.news/tags/communication/</link>
<atom:link href="https://readme.news/tags/communication/feed.xml" rel="self" type="application/rss+xml"/>
<description>README pieces tagged communication.</description>
<language>en-us</language>
<lastBuildDate>Thu, 01 Oct 2026 13:20:31 +0000</lastBuildDate>
<item><title>What a good incident channel looks like</title><link>https://readme.news/what-a-good-incident-channel-looks-like/</link><guid isPermaLink="true">https://readme.news/what-a-good-incident-channel-looks-like/</guid><pubDate>Sat, 29 Aug 2026 09:00:00 +0000</pubDate><description>The chat log is where the incident is actually run. Four conventions make it useful rather than a wall of noise.</description><content:encoded><![CDATA[<p>Every incident runs in a chat channel. Almost nobody has thought about how that channel should work, so the default is thirty people speculating in parallel while two people try to fix something.</p>
<p>Four conventions fix most of it, and none of them need a tool.</p>
<h2 id="1-one-channel-created-at-declaration">1. one channel, created at declaration<a class="anchor" href="#1-one-channel-created-at-declaration" aria-label="link to this section">#</a></h2>
<p>Not the team channel. Not a thread in the team channel. A dedicated channel per incident, named predictably: <code>inc-2026-08-29-checkout-errors</code>.</p>
<p>Why it matters: the incident becomes searchable as a unit, the timeline is the channel, and people who join late can read from the top instead of asking what happened. It also means the incident does not drown the normal channel, and the normal channel does not drown the incident.</p>
<p>Archive it afterwards rather than deleting it. It is the primary source for the postmortem.</p>
<h2 id="2-named-roles-stated-out-loud">2. named roles, stated out loud<a class="anchor" href="#2-named-roles-stated-out-loud" aria-label="link to this section">#</a></h2>
<p>Three, at minimum, posted in the channel as the first message:</p>
<div class="code"><pre><code>IC: @priya       — decides, delegates, does not debug
Comms: @marcus   — status page, stakeholders, customer support
Ops: @sam        — hands on keyboard</code></pre></div>
<p>The incident commander not debugging is the rule people resist and the one that matters most. The moment the IC opens a terminal, nobody is tracking the whole picture, and the incident gets longer.</p>
<p>For a small incident one person can hold two roles. They should still say which ones, because the alternative is everyone assuming someone else is doing comms.</p>
<h2 id="3-a-pinned-status-message-edited-in-place">3. a pinned status message, edited in place<a class="anchor" href="#3-a-pinned-status-message-edited-in-place" aria-label="link to this section">#</a></h2>
<p>One message, pinned, rewritten as things change:</p>
<div class="code"><pre><code>STATUS 14:22 — Checkout failing for ~12% of users since 13:58.
Cause: unknown. Suspect the payments deploy at 13:55.
Now: rolling back that deploy (@sam), ETA 5 min.
Impact: card payments only, wallet payments unaffected.
Next update: 14:35</code></pre></div>
<p>Five lines: what is broken, since when, what we think, what we are doing, when the next update comes.</p>
<p>This single convention removes most of the noise, because it answers the question that generates the noise — "what's the current state?" — without anyone having to ask. Everyone joining reads the pin instead of scrolling.</p>
<p><strong>Always include the next-update time.</strong> It is what stops people asking for updates.</p>
<h2 id="4-mark-the-speculation">4. mark the speculation<a class="anchor" href="#4-mark-the-speculation" aria-label="link to this section">#</a></h2>
<p>The most common way incidents go wrong is that a guess gets repeated until it becomes the working theory, and then twenty minutes go into the wrong system.</p>
<p>A one-word convention fixes it:</p>
<div class="code"><pre><code>FACT: error rate went from 0.1% to 12% at 13:58:20
FACT: the payments deploy completed at 13:55:41
GUESS: the deploy caused it
ACTION: rolling back to confirm</code></pre></div>
<p>Facts have evidence attached. Guesses are labelled as guesses. Actions say who is doing them.</p>
<p>It looks pedantic for about four minutes and then it saves the incident, because somebody reading the channel can tell the difference between what is known and what somebody said.</p>
<h2 id="what-to-keep-out">what to keep out<a class="anchor" href="#what-to-keep-out" aria-label="link to this section">#</a></h2>
<p><strong>Speculation from people who are not investigating.</strong> Well-meant and it fills the channel that responders are trying to read. If you are not on a role, watch.</p>
<p><strong>"Is it fixed yet?"</strong> The pinned status has the next update time.</p>
<p><strong>Root-cause analysis during the incident.</strong> Restore first. The good question at 14:22 is "what makes this stop", not "why did this happen". Why is a question for Thursday.</p>
<p><strong>Blame, in any form, including jokes.</strong> It is in a permanent record that a person will read afterwards.</p>
<h2 id="the-handoff">the handoff<a class="anchor" href="#the-handoff" aria-label="link to this section">#</a></h2>
<p>Long incidents cross shift boundaries and the handoff is where context dies. It needs to be explicit and in the channel:</p>
<div class="code"><pre><code>HANDOFF 22:00 — @priya → @dan (IC)
Ruled out: deploy (rolled back, no change), CDN (unaffected regions also failing)
Current theory: connection pool exhaustion on the orders DB
In flight: @sam is capturing pg_stat_activity every 30s → thread above
Not yet tried: failover to replica
Customers: status page updated 21:40, support has the template</code></pre></div>
<p>Ruled out, current theory, in flight, not tried, customer state. Five lines, and the incoming IC starts with the accumulated knowledge instead of rediscovering it.</p>
<h2 id="why-bother">why bother<a class="anchor" href="#why-bother" aria-label="link to this section">#</a></h2>
<p>The channel is not a side effect of the incident. During the incident it is the coordination mechanism, and afterwards it is the only complete record of what was known and when.</p>
<p>A channel that follows these four conventions produces a postmortem that half writes itself, and — more importantly — an incident that ends sooner, because the people fixing it spent their attention on the system rather than on the chat.</p>]]></content:encoded></item><item><title>The unreasonable effectiveness of a changelog</title><link>https://readme.news/the-unreasonable-effectiveness-of-a-changelog/</link><guid isPermaLink="true">https://readme.news/the-unreasonable-effectiveness-of-a-changelog/</guid><pubDate>Wed, 22 Jul 2026 09:00:00 +0000</pubDate><description>A file that takes ten minutes per release and answers most of the questions your users would otherwise ask you.</description><content:encoded><![CDATA[<p>Most projects do not have a changelog. Most projects have a commit log and a release page auto-generated from pull request titles, which is not the same thing and does not serve the same purpose.</p>
<p>A real changelog is a small amount of work with an outsized return.</p>
<h2 id="what-it-is-for">what it is for<a class="anchor" href="#what-it-is-for" aria-label="link to this section">#</a></h2>
<p><strong>Deciding whether to upgrade.</strong> The single most common reason someone reads a changelog. They are on version 3.2, version 3.7 exists, and they want to know whether it is worth the risk.</p>
<p><strong>Knowing what will break.</strong> The most important information you can provide, and the thing auto-generated release notes are worst at.</p>
<p><strong>Debugging.</strong> "This started failing after we upgraded" — a good changelog turns that into "here is the change that caused it" in thirty seconds.</p>
<p><strong>Finding out what exists.</strong> People discover features by reading changelogs. This is a real and underrated distribution channel for your own work.</p>
<h2 id="why-generated-release-notes-are-not-enough">why generated release notes are not enough<a class="anchor" href="#why-generated-release-notes-are-not-enough" aria-label="link to this section">#</a></h2>
<p>A list of merged pull request titles has three problems.</p>
<p><strong>It is written for the wrong audience.</strong> "Refactor connection handling" means something to the maintainer and nothing to the user. What changed <em>for them</em>?</p>
<p><strong>It has no hierarchy.</strong> A breaking change and a typo fix appear as sibling bullets of equal weight.</p>
<p><strong>It has no migration guidance.</strong> "Remove deprecated <code>parse()</code> method" tells you something broke. It does not tell you what to do about it.</p>
<h2 id="the-format">the format<a class="anchor" href="#the-format" aria-label="link to this section">#</a></h2>
<p>Keep a Changelog is the established convention and it is good. The structure:</p>
<div class="code"><span class="code-lang">markdown</span><pre><code class="lang-markdown">## [4.2.0] - 2026-07-22

### Breaking
- `Client.connect()` no longer accepts a positional timeout.
  Pass `timeout=` as a keyword.
      # before
      client.connect(host, 30)
      # after
      client.connect(host, timeout=30)

### Added
- `Client.ping()` for health checks without a full round trip (#412)
- Support for Unix domain sockets via `unix://` URLs (#398)

### Fixed
- Connections leaked when the handshake timed out (#405).
  If you saw file descriptor exhaustion under load, this was it.

### Deprecated
- `Client.legacy_mode` — will be removed in 5.0. Use `compatibility=`.

### Security
- Fixed a case where credentials could appear in debug logs (GHSA-xxxx-xxxx).
  Affects 4.0.0–4.1.3. Rotate credentials if debug logging was enabled.</code></pre></div>
<h2 id="the-rules-that-make-it-useful">the rules that make it useful<a class="anchor" href="#the-rules-that-make-it-useful" aria-label="link to this section">#</a></h2>
<p><strong>Breaking changes first, always.</strong> That is what people are scanning for. Do not bury them under twelve feature bullets.</p>
<p><strong>Include the migration.</strong> A breaking change without "do this instead" makes the reader open your source code. Two lines of before-and-after saves everyone time.</p>
<p><strong>Describe the user-visible effect, not the implementation.</strong> Not "refactored the retry logic." Rather: "<a class="xref" href="/retries-a-complete-guide-to-not-making-it-worse/" title="Retries: a complete guide to not making it worse">retries</a> now use exponential backoff with jitter; if you relied on the previous fixed 1-second interval, set <code>retry_delay=1.0</code>."</p>
<p><strong>Say who is affected.</strong> "If you use X, this changes for you. Otherwise nothing changes." Most readers can then stop reading, which is a service.</p>
<p><strong>Link to the issue or pull request</strong> for anyone who wants detail. The changelog is a summary, not a substitute.</p>
<p><strong>Date every release</strong>, in ISO format. Version numbers alone do not tell you whether you are two months or three years behind.</p>
<p><strong>Write it as you go</strong>, not at release time. An <code>Unreleased</code> section at the top that each pull request adds to. Reconstructing a changelog from git history at release time is miserable and it is why changelogs get skipped.</p>
<h2 id="the-security-section-specifically">the security section specifically<a class="anchor" href="#the-security-section-specifically" aria-label="link to this section">#</a></h2>
<p>If you fix a security issue, say so, with:</p>
<ul><li>Which versions are affected.</li><li>What the impact is.</li><li>Whether any action beyond upgrading is required.</li></ul>
<p>That last one is the part that gets omitted and it is critical. "Upgrade to 4.2.0" is insufficient if credentials may have been exposed — the user also needs to rotate them, and they will not know unless you say so.</p>
<h2 id="for-internal-projects">for internal projects<a class="anchor" href="#for-internal-projects" aria-label="link to this section">#</a></h2>
<p>The same file works for internal services, and the audience is your future self and the person who takes over the service.</p>
<p>The most valuable internal changelog entries are the ones that record a decision:</p>
<div class="code"><span class="code-lang">markdown</span><pre><code class="lang-markdown">## 2026-07-14
- Switched from polling to webhooks for order status. Polling was
  costing ~40 requests/second against the vendor's rate limit and
  we were getting throttled during peaks.</code></pre></div>
<p>Six months later, when someone asks why there is webhook infrastructure, the answer is in one place.</p>
<h2 id="the-return-on-investment">the return on investment<a class="anchor" href="#the-return-on-investment" aria-label="link to this section">#</a></h2>
<p>Ten minutes per release. In exchange:</p>
<ul><li>Fewer support questions.</li><li>Faster upgrades by your users, which means fewer people on old versions you have to support.</li><li>Fewer surprised users after a breaking change.</li><li>A record you can search when debugging.</li></ul>
<p>There are not many ten-minute tasks with that profile.</p>]]></content:encoded></item><item><title>Code review comments that change things</title><link>https://readme.news/code-review-comments-that-change-things/</link><guid isPermaLink="true">https://readme.news/code-review-comments-that-change-things/</guid><pubDate>Mon, 06 Jul 2026 09:00:00 +0000</pubDate><description>Most review comments are noise or nitpicks. A small taxonomy of the ones that are worth writing.</description><content:encoded><![CDATA[<p>Most code review comments do not change the code, and of the ones that do, most change something that did not matter.</p>
<p>Here is a taxonomy of comments worth writing, roughly in descending order of value.</p>
<h2 id="the-ones-worth-writing">the ones worth writing<a class="anchor" href="#the-ones-worth-writing" aria-label="link to this section">#</a></h2>
<p><strong>"This will break when X."</strong> The highest-value comment there is. A specific failure scenario the author did not consider.</p>
<blockquote><p>"If two requests hit this concurrently, both will pass the existence check and both will insert. We saw this exact bug in the invoicing path last year."</p></blockquote>
<p>Concrete, falsifiable, and it comes with evidence.</p>
<p><strong>"This contradicts how we do it elsewhere."</strong> Consistency has real value and the author frequently does not know the precedent exists.</p>
<blockquote><p>"<code>orders/</code> uses the repository pattern for this. Worth matching, or is there a reason to differ here?"</p></blockquote>
<p>Note the question at the end. Sometimes there is a reason and you have just learned something.</p>
<p><strong>"I don't understand this."</strong> Underrated. If a reviewer with context cannot follow it, a stranger in two years will not either.</p>
<p>This is not an admission of inadequacy. It is a measurement of the code's clarity, and it is a measurement only a reader can take.</p>
<p><strong>"What happens if this fails?"</strong> The most consistently productive question in code review. Error paths are the least-considered part of most changes and the most-exercised part in production.</p>
<p><strong>"Is this the right layer?"</strong> Business logic in a controller, presentation logic in a model, a database call in a template. Structural, cheap to fix now, expensive later.</p>
<p><strong>"This is good and here is why."</strong> Genuinely valuable and almost never written. Naming what worked teaches the pattern and it makes the critical comments land better, because they arrive from someone who is paying attention rather than someone who is looking for problems.</p>
<h2 id="the-ones-not-worth-writing">the ones not worth writing<a class="anchor" href="#the-ones-not-worth-writing" aria-label="link to this section">#</a></h2>
<p><strong>Anything a formatter or linter handles.</strong> If you are commenting on spacing, quotes, or import order, fix your tooling instead. A human enforcing mechanical rules is a broken process.</p>
<p><strong>Style preferences without a reason.</strong> "I would have used a map here" is not a review comment, it is a preference. If there is a reason — clarity, performance, consistency with a convention — say the reason. If there is not, do not send it.</p>
<p><strong>Speculative generality.</strong> "What if we later need to support multiple currencies?" Usually they will not, and building for it costs now. If you genuinely believe it, say what makes you believe it.</p>
<p><strong>A redesign in a review comment.</strong> If the approach is fundamentally wrong, that is a conversation, not a comment thread. Comments are for improving an approach; a different approach needs a discussion, and doing it in review comments after the work is done is the most expensive possible time.</p>
<p>That failure is on the process, not the reviewer: the design should have been discussed before implementation.</p>
<h2 id="how-to-phrase-things">how to phrase things<a class="anchor" href="#how-to-phrase-things" aria-label="link to this section">#</a></h2>
<p><strong>Distinguish blocking from non-blocking.</strong> A convention that removes an enormous amount of ambiguity:</p>
<div class="code"><pre><code>blocking: this deletes rows without the tenant filter
suggestion: this could use the existing helper in utils/dates
question: is the retry here intentional given the caller already retries?
nit: typo in the comment
praise: nice — this handles the empty case correctly, which the old one did not</code></pre></div>
<p>The author knows exactly what to act on. The reviewer can leave a thought without implying it must be addressed. Both people save time.</p>
<p><strong>Ask rather than assert when you are unsure.</strong> "Why does this need a lock?" is better than "this does not need a lock" when you are not certain, and it is better even when you are, because the answer might teach you something about the system.</p>
<p><strong>Explain the why, not just the what.</strong> "Use a set here" is an instruction. "Use a set here — this is O(n²) on a list that can have thousands of entries" is teaching, and the author will apply it next time without being told.</p>
<p><strong>Comment on the code, not the person.</strong> "This is confusing" rather than "you wrote this confusingly." Small difference, and it consistently changes how the comment is received.</p>
<h2 id="the-process-problems-that-comments-cannot-fix">the process problems that comments cannot fix<a class="anchor" href="#the-process-problems-that-comments-cannot-fix" aria-label="link to this section">#</a></h2>
<p><strong>The diff is too large.</strong> Past a few hundred lines, review quality collapses. The comment "this PR is too large, can you split it" is the most valuable one available and it is socially costly to write, which is why nobody does.</p>
<p>Make it a policy so it is not a personal judgment.</p>
<p><strong>Review is too late.</strong> If a fundamental problem is found in review, the process failed earlier. That belongs in design.</p>
<p><strong>Only one person reviews.</strong> Different reviewers see different things. For anything significant, two, with different backgrounds.</p>
<p><strong>Comments arrive over three days.</strong> The author has moved on and has to reload the entire context. Batch your review into one pass, and do it within a day.</p>
<h2 id="the-goal">the goal<a class="anchor" href="#the-goal" aria-label="link to this section">#</a></h2>
<p>The purpose of code review is not to find bugs — tests find bugs more reliably and more cheaply.</p>
<p>It is to spread understanding, maintain coherence, and catch the class of problem that automation cannot see: wrong abstraction, wrong layer, missing case, code that will confuse the next reader.</p>
<p>Comments that serve those are worth writing. Everything else is noise with a notification attached.</p>]]></content:encoded></item><item><title>What a great bug report contains</title><link>https://readme.news/what-a-great-bug-report-contains/</link><guid isPermaLink="true">https://readme.news/what-a-great-bug-report-contains/</guid><pubDate>Sat, 30 May 2026 09:00:00 +0000</pubDate><description>Six fields. Most reports have two. The difference is measured in days of engineering time.</description><content:encoded><![CDATA[<p>A bug report's job is to get someone to the reproduction as fast as possible. Everything else is decoration.</p>
<p>Most reports fail at this, and the cost is enormous — a round trip asking for information takes hours or days, during which the bug is not being fixed and the reporter is not being helped.</p>
<h2 id="the-six-fields">the six fields<a class="anchor" href="#the-six-fields" aria-label="link to this section">#</a></h2>
<p><strong>1. What you did.</strong> The exact steps. Not "I tried to log in" — the sequence, including things that seem irrelevant. The irrelevant thing is frequently the cause.</p>
<p><strong>2. What you expected.</strong> This seems redundant and is not. A meaningful fraction of bug reports are misunderstandings, and stating the expectation surfaces that immediately. It also catches the case where the software is working as designed and the design is wrong, which is a different and often more important bug.</p>
<p><strong>3. What actually happened.</strong> Specifically. The exact error text, copied, not paraphrased and not described. Screenshots for visual issues. The full stack trace, not the last line.</p>
<p><strong>4. Environment.</strong> Version of the software, operating system, browser, region, account type, relevant <a class="xref" href="/feature-flags-and-the-state-space-nobody-tests/" title="Feature flags and the state space nobody tests">feature flags</a>. Half of all "cannot reproduce" outcomes are environment differences.</p>
<p><strong>5. Frequency.</strong> Every time? Once? Intermittently? This changes the debugging approach completely — a deterministic bug is a logic error, an intermittent one is usually concurrency, <a class="xref" href="/caching-is-the-only-optimization-that-reliably-works/" title="Caching is the only optimization that reliably works">caching</a>, or state.</p>
<p><strong>6. An identifier.</strong> Request ID, trace ID, session ID, timestamp with timezone, account ID. This is what lets an engineer find the actual event in the logs, and it converts "I have to reproduce this" into "I can look at exactly what happened."</p>
<p>That last one is the highest-leverage field and the one most often absent, because most products do not surface an identifier to the user.</p>
<p><strong>If you build software: put a request ID on your error pages.</strong> It costs nothing and it is the difference between a bug report you can act on and one you cannot.</p>
<h2 id="the-title">the title<a class="anchor" href="#the-title" aria-label="link to this section">#</a></h2>
<p>The title is read by dozens of people and determines whether the right person opens it.</p>
<p>Bad: "Login broken" Better: "Login fails with 500 for SSO users after password reset"</p>
<p>The pattern: <strong>what fails, for whom, under what condition.</strong></p>
<h2 id="what-to-leave-out">what to leave out<a class="anchor" href="#what-to-leave-out" aria-label="link to this section">#</a></h2>
<p><strong>Your theory about the cause</strong>, unless you have evidence. A confident wrong theory sends the investigation in the wrong direction, and reports with a theory get debugged less carefully because the reader anchors on it.</p>
<p>If you do have a theory, put it at the bottom, labeled as speculation.</p>
<p><strong>Emotional content.</strong> "This is completely broken and unacceptable" adds nothing and makes the reader defensive. The severity is communicated by the impact description, not by adjectives.</p>
<p><strong>Multiple bugs in one report.</strong> One issue per report. Multi-bug reports get partially fixed and closed, and the remaining bugs are lost.</p>
<h2 id="the-minimal-reproduction">the minimal reproduction<a class="anchor" href="#the-minimal-reproduction" aria-label="link to this section">#</a></h2>
<p>If you can produce one, it is worth ten times the rest of the report combined.</p>
<p>The technique: start with the failing case and remove things until it stops failing. The last thing you removed is involved.</p>
<p>This is work, and it is work that would otherwise be done by someone with less context than you. A minimal reproduction frequently reveals the cause to the reporter before they finish making it.</p>
<h2 id="the-template">the template<a class="anchor" href="#the-template" aria-label="link to this section">#</a></h2>
<p>Put this in your issue template. Teams that do see a measurable improvement in report quality, because most people will fill in the fields you give them and will not invent them:</p>
<div class="code"><span class="code-lang">markdown</span><pre><code class="lang-markdown">**What I did**
1.
2.
3.

**Expected**

**Actual**
(exact error text, full stack trace, screenshot)

**Environment**
- Version:
- OS / Browser:
- Account / Region:

**Frequency**
Always / Sometimes / Once

**Identifiers**
Request ID:
Timestamp (with timezone):</code></pre></div>
<h2 id="for-the-person-receiving-it">for the person receiving it<a class="anchor" href="#for-the-person-receiving-it" aria-label="link to this section">#</a></h2>
<p>The other half of this.</p>
<p><strong>Reproduce before you theorize.</strong> The single most common debugging failure is building a mental model from the description and investigating that model instead of the actual behavior.</p>
<p><strong>Ask for the missing field specifically.</strong> "Can you send the request ID from the error page?" gets an answer. "Can you provide more details?" does not.</p>
<p><strong>Close the loop.</strong> Tell the reporter what it was. This costs a sentence and it is the reason people file good reports next time — a report that vanishes into a backlog teaches people not to bother.</p>
<h2 id="the-meta-point">the meta-point<a class="anchor" href="#the-meta-point" aria-label="link to this section">#</a></h2>
<p>A bug report is a handoff of context between two people, one of whom has seen the failure and one of whom has to fix it.</p>
<p>Everything in a good report is in service of moving context across that gap. Everything that does not move context across the gap is noise.</p>]]></content:encoded></item><item><title>Design docs people actually read</title><link>https://readme.news/design-docs-people-actually-read/</link><guid isPermaLink="true">https://readme.news/design-docs-people-actually-read/</guid><pubDate>Wed, 18 Mar 2026 09:00:00 +0000</pubDate><description>The average design doc is eleven pages and gets three comments, all on page one. Here&#x27;s the format that works.</description><content:encoded><![CDATA[<p>Most design documents are written to satisfy a process and read by nobody. You can tell because the comments cluster in the first two pages and stop.</p>
<p>A design doc that works has a different shape, and the shape follows from being honest about why you are writing it.</p>
<h2 id="why-you-are-actually-writing-it">why you are actually writing it<a class="anchor" href="#why-you-are-actually-writing-it" aria-label="link to this section">#</a></h2>
<p>Three reasons, and they want different documents:</p>
<p><strong>To think.</strong> Writing forces you to discover that your plan has a hole. This version is for you and can be messy.</p>
<p><strong>To get input before it is expensive.</strong> The highest-value function. You want the person who knows the thing you do not know to tell you before you build.</p>
<p><strong>To create a record.</strong> So that in two years, someone can find out why.</p>
<p>Most templates optimize for the third and produce documents that fail at the second. The record is only worth having if the decision was good, and the decision is good because of the input.</p>
<h2 id="the-format">the format<a class="anchor" href="#the-format" aria-label="link to this section">#</a></h2>
<p>One page. Two if the problem genuinely needs it. Structured so a reader can stop after any section and have gotten something.</p>
<p><strong>Problem.</strong> What is wrong right now, with evidence. Not "we should improve X" — what breaks, how often, who it affects. If you cannot state the problem concretely, the design is premature.</p>
<p><strong>Goals and non-goals.</strong> The non-goals are the important half and almost always missing. "This does not address the reporting pipeline" prevents forty comments about the reporting pipeline.</p>
<p><strong>Proposal.</strong> What you are going to build, in enough detail to disagree with. Interfaces, data shapes, the sequence of operations. A diagram if the shape is not obvious in prose.</p>
<p><strong>Alternatives considered.</strong> Two or three, each with one honest sentence about why not. This is the section that gets the good comments, because it is where a reader sees the option you dismissed for a reason they know is wrong.</p>
<p>If your alternatives section contains obvious strawmen, experienced readers will notice and discount the whole document.</p>
<p><strong>Risks and open questions.</strong> What might go wrong. What you are unsure about. This is where you ask for help explicitly, and it is where the people who know things will engage.</p>
<p><strong>Rollout.</strong> How it ships, how it is verified, how it is rolled back. A design without a rollback plan is not finished.</p>
<p>That is it. Under a thousand words for most things.</p>
<h2 id="the-things-that-make-people-not-read-it">the things that make people not read it<a class="anchor" href="#the-things-that-make-people-not-read-it" aria-label="link to this section">#</a></h2>
<p><strong>Length.</strong> Past about two pages, readership drops sharply. If you need more, put the detail in an appendix and keep the main document short.</p>
<p><strong>Burying the proposal.</strong> If the reader has to get through three pages of background before learning what you want to do, they will not.</p>
<p><strong>No clear ask.</strong> Say what you want. "I need someone who knows the payment path to confirm the <a class="xref" href="/idempotency-is-the-only-distributed-systems-concept-you-need/" title="Idempotency is the only distributed systems concept you need">idempotency</a> assumption in section 3" gets a response. "Please review" does not.</p>
<p><strong>Passive voice throughout.</strong> "It was determined that" hides who decided and why. It also reads as evasive, because it usually is.</p>
<p><strong>No deadline.</strong> "Comments by Thursday, I will start Friday" produces comments by Thursday. An open-ended review produces comments never.</p>
<h2 id="the-review-that-works">the review that works<a class="anchor" href="#the-review-that-works" aria-label="link to this section">#</a></h2>
<p><strong>Ask specific people specific questions.</strong> Not "posting this to the channel." Three named people, each with a sentence about why you want their input.</p>
<p><strong>Give a real window.</strong> Three to five days. Long enough to fit into someone's week, short enough to stay on the top of the pile.</p>
<p><strong>Timebox the meeting, if there is one.</strong> For a contentious design, a 30-minute discussion after people have read it resolves more than two weeks of comments. For an uncontentious one, do not have a meeting.</p>
<p><strong>Record the decision at the top when it is made.</strong> A resolved design doc should start with "Decision: we are doing X, decided 2026-03-18, because Y." The person finding this in 2028 wants that sentence and nothing else.</p>
<h2 id="the-thing-that-makes-them-worth-writing">the thing that makes them worth writing<a class="anchor" href="#the-thing-that-makes-them-worth-writing" aria-label="link to this section">#</a></h2>
<p>The value is not the document. It is that you discovered the flaw while writing rather than while implementing, and that someone who knew something told you before you had built the wrong thing.</p>
<p>If you write a design doc and nothing changes as a result, either your design was already right — possible, uncommon — or nobody engaged, and the process is theater.</p>
<p>Measure it that way: how many of your design docs changed the design? If the answer is none, stop writing them in this format and figure out what would actually get you input.</p>]]></content:encoded></item><item><title>The engineer's guide to saying no</title><link>https://readme.news/the-engineers-guide-to-saying-no/</link><guid isPermaLink="true">https://readme.news/the-engineers-guide-to-saying-no/</guid><pubDate>Sat, 28 Feb 2026 09:00:00 +0000</pubDate><description>Refusing work badly is a career problem. Refusing it well is one of the most valuable things a senior engineer does.</description><content:encoded><![CDATA[<p>Most engineers are bad at saying no. They either cannot do it — and end up with a commitment they cannot meet — or they do it in a way that reads as obstruction, and get routed around.</p>
<p>Both failures come from the same mistake: treating "no" as a verdict rather than as the opening of a conversation about trade-offs.</p>
<h2 id="what-the-request-actually-is">what the request actually is<a class="anchor" href="#what-the-request-actually-is" aria-label="link to this section">#</a></h2>
<p>When someone asks you to build something, they are not asking for the thing. They are asking for an outcome, and the thing is their guess at how to get it.</p>
<p>That means the highest-value response is frequently not yes or no. It is a better guess.</p>
<blockquote><p>"You are asking for a real-time <a class="xref" href="/the-dashboard-nobody-looks-at/" title="The dashboard nobody looks at">dashboard</a>. What decision are you going to make from it? If it is 'should we page someone,' an alert is better than a dashboard and it is a day of work instead of a month."</p></blockquote>
<p>That is a no to the request and a yes to the outcome, and nobody experiences it as obstruction.</p>
<p>Ask what the outcome is before you evaluate the request. Half the time the request evaporates.</p>
<h2 id="the-four-honest-noes">the four honest noes<a class="anchor" href="#the-four-honest-noes" aria-label="link to this section">#</a></h2>
<p><strong>"Not this, that."</strong> The alternative approach that achieves the same outcome for less. This is the best one and it requires actually understanding the problem.</p>
<p><strong>"Yes, and here is what it displaces."</strong> Not a no. A statement about capacity, which is factual and is the other person's decision to make.</p>
<blockquote><p>"I can do that in this cycle. It means the API migration slips to next quarter. Which do you want?"</p></blockquote>
<p>This is enormously more effective than "we do not have time," because it hands the prioritization decision to the person whose job it is, with the information they need.</p>
<p><strong>"Yes, after X."</strong> A sequencing objection. "We can build this on top of the new data model. Building it on the old one means we build it twice."</p>
<p><strong>"No, and here is the risk I am flagging."</strong> The real no, reserved for correctness, security, legal, or ethical problems. Use it rarely so that it lands when you do.</p>
<blockquote><p>"I am not going to implement this the way it is specified because it stores plaintext credentials. I will implement it with a token exchange, which takes three extra days. If that is unacceptable, I want the decision documented and made by someone who can accept the risk."</p></blockquote>
<p>That is a hard sentence to say and it is the sentence the job sometimes requires.</p>
<h2 id="the-ones-that-do-not-work">the ones that do not work<a class="anchor" href="#the-ones-that-do-not-work" aria-label="link to this section">#</a></h2>
<p><strong>"That is not possible."</strong> Almost always false, and the person will find someone who says it is. Say "that would take six months" instead, which is the real constraint and is checkable.</p>
<p><strong>"That is a bad idea."</strong> Without an alternative, this is just friction.</p>
<p><strong>Silence.</strong> Not responding is a no that damages trust, because it looks like you did not care rather than that you disagreed.</p>
<p><strong>"Sure"</strong> followed by not doing it. The worst one. It destroys your reliability, which is the only currency you actually have.</p>
<h2 id="the-timing">the timing<a class="anchor" href="#the-timing" aria-label="link to this section">#</a></h2>
<p>Say no early. The cost of a no rises with every day of planning that assumed a yes.</p>
<p>An objection raised in the design review is a discussion. The same objection raised two weeks before launch is a crisis, and people will remember that you could have said it earlier — correctly.</p>
<p>If you have doubts, voice them while they are cheap.</p>
<h2 id="the-part-about-capital">the part about capital<a class="anchor" href="#the-part-about-capital" aria-label="link to this section">#</a></h2>
<p>Every no spends something. Every yes earns something. If you never say yes, your noes stop landing, because you have become the person who says no.</p>
<p>Deliver reliably on what you agree to. That is what makes the refusal credible when it matters. Engineers who are trusted to ship get enormous latitude to push back, and engineers who are not, do not — regardless of whether they are right.</p>
<h2 id="the-hardest-case">the hardest case<a class="anchor" href="#the-hardest-case" aria-label="link to this section">#</a></h2>
<p>Sometimes you are overruled on something you believe is wrong, and it is not a correctness or ethics issue — it is a judgment call and someone with the authority made a different one.</p>
<p>Disagree and commit is the right practice here and it is genuinely hard. Say your piece once, clearly, in writing. Then build the thing well.</p>
<p>Do not build it badly to prove a point. Do not relitigate it in every standup. Do not say "I told you so" if it goes wrong — the written record already said it, and gloating costs you the ability to be listened to next time.</p>
<p>You will be wrong about some of these. That is the actual reason to commit gracefully: you are not always right, and a culture where disagreement is followed by good-faith execution is one where being wrong is survivable for everyone, including you.</p>]]></content:encoded></item>
</channel>
</rss>
