<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ben Balter</title><description>Engineering leadership, open source, and showing your work</description><link>https://ben.balter.com/</link><item><title>It&apos;s pronounced JIF. The Open and Async audiobook is out.</title><link>https://ben.balter.com/2026/09/08/gif-or-jif-audiobook-both-ways/</link><guid isPermaLink="true">https://ben.balter.com/2026/09/08/gif-or-jif-audiobook-both-ways/</guid><description>The Open and Async audiobook is out. It&apos;s read by a synthetic narrator who says &apos;jif,&apos; correctly. For the hard-G crowd who insist that&apos;s heresy, I re-recorded an entire chapter in their (wrong) pronunciation and posted it free. One chapter the wrong way, or eleven and a half hours the right way.</description><pubDate>Tue, 08 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/09/08/gif-or-jif-audiobook-both-ways/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The &lt;em&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-gif-jif-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Open and Async&lt;/a&gt;&lt;/em&gt; audiobook is out. It’s read by a synthetic voice, which means that at some point a machine had to decide how to say GIF, with no help from me. It picked “jif.” Correctly. I have never been prouder of a robot.&lt;/p&gt;
&lt;p&gt;In a bit, you’ll hear the audiobook both ways, the same line read by the same robot, and you can settle the argument yourself. Headphones on.&lt;/p&gt;
&lt;h2 id=&quot;the-fight-only-exists-out-loud&quot;&gt;The fight only exists out loud&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-fight-only-exists-out-loud&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GIF-versus-JIF is the one internet argument that text is structurally incapable of settling. You can go your whole career typing “GIF” and never once reveal which side you’re on, because the letters look identical in both camps. Type “actually, it’s pronounced jif” into a thread until your keyboard wears out, and nobody hears a thing. The fight only exists in sound. Record audio and it comes for you, wanting an answer before the next sentence. That’s how the audiobook became the first version of this book that could even &lt;em&gt;have&lt;/em&gt; the argument. It took my side without being asked.&lt;/p&gt;
&lt;p&gt;For the record: soft G. It’s “jif,” like the peanut butter.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-wilhite&quot; id=&quot;user-content-fnref-wilhite&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; Reasonable, thoughtful, wrong people say it with a hard G. We work together anyway.&lt;/p&gt;
&lt;style&gt;
  .post-gif { max-width: 15rem; margin: 0 0 1rem; }
  .post-gif video { display: block; width: 100%; height: auto; border-radius: .5rem; }
  .post-gif figcaption { margin-top: .5rem; font-size: .9rem; opacity: .75; }
  @media (min-width: 640px) {
    .post-gif--right { float: right; max-width: 18rem; margin: .25rem 0 1rem 1.5rem; }
    .post-gif--left  { float: left;  max-width: 13rem; margin: .25rem 1.5rem 1rem 0; }
  }
&lt;/style&gt;
&lt;figure class=&quot;not-prose post-gif post-gif--right&quot;&gt;
  &lt;video width=&quot;480&quot; height=&quot;270&quot; autoplay muted loop playsinline preload=&quot;metadata&quot; poster=&quot;/video/gif-jif/that-escalated-quickly.jpg&quot; aria-label=&quot;Ron Burgundy in Anchorman saying &amp;#x27;boy, that escalated quickly&amp;#x27;&quot;&gt;
    &lt;source src=&quot;/video/gif-jif/that-escalated-quickly.webm&quot; type=&quot;video/webm&quot;&gt;
    &lt;source src=&quot;/video/gif-jif/that-escalated-quickly.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;Case in point. A paragraph can&apos;t do this.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There’s a chapter in the book about using chat well, and a section in it about emoji and animated GIFs being a kind of body language for text. A well-placed “that escalated quickly” carries something a paragraph can’t. The book already argues, in print, that GIFs matter. It just never had to say the word out loud. Then I made an audiobook, and it did, eleven times, in one chapter.&lt;/p&gt;
&lt;h2 id=&quot;i-recorded-it-wrong-on-purpose&quot;&gt;I recorded it wrong on purpose&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#i-recorded-it-wrong-on-purpose&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote a whole book about working in the open, though, and the open-and-async answer to a disagreement is not to win the thread. It’s to &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;ship the artifact and let people decide&lt;/a&gt;. I built the most literal version of that I could think of. I re-recorded the entire chapter with the narrator forced to say hard-G “gif,” beginning to end, and posted it, free, right next to the correct one. An entire edition in the wrong pronunciation, rendered for the heretics to enjoy their own wrongness. Same words, same narrator, one &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;The smallest unit of sound that distinguishes one word from another—like the soft &amp;#x22;j&amp;#x22; and hard &amp;#x22;g&amp;#x22; that split JIF from GIF&quot; title=&quot;The smallest unit of sound that distinguishes one word from another—like the soft &amp;#x22;j&amp;#x22; and hard &amp;#x22;g&amp;#x22; that split JIF from GIF&quot; tabindex=&quot;0&quot;&gt;phoneme&lt;/abbr&gt; of difference. Pick your fighter. Mine already won.&lt;/p&gt;
&lt;figure class=&quot;not-prose post-gif post-gif--left&quot;&gt;
  &lt;video width=&quot;480&quot; height=&quot;360&quot; autoplay muted loop playsinline preload=&quot;metadata&quot; poster=&quot;/video/gif-jif/keyboard-cat.jpg&quot; aria-label=&quot;Keyboard Cat, an orange cat in a blue shirt playing an electronic keyboard&quot;&gt;
    &lt;source src=&quot;/video/gif-jif/keyboard-cat.webm&quot; type=&quot;video/webm&quot;&gt;
    &lt;source src=&quot;/video/gif-jif/keyboard-cat.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;The cat in question, for the record.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Ten seconds each, the same line both ways: “an urgent message from your manager gets the same screen space as a GIF of a cat playing the piano.”&lt;/p&gt;
&lt;div class=&quot;not-prose&quot; style=&quot;display:flex;flex-wrap:wrap;gap:1.5rem;margin:1.75rem 0&quot;&gt;
  &lt;figure style=&quot;flex:1 1 15rem;margin:0&quot;&gt;
    &lt;figcaption style=&quot;margin-bottom:.4rem&quot;&gt;&lt;strong&gt;Team JIF&lt;/strong&gt; (the correct one)&lt;/figcaption&gt;
    &lt;audio controls preload=&quot;none&quot; style=&quot;width:100%&quot; aria-label=&quot;Chat chapter sample line, narrated with the soft-G &amp;#x27;jif&amp;#x27; pronunciation&quot;&gt;
      &lt;source src=&quot;/audio/gif-jif/jif-cat-line.mp3&quot; type=&quot;audio/mpeg&quot;&gt;
    &lt;/audio&gt;
  &lt;/figure&gt;
  &lt;figure style=&quot;flex:1 1 15rem;margin:0&quot;&gt;
    &lt;figcaption style=&quot;margin-bottom:.4rem&quot;&gt;&lt;strong&gt;Team hard-G&lt;/strong&gt; (for the heretics)&lt;/figcaption&gt;
    &lt;audio controls preload=&quot;none&quot; style=&quot;width:100%&quot; aria-label=&quot;Chat chapter sample line, narrated with the hard-G &amp;#x27;gif&amp;#x27; pronunciation&quot;&gt;
      &lt;source src=&quot;/audio/gif-jif/gif-hardg-cat-line.mp3&quot; type=&quot;audio/mpeg&quot;&gt;
    &lt;/audio&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;div style=&quot;clear:both&quot;&gt;&lt;/div&gt;
&lt;p&gt;&lt;a id=&quot;quote-the-gif-is-the-point&quot; href=&quot;#quote-the-gif-is-the-point&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;the-gif-is-the-point&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The pronunciation was never the point. The GIF is.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That’s the point hiding inside the bit. The whole reason the chapter defends GIFs is that the shared reference does the work. Nobody who gets the joke has ever cared how you say the three letters. Arguing about the phoneme is bikeshedding the one part of the thing that carries no meaning at all. It’s impact over input, applied to a debate about peanut butter.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/open-and-async/feedback/releases/tag/audiobook-gif-jif&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Both cuts&lt;/a&gt; live on the book’s feedback repository, which is where readers tell me what’s broken, what’s missing, and what actually landed.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-bug-on-purpose&quot; href=&quot;#quote-bug-on-purpose&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;bug-on-purpose&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Consider the hard-G edition a bug I shipped on purpose, a public service to the people who need to hear it.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-the-robot-narrator&quot;&gt;About the robot narrator&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#about-the-robot-narrator&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;People kept asking for an audiobook, to listen at the gym or on a commute, and I’m a big audiobook fan myself. I did some of my best listening on parental leave, one hand on a stroller. I wanted the book to reach people that way too. What I didn’t have was a way to pay for it. A human narration doesn’t pencil out for me right now, not a professional’s four-figure, months-long project, and not the fifty-plus hours it would take me to read a hundred thousand words myself, badly. That left a synthetic narrator, and I’d rather say so straight than have you notice it later. No human voice actor, no studio.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-synthetic-shortcut&quot; href=&quot;#quote-synthetic-shortcut&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;synthetic-shortcut&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Synthetic is the shortcut, and I’m not going to pretend it’s anything else.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Something real is lost with it. A good narrator doesn’t just say the words, they perform them: the timing, the dry aside, the breath before a hard sentence. My robot reads cleanly. It isn’t an actor, and if you’ve heard a great human narration, you’ll feel the difference. The bigger cost lands past my one book. Synthetic narration takes work from the people who do this for a living, and “it was cheaper for me” is exactly the logic that adds up to their livelihoods shrinking. I don’t have a clean answer to that.&lt;/p&gt;
&lt;p&gt;A human narrator for this book is the goal, not a synthetic stand-in forever. If it earns its way there, it’ll get one. Until then, imperfect and shipped beats perfect and imaginary. Ship early, ship often.&lt;/p&gt;
&lt;p&gt;Here’s where that leaves me: the voice is disclosed, it isn’t cloned from a real person, and a human (me) listened to all eleven and a half hours before it shipped.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-qa&quot; id=&quot;user-content-fnref-qa&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; Machine-narrated, human-checked. If a synthetic voice is a hard pass for you, I get it, and the ebook and paperback let you hear “jif” in your own head, at your own pace.&lt;/p&gt;
&lt;h2 id=&quot;get-the-audiobook&quot;&gt;Get the audiobook&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#get-the-audiobook&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That &lt;a href=&quot;https://github.com/open-and-async/feedback/releases/tag/audiobook-gif-jif&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;free chapter&lt;/a&gt; is a free sample, and I mean that literally. If you want the same synthetic narrator to read you the whole book, saying “jif” correctly the entire time, the &lt;a href=&quot;https://open-and-async.com/go/audiobook?src=gif-jif-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;full audiobook&lt;/a&gt; is out now. Eleven and a half hours of it, and not one hard G.&lt;/p&gt;
&lt;p&gt;I am not going to win this argument. Nobody wins this argument. That’s exactly why you hand people the choice instead of typing one more reply, then go build the next thing. Even if one of the choices is objectively incorrect.&lt;/p&gt;
&lt;p&gt;The right edition is the whole audiobook. The other one is just me being generous to the wrong ones.&lt;/p&gt;
&lt;p&gt;So, which are you? Choose carefully. My narrator already did.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-wilhite&quot;&gt;
&lt;p&gt;Steve Wilhite created the format at CompuServe in 1987, and when he accepted a lifetime achievement award he stood on a stage and said “it’s pronounced JIF.” The man who invented the thing gets to name it, and the hard-G crowd is out here overruling him. “But &lt;em&gt;graphics&lt;/em&gt; has a hard G” is not the counterargument they think it is. It’s an acronym, not a word, and the inventor already ruled. Yes, there’s the peanut butter. Yes, I’m on that side too. I have made my peace with being right. &lt;a href=&quot;#user-content-fnref-wilhite&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-qa&quot;&gt;
&lt;p&gt;The audiobook has its own QA suite. It transcribes the generated narration back to text, diffs that against the script, and flags mispronunciations, so a stray hard-G “gif” can’t slip into the correct edition. The machine reads. I still get the last ear. &lt;a href=&quot;#user-content-fnref-qa&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Accessible by default: writing a book like software</title><link>https://ben.balter.com/2026/08/27/accessible-by-default/</link><guid isPermaLink="true">https://ben.balter.com/2026/08/27/accessible-by-default/</guid><description>The ebook meets the Web Content Accessibility Guidelines (WCAG 2.1 Level AA) because the format wouldn&apos;t let me fake a heading and a browser audits every build, not because anyone scheduled a week for it. The parts Markdown can&apos;t check, like color contrast, axe-core catches under Playwright.</description><pubDate>Thu, 27 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/08/27/accessible-by-default/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The build pipeline behind &lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-accessibility-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Open and Async&lt;/em&gt;&lt;/a&gt; is &lt;a href=&quot;/2026/08/17/how-i-over-engineered-my-book/&quot;&gt;absurdly over-engineered&lt;/a&gt;: five formats out of one Markdown source, a real browser auditing every push, the works. I built it to satisfy my own compulsions, not a standard (or, more honestly, as an exercise in &lt;a href=&quot;https://www.structuredprocrastination.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;structured procrastination&lt;/a&gt;: elaborate tooling is a great way to not write the book it’s for). Then I went to check whether the ebook was actually accessible and found out it already was. It meets the Web Content Accessibility Guidelines (&lt;a href=&quot;https://www.w3.org/TR/WCAG21/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WCAG&lt;/a&gt;) 2.1 Level AA, spelled out in the book’s &lt;a href=&quot;https://open-and-async.com/accessibility/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;accessibility statement&lt;/a&gt;. Almost none of it was on purpose. Here’s what actually did the work, and what it missed.&lt;/p&gt;
&lt;h2 id=&quot;markdown-wont-let-you-fake-structure&quot;&gt;Markdown won’t let you fake structure&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#markdown-wont-let-you-fake-structure&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most of accessibility, at least for a book, is structure. Headings that are actually headings, lists that are actually lists, and links that say where they go.&lt;/p&gt;
&lt;p&gt;Markdown is inflexible about that, in the best way. &lt;code&gt;##&lt;/code&gt; is an &lt;code&gt;&amp;#x3C;h2&gt;&lt;/code&gt; or it’s nothing. There’s no font size to reach for, so there’s no way to make a line that &lt;em&gt;looks&lt;/em&gt; like a heading without being one. Alt text sits on the same line as the image it describes, conspicuously empty when it’s missing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2014/03/31/word-versus-markdown-more-than-mere-semantics/&quot;&gt;Open a word processor&lt;/a&gt; and a heading is a style you’re welcome to apply, or you can select the line, bump it to 18pt bold, and move on. Both look identical on the page. Only one of them is a heading when assistive tech comes looking, and nothing in the editor tells you which one you made. &lt;a id=&quot;quote-format-offers-no-option&quot; href=&quot;#quote-format-offers-no-option&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;format-offers-no-option&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;I didn’t get real headings through discipline. I got them because my formatting doesn’t offer the other option.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And everything downstream inherits those semantics. Pandoc renders the same source into HTML and &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Electronic Publication—the open ebook format (essentially zipped HTML and CSS) that non-Kindle readers use&quot; title=&quot;Electronic Publication—the open ebook format (essentially zipped HTML and CSS) that non-Kindle readers use&quot; tabindex=&quot;0&quot;&gt;EPUB&lt;/abbr&gt;, and a Lua filter adds &lt;a href=&quot;https://www.w3.org/TR/dpub-aria-1.0/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;DPUB-ARIA&lt;/a&gt; roles on the way through. A screen reader knows a callout is a callout, and knows which list of links is the table of contents.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pipeline&quot; id=&quot;user-content-fnref-pipeline&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;markdown-cant-check-the-parts-you-didnt-write&quot;&gt;Markdown can’t check the parts you didn’t write&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#markdown-cant-check-the-parts-you-didnt-write&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Three weeks out from launch, the book came out to 575 pages. Print books get bound in signatures (big sheets folded down into 16 or 32 pages at a time), so an odd count meant a blank page at the end. Might as well fill it. At midnight I wrote a one-page back-matter spread (a QR code pointing at the book’s &lt;a href=&quot;https://open-and-async.com/q/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;quote wall&lt;/a&gt;) and print landed at a tidy 576. Deploying on a Friday afternoon, in book form. It went about how you’d expect.&lt;/p&gt;
&lt;p&gt;In the EPUB, that QR went out as a bare inline &lt;code&gt;&amp;#x3C;svg&gt;&lt;/code&gt;: not marked decorative, not given a name, just left for assistive tech to guess about, two files from my own &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;conformance claims&lt;/a&gt;. Every build already ran the rendered book through &lt;a href=&quot;https://github.com/dequelabs/axe-core&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;axe-core&lt;/a&gt;, and every EPUB through &lt;a href=&quot;https://www.w3.org/publishing/epubcheck/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EPUBCheck&lt;/a&gt; and &lt;a href=&quot;https://daisy.org/activities/software/ace/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ace by DAISY&lt;/a&gt;.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-checks&quot; id=&quot;user-content-fnref-checks&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-standards&quot; id=&quot;user-content-fnref-standards&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; All three came back green and the book shipped with it.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-found&quot; id=&quot;user-content-fnref-found&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; Why they were green is the more useful half of the story.&lt;/p&gt;
&lt;h3 id=&quot;contrast-doesnt-exist-until-the-render&quot;&gt;Contrast doesn’t exist until the render&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#contrast-doesnt-exist-until-the-render&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Start with contrast, the cleanest example of something the source can’t check, because the source doesn’t contain it. Markdown has no color. The foreground comes from one CSS rule, the background from another (usually a different file, often behind a media query), and the pair only exists once the cascade has run. There’s nothing to grep, so the accessibility suite is &lt;a href=&quot;https://playwright.dev/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Playwright&lt;/a&gt; driving a real browser over the built HTML, injecting axe-core into the page, and auditing the DOM the browser actually resolved.&lt;/p&gt;
&lt;p&gt;That’s what makes the contrast rule possible. For every text node, axe reads the computed &lt;code&gt;color&lt;/code&gt;, walks up the ancestors until it finds a background that isn’t transparent, blends in any semi-transparent layers it passes through, converts both ends to relative luminance (a weighted brightness value, not the raw RGB), and checks the ratio against &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WCAG 1.4.3&lt;/a&gt;: 4.5:1 for body text, 3:1 for large text (18pt, or 14pt bold). &lt;a id=&quot;quote-pairs-nobody-designed&quot; href=&quot;#quote-pairs-nobody-designed&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;pairs-nobody-designed&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The pairs that fail are the ones nobody designed.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Inline &lt;code&gt;&amp;#x3C;code&gt;&lt;/code&gt; inside a tinted callout. A muted caption on a page background that isn’t quite white. Two rules that are each fine alone and only meet in the render.&lt;/p&gt;
&lt;p&gt;Then the whole audit runs a second time with &lt;code&gt;page.emulateMedia({ colorScheme: &apos;dark&apos; })&lt;/code&gt;, which flips &lt;code&gt;prefers-color-scheme&lt;/code&gt; and hands axe the same DOM with an entirely different set of computed colors. Same tree, different palette, two verdicts. Both have to come back with no critical or serious violations, or &lt;code&gt;validate-playwright&lt;/code&gt; goes red alongside the build jobs.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-gate&quot; id=&quot;user-content-fnref-gate&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-the-checks-cant-see&quot;&gt;What the checks can’t see&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-the-checks-cant-see&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What none of it can see is markup that never announced what it was. Every axe rule that fires on an unnamed image keys off either an &lt;code&gt;&amp;#x3C;img&gt;&lt;/code&gt; element or an explicit role: &lt;code&gt;image-alt&lt;/code&gt; matches &lt;code&gt;img&lt;/code&gt;, &lt;code&gt;svg-img-alt&lt;/code&gt; matches &lt;code&gt;svg[role=&quot;graphics-document&quot;]&lt;/code&gt; and its neighbors. A &lt;code&gt;&amp;#x3C;svg&gt;&lt;/code&gt; with no role matches nothing at all, and EPUBCheck has no opinion about accessible names in the first place. &lt;a id=&quot;quote-no-check-to-fail&quot; href=&quot;#quote-no-check-to-fail&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;no-check-to-fail&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The QR didn’t fail a check. There was no check for it to fail.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Your checks have the same shape, whatever you’re building: they cover the categories somebody thought to name, and generated markup rarely announces itself as a category. Tagging this one decorative was my own postprocessing step’s job, and my rule was too specific.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-qr&quot; id=&quot;user-content-fnref-qr&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;accessible-is-a-synonym-for-inclusive&quot;&gt;Accessible is a synonym for inclusive&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#accessible-is-a-synonym-for-inclusive&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Strip away the acronyms and conformance levels and what’s left is a question about who gets to read the thing.&lt;/p&gt;
&lt;h3 id=&quot;the-reader-sets-the-terms&quot;&gt;The reader sets the terms&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-reader-sets-the-terms&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An accessible ebook reflows, so a reader sets their own font, size, spacing, and colors instead of squinting at whatever I happened to pick. I’m one of those readers, after enough years of staring at a screen all day. Its navigation works, letting anyone jump straight to the chapter they came for. It reads cleanly aloud, which matters as much for someone on a commute with the screen off as for someone using a screen reader. I can vouch for that part firsthand: I read the whole book screen off, listening straight through, while prepping the narration. That’s the one audit no checker in my pipeline runs, and the kind that catches what a green build can’t. A narrated edition turned out to be a much smaller lift than I’d budgeted for, because the structure that passed axe is the structure a voice follows. More on that soon.&lt;/p&gt;
&lt;h3 id=&quot;exclusion-dressed-as-preference&quot;&gt;Exclusion dressed as preference&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#exclusion-dressed-as-preference&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;None of this is charity. A book that only works at my font size is making the same mistake as a decision that only happens in a meeting. The format quietly decides who gets to participate, and then everyone calls the result a preference.&lt;/p&gt;
&lt;h3 id=&quot;readers-youll-never-meet&quot;&gt;Readers you’ll never meet&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#readers-youll-never-meet&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some of it pays off where I’ll never know. The EPUB’s metadata declares what the file is and what it conforms to, which is how the book ended up on &lt;a href=&quot;https://www.bookshare.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Bookshare&lt;/a&gt;, the Department of Education–backed library where readers with print disabilities get books free, in DAISY, braille, or large print. I’ll never hear from that reader. Getting into that catalog cost nothing beyond accurately describing what I’d already built.&lt;/p&gt;
&lt;h2 id=&quot;a-screen-reader-and-a-language-model-want-the-same-thing&quot;&gt;A screen reader and a language model want the same thing&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#a-screen-reader-and-a-language-model-want-the-same-thing&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Those readers are all human. The same markup that serves them serves the software now reading your work, too. Every chapter in the book opens with a TL;DR, a fenced div in the source: &lt;code&gt;::: {.tldr}&lt;/code&gt;. The Lua filter gives it a DPUB-ARIA role, so assistive tech announces it as a summary instead of another paragraph of body text.&lt;/p&gt;
&lt;h3 id=&quot;structure-is-the-interface&quot;&gt;Structure is the interface&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#structure-is-the-interface&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;That same TL;DR annotation feeds the book’s &lt;a href=&quot;https://modelcontextprotocol.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MCP server&lt;/a&gt;. Yes, the book ships one, so an agent can query it directly. A build script pulls 44 chapter TL;DRs, nine key-takeaway blocks, and the full outline straight out of the manuscript, with nobody hand-curating a machine-readable copy of any of it. One piece of markup, two consumers that have nothing else in common.&lt;/p&gt;
&lt;p&gt;That overlap isn’t limited to my build script. Alt text is the only description a model has of your image. Headings are the boundaries a retrieval system chunks on. Link text is what an index actually reads. &lt;a id=&quot;quote-neither-can-see-bold&quot; href=&quot;#quote-neither-can-see-bold&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;neither-can-see-bold&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Neither a screen reader nor an LLM can see your 18pt bold. Both of them can read an &lt;code&gt;&amp;#x3C;h2&gt;&lt;/code&gt;.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Semantic structure has become the interface for everything that isn’t a pair of human eyes. The accessible version of your writing and the machine-readable version turn out to be the same file.&lt;/p&gt;
&lt;h3 id=&quot;the-file-has-to-open-first&quot;&gt;The file has to open first&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-file-has-to-open-first&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The accessible file and the machine-readable file are only the same file if a reader can open it, which is why the book ships DRM-free everywhere it’s sold. DRM encrypts the EPUB so only an approved app can open it, and every tool downstream of that lock is on the wrong side of it: a screen reader the vendor doesn’t support, a text-to-speech engine, a braille conversion, an agent the reader points at a book they paid for. &lt;a id=&quot;quote-drm-cant-file&quot; href=&quot;#quote-drm-cant-file&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;drm-cant-file&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;DRM is the one accessibility bug a reader can’t file.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; The structure only counts if the file is one they can open with whatever they read with.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-drm&quot; id=&quot;user-content-fnref-drm&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;steal-this&quot;&gt;Steal this&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#steal-this&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You don’t need my pipeline. Two decisions get you almost everything, and the first one is free.&lt;/p&gt;
&lt;p&gt;Write in a format where structure is real. Markdown, if you like it. Your word processor’s actual heading styles, if you don’t. The goal is that faking structure takes more effort than doing it properly, so the lazy path and the correct path are the same path.&lt;/p&gt;
&lt;p&gt;Then run one checker against the built output, on a schedule you can’t skip, one command wired into whatever already runs when you export or deploy. It doesn’t have to be axe and it doesn’t have to be thorough on day one. It has to be automatic, because the answer only helps while you still remember what you were doing.&lt;/p&gt;
&lt;p&gt;Do both and accessibility stops being a heroic sprint at the end of the project. The ebook is accessible for the same unremarkable reason its links work: a machine checks, every build, and I can’t ship until it’s happy.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-pipeline&quot;&gt;
&lt;p&gt;The whole thing is Markdown in Git, rendered by Pandoc into five formats and tested on every push. I wrote up &lt;a href=&quot;/2026/08/17/how-i-over-engineered-my-book/&quot;&gt;the full pipeline&lt;/a&gt; separately, in more detail than anyone asked for. &lt;a href=&quot;#user-content-fnref-pipeline&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-checks&quot;&gt;
&lt;p&gt;axe-core runs under Playwright against the actual built HTML, in light and dark, checking contrast, heading order with no skipped levels, alt text, link text that says where it goes, and a declared &lt;code&gt;lang&lt;/code&gt; so a screen reader picks the right pronunciation. EPUBCheck covers spec conformance and Ace covers the accessibility metadata and structure that e-readers and library catalogs read. &lt;a href=&quot;#user-content-fnref-checks&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-standards&quot;&gt;
&lt;p&gt;The book declares &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Web Content Accessibility Guidelines—the international standard for making web and ebook content accessible&quot; title=&quot;Web Content Accessibility Guidelines—the international standard for making web and ebook content accessible&quot; tabindex=&quot;0&quot;&gt;WCAG&lt;/abbr&gt; 2.1 Level AA and EPUB Accessibility 1.1 in its &lt;a href=&quot;https://open-and-async.com/accessibility/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;accessibility statement&lt;/a&gt;. Those are the same conformance levels the &lt;a href=&quot;https://ec.europa.eu/social/main.jsp?catId=1202&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;European Accessibility Act&lt;/a&gt; points to for ebooks, in force since June 28, 2025, which is how I ended up reading the conformance-reporting spec closely enough to learn that &lt;code&gt;a11y:certifierReport&lt;/code&gt; has to be a &lt;code&gt;&amp;#x3C;link rel&gt;&lt;/code&gt; rather than a &lt;code&gt;&amp;#x3C;meta property&gt;&lt;/code&gt;. EPUBCheck failed my build until I got that one line right. Two things the checks still can’t fix, though. The claim covers only the ebook, since a fixed 6×9 print page doesn’t reflow for anybody. And there’s still no page list tying the ebook to the paperback’s page numbers, so citing the book by page means having the print edition on hand. &lt;a href=&quot;#user-content-fnref-standards&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-found&quot;&gt;
&lt;p&gt;Not from a check. All three passed it. I caught it weeks later while prepping the audiobook, going through the book with the screen off to decide what the narration should and shouldn’t voice. A QR with no accessible name is silent when read aloud, so it stood out by producing nothing at all. That screen-off read-through, which I get into later, is the one audit my pipeline doesn’t run. &lt;a href=&quot;#user-content-fnref-found&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-gate&quot;&gt;
&lt;p&gt;It’s a separate &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Continuous Integration—automatically building and testing every change&quot; title=&quot;Continuous Integration—automatically building and testing every change&quot; tabindex=&quot;0&quot;&gt;CI&lt;/abbr&gt; job rather than a step inside the build, so the audit runs in parallel with the EPUB, Kindle, and PDF builds instead of adding to the critical path. The whole graph finishes in about five minutes, which is the actual reason the checks survived: a gate slow enough to be annoying is a gate you start skipping. &lt;a href=&quot;#user-content-fnref-gate&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-qr&quot;&gt;
&lt;p&gt;Fixed in v1.0.1. My decorative-image rule matched &lt;code&gt;&amp;#x3C;img class=&quot;callout-emoji&quot;&gt;&lt;/code&gt;, and the QR was neither an &lt;code&gt;img&lt;/code&gt; nor a callout, so it walked right past. The EPUB postprocessing step now tags QR SVGs with &lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt;, identifying them by the &lt;code&gt;shape-rendering=&quot;crispEdges&quot;&lt;/code&gt; attribute that’s unique to QR renders, so the cover art is left alone. Nothing is lost by hiding it. The URL is printed right underneath as a real link. &lt;a href=&quot;#user-content-fnref-qr&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-drm&quot;&gt;
&lt;p&gt;It’s a checkbox at upload time on every store, and the default in at least one of them is on. Amazon also has a separate publisher-set flag for whether text-to-speech is allowed at all, which is a strange thing to be able to switch off on someone else’s behalf. &lt;a href=&quot;#user-content-fnref-drm&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>How I over-engineered my book</title><link>https://ben.balter.com/2026/08/17/how-i-over-engineered-my-book/</link><guid isPermaLink="true">https://ben.balter.com/2026/08/17/how-i-over-engineered-my-book/</guid><description>My book has a linter, ~5,500 automated checks, and a Pandoc pipeline that rebuilds five formats (EPUB, Kindle, paperback ×2, and web) on every git push. For a book one person wrote. Here&apos;s how I built and published it the way I ship software.</description><pubDate>Mon, 17 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/08/17/how-i-over-engineered-my-book/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;My book has a &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; title=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; tabindex=&quot;0&quot;&gt;linter&lt;/abbr&gt; that yells at me for hyphenating “open source.”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-opensource&quot; id=&quot;user-content-fnref-opensource&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; It runs a battery of automated checks, rebuilds every format on each &lt;code&gt;git push&lt;/code&gt;, and fails the build if I so much as imply I still work at a job I left. For a book. That one person wrote.&lt;/p&gt;
&lt;p&gt;I didn’t set out to do this. Most people start a writing project by firing up Word or Google Docs, and I started down that same path. I even tried some purpose-built authoring tools, but every tool felt inferior to the ones I used as a developer every day. I did “the only reasonable” thing and threw all of them out, writing the whole book on Git, Markdown, and a &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Continuous Integration—automatically building and testing every change&quot; title=&quot;Continuous Integration—automatically building and testing every change&quot; tabindex=&quot;0&quot;&gt;CI&lt;/abbr&gt; build pipeline. If you’ve read how I &lt;a href=&quot;/2020/12/04/over-engineered-home-network-for-privacy-and-security/&quot;&gt;over-engineered my home network&lt;/a&gt; (&lt;a href=&quot;/2021/09/01/how-i-re-over-engineered-my-home-network/&quot;&gt;twice&lt;/a&gt;), none of this will surprise you.&lt;/p&gt;
&lt;p&gt;I’ve been making websites for decades, so the leap was short: the same tools that build websites could build books, no last-minute magic-trick reveal required to &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;A magician&amp;#x27;s incantation—used here as a verb, to conjure one thing into another as if by magic&quot; title=&quot;A magician&amp;#x27;s incantation—used here as a verb, to conjure one thing into another as if by magic&quot; tabindex=&quot;0&quot;&gt;abracadabra&lt;/abbr&gt; a standard Word doc into a full-blown book. There were missteps,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-latex&quot; id=&quot;user-content-fnref-latex&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; but in the end, I would not have written &lt;a href=&quot;https://open-and-async.com&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Open and Async&lt;/a&gt; any other way.&lt;/p&gt;
&lt;h2 id=&quot;content&quot;&gt;Content&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#content&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Naturally&lt;/em&gt;, the content itself lived as Markdown files in a Git repository. After all, that’s where I spend most of my day. I used VS Code, with a handful of prose extensions (&lt;a href=&quot;#real-time&quot;&gt;listed below&lt;/a&gt;). Each chapter was its own Markdown file, and a single &lt;code&gt;index.yml&lt;/code&gt; file defined the order, making it easy to re-order chapters or add new ones.&lt;/p&gt;
&lt;p&gt;Practically, I wrote most of this book on an iPad (Codespaces in a browser tab, a Bluetooth keyboard, often nights and weekends while away from my desk), and the Git repository kept everything in sync no matter where I opened it. I could focus on the words, and a bad idea was one &lt;code&gt;git revert&lt;/code&gt; away from gone.&lt;/p&gt;
&lt;p&gt;Not to mention, I had real-time feedback on my writing right in my &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Integrated Development Environment—the editor where developers write, run, and debug code (here, VS Code)&quot; title=&quot;Integrated Development Environment—the editor where developers write, run, and debug code (here, VS Code)&quot; tabindex=&quot;0&quot;&gt;IDE&lt;/abbr&gt; from the various prose linters, just as I would have real-time feedback on my code from ESLint or Prettier.&lt;/p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#testing&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With content as code, the next logical step (and the point where “reasonable” quietly left the building) was to set up automated tests. &lt;a href=&quot;/2015/05/22/test-your-prose/&quot;&gt;Testing prose the way you test code&lt;/a&gt; is something I’d argued for years; this was me taking it to an absurd extreme. I did that two ways: real-time, and on push (CI).&lt;/p&gt;
&lt;h3 id=&quot;real-time&quot;&gt;Real-time&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#real-time&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Locally, as I typed, I ran several VS Code extensions all giving me real-time feedback. Specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DavidAnson/markdownlint&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Markdownlint&lt;/a&gt;: Markdown syntax and formatting consistency&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Automattic/harper&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Harper&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cspell&quot; id=&quot;user-content-fnref-cspell&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;: grammar and word choice, entirely on-device&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://languagetool.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LanguageTool&lt;/a&gt;: grammar, punctuation, and style&lt;sup&gt;&lt;a href=&quot;#user-content-fn-languagetool&quot; id=&quot;user-content-fnref-languagetool&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vale.sh/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vale&lt;/a&gt;: my own house-style rules and banned terms&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://alexjs.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Alex&lt;/a&gt;: insensitive or exclusionary phrasing&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/btford/write-good&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Write-good&lt;/a&gt;: weak prose, like passive voice, weasel words, and clichés&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All six also ran in CI (Alex and Write-good folded into Vale there; &lt;a href=&quot;#on-each-push&quot;&gt;more on that below&lt;/a&gt;). Together, they layered hundreds of curated style rules over a full grammar engine, all of it underlining my mistakes in real time, the way a red squiggle flags a type error.&lt;/p&gt;
&lt;h3 id=&quot;on-each-push&quot;&gt;On each push&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#on-each-push&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In addition to running those open source linters in CI (some blocking), I built a custom test suite of my own: a standalone Node script of content validators, a Vitest suite, and Playwright specs. The validators are the fun part. Each is a few lines that read the Markdown and push an error with a file-and-line pointer. My favorite: I don’t work at GitHub anymore, so any sentence claiming I &lt;em&gt;still&lt;/em&gt; do fails the build.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// My time at GitHub has to read as past tense — present-tense&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;// employment claims about it fail the build.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; validateGitHubTense&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E36209;--shiki-dark:#FFAB70&quot;&gt;files&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; patterns&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;    // &quot;is/are ... at GitHub&quot; — a current-employment claim&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;    /&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;\b&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;(is&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;are)&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;\b&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;.!?\n]&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;{1,80}?\b&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;at GitHub&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;\b&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;    // &quot;works/leads/runs at GitHub&quot; — current-employment activity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;    /&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;\b&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;(works&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;?|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;leads&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;?|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;runs&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;?|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;manages&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;?|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;directs&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;(at&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;for)&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;\s&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#DBEDFF&quot;&gt;GitHub&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;\b&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;  ];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; flagLinesMatching&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(files, patterns);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That validator exists because I made the mistake once, which is the whole pattern. The first time an error got past me, I didn’t just fix that one sentence; I wrote a rule so I’d never have to catch it by eye again. It’s &lt;a href=&quot;https://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cattle, not pets&lt;/a&gt; for prose: don’t hand-nurse each chapter, govern the whole herd with policy. A mistake caught once becomes a check that sweeps every chapter and fails the build if it ever wanders back. That’s one of about thirty. Others I’m proud of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateOpenSourceHyphenation&lt;/code&gt;&lt;/strong&gt;: “open source” is &lt;a href=&quot;/2012/10/15/open-source-is-not-a-verb/&quot;&gt;a noun, not a verb&lt;/a&gt;, and never hyphenated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateHypotheticalHooks&lt;/code&gt;&lt;/strong&gt;: formulaic AI-tell openers (“Picture this…”, “Imagine…”, “Consider a…”) at the start of a paragraph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateSentenceStarters&lt;/code&gt;&lt;/strong&gt;: three-plus sentences in a row opening with the same word.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateNoBareUrlLinkText&lt;/code&gt;&lt;/strong&gt;: no link whose visible text is just the raw URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateCalloutBalance&lt;/code&gt;&lt;/strong&gt;: the book speaks to managers and individual contributors, so a “For managers” callout has to have a “For &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Individual Contributor—someone who does the work rather than managing people&quot; title=&quot;Individual Contributor—someone who does the work rather than managing people&quot; tabindex=&quot;0&quot;&gt;ICs&lt;/abbr&gt;” counterpart nearby.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;validateCrossReferences&lt;/code&gt;&lt;/strong&gt;: every &lt;code&gt;[text](#anchor)&lt;/code&gt; cross-reference resolves to a real heading.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;…plus a couple dozen more for small caps, em-dashes, doubled words, en-dash ranges, TL;DR length, and every other tic I could name.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-validators&quot; id=&quot;user-content-fnref-validators&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;One of these caught &lt;em&gt;me&lt;/em&gt;. &lt;code&gt;validateHypotheticalHooks&lt;/code&gt; flagged the opening of a paragraph I was certain I’d written myself. And I had. But reading it back cold, it did sound ghost-authored; I’d absorbed the cadence from reading too much generated text and produced a fluent imitation of nothing. A linter can’t tell good prose from bad. What it can flag are the patterns you reach for when you’ve stopped thinking, which is exactly the thing you can’t see in your own draft. Same reason I keep &lt;code&gt;eslint&lt;/code&gt; around: it can’t tell good code from bad either, but it catches the autopilot mistakes your own eye skates right over.&lt;/p&gt;
&lt;p&gt;All in all, the CI suite ran ~70 test files with 2,204 test cases and ~3,900 &lt;code&gt;expect()&lt;/code&gt; assertions, plus another ~1,600 per-chapter structural checks from the validators above.&lt;/p&gt;
&lt;h2 id=&quot;audits&quot;&gt;Audits&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#audits&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The linters caught mistakes, but they couldn’t tell me whether the book repeated itself or whether a chapter was any good. For that I built a second layer of tooling: audits that judge the writing, not just check it. One thing they never did, though, was write it. Every word is mine; these tools are readers of last resort, catching what I’d stopped being able to see.&lt;/p&gt;
&lt;h3 id=&quot;duplication-detection&quot;&gt;Duplication detection&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#duplication-detection&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After reading the book over and over, I was &lt;em&gt;convinced&lt;/em&gt; I’d repeated the same idea across chapters. I wanted proof, not a hunch, so I built three layers of duplication detection, each catching what the one before it misses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/kucherenko/jscpd&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;jscpd&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;: token-level copy-paste detection. Catches longer verbatim blocks I’d pasted between chapters, but nothing subtler.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-dry&quot; id=&quot;user-content-fnref-dry&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/N-gram&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;n-gram&lt;/a&gt;&lt;/strong&gt;: tokenizes every chapter in &lt;code&gt;index.yml&lt;/code&gt;, strips Markdown/Pandoc syntax, builds word n-grams (phrases of n words), and flags phrases appearing in more than one chapter (plus a “most-repeated stock wording” ranking). Two modes: cross-chapter (default &lt;code&gt;--n&lt;/code&gt;) and intra-chapter (&lt;code&gt;--scope=intra --n=8&lt;/code&gt;) for a chapter repeating itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;semantic&lt;/strong&gt;: the one the other two can’t do, the same &lt;em&gt;point&lt;/em&gt; restated in different words. An on-demand &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; title=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; tabindex=&quot;0&quot;&gt;LLM&lt;/abbr&gt; audit, designed so it never feeds the whole book to a model, runs three passes, each over small units:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;intra&lt;/strong&gt;: one call per chapter: “where does this chapter restate itself?”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cross&lt;/strong&gt;: a single call over every chapter’s TL;DR, producing a map of conceptually overlapping chapters. A whole-book scan for the cost of one call.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;arguments&lt;/strong&gt;: extract each chapter’s central claims one call at a time, then cluster the same argument across all chapters in one final call. Catches arguments made in body prose that &lt;code&gt;cross&lt;/code&gt; misses.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Was I actually repeating myself? By this final pass the two mechanical layers came up clean, but they’d earned it: &lt;code&gt;jscpd&lt;/code&gt; and the n-gram scan had already caught the copy-paste and recycled phrasing (a doubled motif here, a reused TL;DR structure there), and I’d fixed each.&lt;/p&gt;
&lt;p&gt;What neither could see was the subtler kind: nothing was duplicated word-for-word anymore, just the same &lt;em&gt;point&lt;/em&gt; in different words. The semantic pass caught that, and it was right: I’d made the same argument, that moving office habits online isn’t the same as working remote-first, in &lt;em&gt;five&lt;/em&gt; separate chapters, on top of 166 smaller self-restatements scattered across 51 chapters. &lt;a id=&quot;quote-paraphrased-myself&quot; href=&quot;#quote-paraphrased-myself&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;paraphrased-myself&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;I’d paraphrased myself too well for anything cheaper than an LLM to catch me.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My hunch was correct; I’d just needed three escalating tools to prove what re-reading my own book to blurry-eyed exhaustion couldn’t.&lt;/p&gt;
&lt;h3 id=&quot;content-audits&quot;&gt;Content audits&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#content-audits&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Beyond duplication, a second set of tools graded the prose itself, split by how they judge. Some are &lt;strong&gt;probabilistic&lt;/strong&gt; (an LLM reads the chapter and weighs in; run it twice and the findings can shift), and some are &lt;strong&gt;deterministic&lt;/strong&gt; (rules and arithmetic, same input, same output, every time).&lt;/p&gt;
&lt;h4 id=&quot;probabilistic-llm-audits&quot;&gt;Probabilistic (LLM) audits&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#probabilistic-llm-audits&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Claims lens caught what nothing else could, early. It stopped on a sentence claiming GitHub’s monthly all-hands “dedicated roughly half of each session to live Q&amp;#x26;A.” That was a number I was certain of and had wrong; it was closer to a third, and the share moved around over the years. No linter flags that: it’s clean, confident prose that happens to be false. Only a reader asking “is this actually true?” catches it, and I’d have shipped it otherwise.&lt;/p&gt;
&lt;p&gt;Claims is one of &lt;strong&gt;20 single-purpose lenses&lt;/strong&gt; in a “prose audits” test, a per-chapter LLM auditor where each lens asks one narrow question so the model can’t hand-wave a vague “looks good.” &lt;code&gt;--lens=all&lt;/code&gt; runs every lens; &lt;code&gt;--models&lt;/code&gt;/&lt;code&gt;--rounds&lt;/code&gt; add a deduplicated multi-model and self-consistency panel, so a finding has to survive more than one model (or more than one run) to count. A sampling of the rest:&lt;sup&gt;&lt;a href=&quot;#user-content-fn-lenses&quot; id=&quot;user-content-fnref-lenses&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;





































&lt;table class=&quot;w-full border-collapse&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Lens&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;What it catches&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;AI-tells&lt;/td&gt;&lt;td&gt;Phrasing that reads as machine-generated rather than my voice&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hook&lt;/td&gt;&lt;td&gt;Whether the opening does real work or is throat-clearing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Legal&lt;/td&gt;&lt;td&gt;Legal or reputational risk (naming names, unverified claims)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dated&lt;/td&gt;&lt;td&gt;Perishable references that will age badly (“recently,” “this year”)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Global&lt;/td&gt;&lt;td&gt;Idioms and cultural assumptions that trip up non-US readers&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dual-audience&lt;/td&gt;&lt;td&gt;Whether a chapter serves managers &lt;em&gt;and&lt;/em&gt; individual contributors&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Promise&lt;/td&gt;&lt;td&gt;Whether the chapter delivers on the book’s core promise&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Separately, I also had a traits analysis test, which scored each chapter on persuasion and engagement traits, catching prose that was technically clean but flat so I could give it another (human) pass.&lt;/p&gt;
&lt;h4 id=&quot;deterministic-audits&quot;&gt;Deterministic audits&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#deterministic-audits&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I ran these after a round of editing, to see whether the book was improving as a reading experience: chapter- and paragraph-length distributions, whole-book reading time, a hard &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Electronic Publication—the open ebook format (essentially zipped HTML and CSS) that non-Kindle readers use&quot; title=&quot;Electronic Publication—the open ebook format (essentially zipped HTML and CSS) that non-Kindle readers use&quot; tabindex=&quot;0&quot;&gt;EPUB&lt;/abbr&gt; size budget (Kindle penalizes oversized files on delivery), a book-wide consistency checker, and a stats dashboard whose &lt;code&gt;--check&lt;/code&gt; mode fails the build unless every “attention item” (a chapter missing a TL;DR, an unbalanced callout) sits at zero.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-deterministic&quot; id=&quot;user-content-fnref-deterministic&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Together, these turned “is the book actually getting better?” from a gut feeling into a number I could watch move between drafts.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;/assets/img/book-writing-dashboard-1600x438.png&quot; alt=&quot;The writing dashboard for Open and Async: summary cards reading 99,651 total words, 399 minutes reading time, 72 chapters, 1,384 average chapter words, 103 manager callouts, 102 IC callouts, 123 pro tips, and 62 objections, with a green &amp;#x22;No issues found, all content chapters look good!&amp;#x22; all-clear at the bottom.&quot; title=&quot;The writing dashboard&amp;#x27;s --check mode gates the build on zero attention items.&quot; loading=&quot;eager&quot; decoding=&quot;auto&quot; fetchpriority=&quot;high&quot; width=&quot;1600&quot; height=&quot;438&quot;&gt;&lt;figcaption&gt;The writing dashboard for Open and Async: summary cards reading 99,651 total words, 399 minutes reading time, 72 chapters, 1,384 average chapter words, 103 manager callouts, 102 IC callouts, 123 pro tips, and 62 objections, with a green &quot;No issues found, all content chapters look good!&quot; all-clear at the bottom.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;design&quot;&gt;Design&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#design&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am &lt;em&gt;far&lt;/em&gt; from a designer, and I’d never published an ebook before. I had no idea how they worked beyond reading many of them. Two aha moments changed the way I thought about publishing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a id=&quot;quote-ebook-trench-coat&quot; href=&quot;#quote-ebook-trench-coat&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;ebook-trench-coat&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;An ebook is a website in a trench coat&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. Ebooks are just HTML and CSS, albeit a very stripped-down version.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-kindlewebkit&quot; id=&quot;user-content-fnref-kindlewebkit&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;9&lt;/a&gt;&lt;/sup&gt; If you can make a website, you can make an ebook.&lt;/li&gt;
&lt;li&gt;A print book can be one too, with enough effort. CSS natively has powerful &lt;code&gt;@media print&lt;/code&gt; and &lt;code&gt;@page&lt;/code&gt; rules, including left and right page styling, title pages, page numbers, and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the interior design, I reached for &lt;a href=&quot;https://tailwindcss.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tailwind CSS&lt;/a&gt;, not because it’s built for books (it isn’t), but because it’s what I already use every day. &lt;code&gt;@tailwindcss/typography&lt;/code&gt; gave me a typographic baseline to start from instead of a blank page.&lt;/p&gt;
&lt;p&gt;One note: I purposefully hired a &lt;em&gt;human&lt;/em&gt; designer for the cover. For a book about being authentic, the first impression had to be authentic.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-cover&quot; id=&quot;user-content-fnref-cover&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-invisible-leak&quot;&gt;The invisible leak&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-invisible-leak&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Styling every format from one stylesheet has a failure mode: CSS meant for one format bleeding into another. To keep my e-reader tweaks away from the paperback, I scoped each of them to &lt;code&gt;@media not screen&lt;/code&gt;. But &lt;a href=&quot;https://weasyprint.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WeasyPrint&lt;/a&gt; (the HTML/CSS-to-PDF engine that draws the paperback, &lt;a href=&quot;#a-reproducible-toolchain&quot;&gt;more on it below&lt;/a&gt;) renders the print PDF with media type &lt;code&gt;print&lt;/code&gt;, and &lt;code&gt;print&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; “not screen,” exactly as the spec says. So two of those e-reader rules matched anyway and quietly bled into the book: a tighter line-height on callout paragraphs, and a &lt;code&gt;box-decoration-break&lt;/code&gt; change that dropped the repeated padding on any callout that split across a page.&lt;/p&gt;
&lt;p&gt;Neither was visible. Nothing &lt;em&gt;looked&lt;/em&gt; broken. But each shaved a sliver of vertical space off every callout, and the book slowly deflated from 576 printed pages to 567. I only found out because the print PDF is pinned to exactly 576 pages&lt;sup&gt;&lt;a href=&quot;#user-content-fn-pagecount&quot; id=&quot;user-content-fnref-pagecount&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;11&lt;/a&gt;&lt;/sup&gt; and the build went red: a page-count gate written to protect the cover spine, catching a CSS bug it was never designed for. &lt;a id=&quot;quote-cascade-forgets&quot; href=&quot;#quote-cascade-forgets&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;cascade-forgets&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Two formats from one stylesheet is a gift, right up until the cascade forgets which format it’s in.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; The fix re-pins the original values in a print-only block, loud with &lt;code&gt;!important&lt;/code&gt; so the leak can’t win.&lt;/p&gt;
&lt;h3 id=&quot;tests&quot;&gt;Tests&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#tests&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The design and layout got a test suite as well, driven by Playwright against the built HTML, so it checks what actually renders, not what I hoped the CSS did. Two categories:&lt;/p&gt;
&lt;h4 id=&quot;accessibility-axe-core&quot;&gt;Accessibility (axe-core)&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#accessibility-axe-core&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;axe-core&lt;/code&gt; engine runs over the rendered book in &lt;strong&gt;both light and dark mode&lt;/strong&gt; against &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Web Content Accessibility Guidelines—the international standard for making web and ebook content accessible&quot; title=&quot;Web Content Accessibility Guidelines—the international standard for making web and ebook content accessible&quot; tabindex=&quot;0&quot;&gt;WCAG&lt;/abbr&gt; 2.1 AA: no critical or serious violations, AA color contrast in either scheme, alt text on every image, logical heading order, discernible link text (no bare “click here”), and a declared &lt;code&gt;lang&lt;/code&gt; attribute so screen readers pick the right pronunciation. It’s the &lt;code&gt;test:a11y&lt;/code&gt; gate, enforced in CI as &lt;code&gt;validate-playwright&lt;/code&gt;. Accessibility earned more than a subsection here. It has &lt;a href=&quot;https://open-and-async.com/accessibility/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;its own accessibility statement&lt;/a&gt;, and a dedicated deep dive is coming.&lt;/p&gt;
&lt;h4 id=&quot;visual--layout-regression-playwright&quot;&gt;Visual &amp;#x26; layout regression (Playwright)&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#visual--layout-regression-playwright&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These assert that the actual computed styles match the design intent (the details that break quietly and never show up in a diff): the title page renders centered with a lighter-weight subtitle; the table of contents is a real &lt;code&gt;nav&lt;/code&gt; with the &lt;code&gt;doc-toc&lt;/code&gt; role and working links; every callout type has its own unique left-border color and auto-generated label prefix; and small caps get real &lt;code&gt;font-variant: small-caps&lt;/code&gt;, not just a class name.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-visual&quot; id=&quot;user-content-fnref-visual&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;12&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;None of this is glamorous. It’s exactly the kind of thing that breaks silently in a Friday CSS refactor and doesn’t surface until a reader emails you.&lt;/p&gt;
&lt;h2 id=&quot;building&quot;&gt;Building&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#building&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve got clean Markdown, and a test suite that ensures the content is clean, but we still need to get it into a format that can be published (EPUB, Kindle, paperback in two flavors, and web). Core to that custom build pipeline was &lt;a href=&quot;https://pandoc.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pandoc&lt;/a&gt;, a universal document converter that can read Markdown and spit out just about anything else. I used Pandoc as the engine, but Pandoc alone gets you a generic document. Turning that into five store-ready formats took a pile of custom tooling:&lt;/p&gt;
&lt;h3 id=&quot;a-reproducible-toolchain&quot;&gt;A reproducible toolchain&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#a-reproducible-toolchain&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pandoc is only the engine. The full build also needs &lt;a href=&quot;https://weasyprint.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WeasyPrint&lt;/a&gt; to draw the PDF, Ghostscript to convert it, and a pile of Noto fonts for full Unicode coverage: a gnarly stack of native dependencies to install by hand. The whole thing therefore lives in a Docker image and a dev container, pinned to an exact renderer version (a detail that matters more than you’d think, see the &lt;a href=&quot;#rendering--post-processing-per-format&quot;&gt;page-count gate below&lt;/a&gt;). That container is also why I could draft the book from an iPad: the heavy toolchain ran in Codespaces, not on my lap.&lt;/p&gt;
&lt;h3 id=&quot;pre-processing-before-pandoc-sees-the-text&quot;&gt;Pre-processing (before Pandoc sees the text)&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#pre-processing-before-pandoc-sees-the-text&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Everything starts on a throwaway copy of the source tree, so these transforms never touch the real chapters. A couple of scripts massage that copy before Pandoc runs: stamping each build with its commit SHA, turning inline links into numbered footnotes for print (a hyperlink is useless on paper; a footnote with the full URL isn’t), and reshuffling the chapters per format so the EPUB gets shareable quote links while print and Kindle get a QR share page instead.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-preprocess&quot; id=&quot;user-content-fnref-preprocess&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;css-pipeline&quot;&gt;CSS pipeline&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#css-pipeline&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One Tailwind stylesheet (&lt;code&gt;src/style.css&lt;/code&gt;) compiles through PostCSS to &lt;code&gt;dist/style.css&lt;/code&gt;, so screen, print, and EPUB all start from the same source of truth. Each format then takes a different slice: &lt;code&gt;strip-page-rules.js&lt;/code&gt; derives a cut-down stylesheet for EPUB, stripping the CSS Paged-Media features (&lt;code&gt;@page&lt;/code&gt;, &lt;code&gt;target-counter()&lt;/code&gt;, &lt;code&gt;oklch()&lt;/code&gt;, custom properties) that Kindle and EPUBCheck choke on.&lt;/p&gt;
&lt;h3 id=&quot;lua-filters-the-interesting-part&quot;&gt;Lua filters (the interesting part)&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#lua-filters-the-interesting-part&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the part I didn’t expect to love. Pandoc parses everything into an abstract syntax tree (AST) and lets you rewrite that tree with small Lua scripts before it renders, so format-specific tweaks live in code, not smeared through the prose. Deleting a node, for instance, is just returning an empty table:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;&quot; tabindex=&quot;0&quot; data-language=&quot;lua&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;-- strip-comments.lua: drop editorial HTML comments so my notes-to-self&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;-- never ship inside the published .xhtml. Returning {} removes the node.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; RawBlock&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(el)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; is_html_comment&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(el.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;format&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;, el.&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;  end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest are variations on that idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;add-div-titles.lua&lt;/code&gt;&lt;/strong&gt;: injects the callout labels (“TL;DR: ”, ”&lt;span role=&quot;img&quot; aria-label=&quot;light bulb&quot;&gt;💡&lt;/span&gt; Pro-Tip: ”, ”&lt;span role=&quot;img&quot; aria-label=&quot;necktie&quot;&gt;👔&lt;/span&gt; For managers: ”) and DPUB-ARIA accessibility roles, so those labels aren’t hardcoded in every chapter and can differ per format.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Emoji, handled three ways&lt;/strong&gt;: color emoji render fine on screen, but &lt;code&gt;strip-emoji-kindle.lua&lt;/code&gt; swaps them for Kindle-safe glyphs (e-ink has no emoji font) while &lt;code&gt;body-emoji-images.lua&lt;/code&gt; turns them into inline Twemoji images for the print PDF.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;tagline-share-links.lua&lt;/code&gt;&lt;/strong&gt;: appends a “share this idea” permalink after each of the book’s bumper-sticker lines in the EPUB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-dark-box&quot;&gt;The dark box&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-dark-box&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Turning emoji into images solved the missing-font problem and created a subtler one. Twemoji’s PNGs are transparent, but the RGB &lt;em&gt;underneath&lt;/em&gt; the transparent pixels is dark slate: invisible if a renderer honors the alpha channel, an ugly charcoal box if it doesn’t. And whether a renderer honors alpha turned out to depend entirely on where the book got opened.&lt;/p&gt;
&lt;p&gt;On Kindle, my first fix baked the callout’s background color into each PNG and dropped the alpha outright. It looked perfect on a light-mode Paperwhite and terrible on Kindle for iOS, which &lt;em&gt;does&lt;/em&gt; honor transparency and doesn’t paint the callout tint behind the image, so every emoji got a colored rectangle instead of a dark one. I’d traded one box for another. What shipped keeps the alpha intact but rewrites the RGB of every fully transparent pixel to white: renderers that honor alpha get clean transparency; the ones that flatten it get white, which vanishes on a white page.&lt;/p&gt;
&lt;p&gt;The print PDF needed the &lt;em&gt;opposite&lt;/em&gt; fix. Ghostscript converts the paperback to PDF/X-1a with &lt;code&gt;-dNOTRANSPARENCY&lt;/code&gt; (required to keep the text as selectable vectors instead of a 300-DPI bitmap) and that flag &lt;em&gt;always&lt;/em&gt; drops the alpha. So there, transparency is the enemy: each emoji is flattened onto the exact color it sits on (the callout’s gray, or white for body text) with no alpha at all, so there’s nothing to drop and no box to reveal. &lt;a id=&quot;quote-opposite-fixes&quot; href=&quot;#quote-opposite-fixes&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;opposite-fixes&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The same bug needed opposite fixes: one runtime might drop the alpha, the other was guaranteed to.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; A Python script bakes both sets at build time.&lt;/p&gt;
&lt;p&gt;It’s the most ordinary bug in the world. Mine just happened to be in a book.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-emoji&quot; id=&quot;user-content-fnref-emoji&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;rendering--post-processing-per-format&quot;&gt;Rendering &amp;#x26; post-processing per format&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#rendering--post-processing-per-format&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With the tree transformed, each format renders down its own path, and the paperback takes the longest road. HTML and the PDF both render through Pandoc, but the PDF is drawn by &lt;a href=&quot;https://weasyprint.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WeasyPrint&lt;/a&gt;, an HTML/CSS-to-PDF engine, so I typeset the entire book with the same box model I’d use for a web page. The EPUB goes a different way: Pandoc embeds WOFF2 font subsets, then &lt;code&gt;postprocess-epub.js&lt;/code&gt; repackages it to stay valid and small. And the paperback keeps going after everyone else has stopped, out to Ghostscript to become &lt;strong&gt;PDF/X-1a:2001&lt;/strong&gt; (&lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Cyan, Magenta, Yellow, and Key (black)—the four-ink color model used for print, as opposed to screen RGB&quot; title=&quot;Cyan, Magenta, Yellow, and Key (black)—the four-ink color model used for print, as opposed to screen RGB&quot; tabindex=&quot;0&quot;&gt;CMYK&lt;/abbr&gt; color, an embedded USWebCoatedSWOP ICC profile, flattened transparency), the archaic print standard IngramSpark (the print-on-demand distributor) won’t live without.&lt;/p&gt;
&lt;p&gt;That same 576-page gate does double duty here. &lt;code&gt;check-pdf-page-count.js&lt;/code&gt; pins the count and fails the build the moment the paperback drifts off 576 pages: cheap insurance against a stale page count shipping as a wrongly-sized cover spine.&lt;/p&gt;
&lt;p&gt;That’s five publishable files out of one Markdown source&lt;sup&gt;&lt;a href=&quot;#user-content-fn-docx&quot; id=&quot;user-content-fnref-docx&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;15&lt;/a&gt;&lt;/sup&gt;, which raises the obvious question: how do I know any of them are actually correct?&lt;/p&gt;
&lt;h3 id=&quot;testing-the-build&quot;&gt;Testing the build&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#testing-the-build&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The prose linters keep the &lt;em&gt;words&lt;/em&gt; honest; a larger slice of that Vitest suite keeps the &lt;em&gt;build&lt;/em&gt; honest. A Pandoc upgrade or a stray line of CSS can silently break a format, and I wouldn’t find out until a reader’s Kindle rendered the wrong font. The tests check the machinery: that each transform does what it claims, and (the biggest cluster by far) that typography hasn’t quietly drifted across small caps, table borders, print contrast, dark mode, and the e-ink emoji fallback. Each is a test because each is a way I’ve watched a build look fine and render wrong.&lt;/p&gt;
&lt;p&gt;But unit tests only prove &lt;em&gt;my&lt;/em&gt; code is right, not that the &lt;em&gt;output&lt;/em&gt; is valid. So each artifact also runs through the same validators the stores themselves use, and a rejection happens on my laptop, not on upload day. Every EPUB goes through &lt;a href=&quot;https://www.w3.org/publishing/epubcheck/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EPUBCheck&lt;/a&gt;, the validator every store runs on upload, and &lt;a href=&quot;https://daisy.org/activities/software/ace/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ace by DAISY&lt;/a&gt;, an accessibility audit built specifically for ebooks; the built HTML gets crawled by &lt;a href=&quot;https://github.com/lycheeverse/lychee&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;lychee&lt;/a&gt; for link rot and run through an HTML validator before malformed markup can become a malformed ebook.&lt;/p&gt;
&lt;p&gt;None of this is something I have to remember to run. Every push to &lt;code&gt;main&lt;/code&gt; kicks off &lt;strong&gt;14 parallel CI jobs&lt;/strong&gt;: build the CSS, then the EPUB, Kindle EPUB, print PDF, PDF/X-1a, HTML, and DOCX side by side, and validate each as its artifact comes ready. A green check means every format built and passed every gate; a red X means something’s off before I’ve even alt-tabbed away.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# build.yml — mostly independent jobs, so CI runs them in parallel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;jobs&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  build-epub&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:            &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# → dist/book.epub&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  build-kindle&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:          &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# → Kindle-specific EPUB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  build-pdf&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:             &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# → 6&quot;×9&quot; paperback PDF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  build-pdf-ingramspark&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# → PDF/X-1a for IngramSpark&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  build-html&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:            &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# → web preview&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  validate-epubcheck&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;needs&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;build-epub&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  validate-ace&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;:       { &lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;needs&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;build-epub&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;  validate-playwright&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#22863A;--shiki-dark:#85E89D&quot;&gt;needs&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;build-html&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure&gt;&lt;img src=&quot;/assets/img/book-ci-build-graph-1600x794.png&quot; alt=&quot;A GitHub Actions run of build.yml with fourteen parallel jobs (lint-and-test, detect-duplicates, build-css, build-epub, build-kindle, build-pdf, build-html, build-docx, and Build PDF/X-1a, then validate-epubcheck, validate-ace, validate-links, validate-html, and validate-playwright), every one passing green, total duration five minutes eight seconds.&quot; title=&quot;Every push to main kicks off 14 parallel CI jobs.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1600&quot; height=&quot;794&quot;&gt;&lt;figcaption&gt;A GitHub Actions run of build.yml with fourteen parallel jobs (lint-and-test, detect-duplicates, build-css, build-epub, build-kindle, build-pdf, build-html, build-docx, and Build PDF/X-1a, then validate-epubcheck, validate-ace, validate-links, validate-html, and validate-playwright), every one passing green, total duration five minutes eight seconds.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;And because every build stamps its commit onto the title page, the book is versioned like software. I’m on release 1.0.1, with a tag and a changelog, not a graveyard of &lt;code&gt;final_v3_revised_ACTUALLY_FINAL.docx&lt;/code&gt; files. A typo fix is a point release.&lt;/p&gt;
&lt;h2 id=&quot;publishing&quot;&gt;Publishing&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#publishing&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For all that automation, the actual publishing is almost entirely click-ops. There’s no &lt;code&gt;git push&lt;/code&gt; to production. Each store (Amazon’s &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Kindle Direct Publishing—Amazon&amp;#x27;s self-publishing platform for ebooks and print-on-demand&quot; title=&quot;Kindle Direct Publishing—Amazon&amp;#x27;s self-publishing platform for ebooks and print-on-demand&quot; tabindex=&quot;0&quot;&gt;KDP&lt;/abbr&gt;, IngramSpark for bookstores and libraries, Draft2Digital for Apple Books and Kobo) wants you to log into a web dashboard, upload the EPUB and the print PDF by hand, and re-enter the same metadata (title, description, &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Book Industry Standards and Communications—the standardized subject codes that tell stores which shelf a book belongs on&quot; title=&quot;Book Industry Standards and Communications—the standardized subject codes that tell stores which shelf a book belongs on&quot; tabindex=&quot;0&quot;&gt;BISAC&lt;/abbr&gt; categories, keywords, price) into a slightly different form each time. &lt;a id=&quot;quote-upload-like-2009&quot; href=&quot;#quote-upload-like-2009&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;upload-like-2009&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;My pipeline builds a flawless artifact, and then I upload it like it’s 2009.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A few things kept even that part honest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I’m my own publisher.&lt;/strong&gt; I formed an LLC and bought my own ISBNs (one per format) so the catalogs list me as the publisher, not a free platform ISBN with the retailer’s name on it. That also means none of the vendor lock-in that comes with one, since a free ISBN only publishes through the platform that handed it out.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wide, not exclusive.&lt;/strong&gt; I skipped Amazon’s KDP Select, which pays a bit more in exchange for locking the book to Amazon, so the book could be available everywhere at once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The metadata is versioned too.&lt;/strong&gt; The description, categories, and keywords live in the repo (a single source of truth I can diff), and an &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;ONline Information eXchange—the publishing industry&amp;#x27;s standard metadata feed for a book (title, price, categories)&quot; title=&quot;ONline Information eXchange—the publishing industry&amp;#x27;s standard metadata feed for a book (title, price, categories)&quot; tabindex=&quot;0&quot;&gt;ONIX&lt;/abbr&gt; feed is generated from it for the channels that accept one. I still paste it into web forms by hand, but at least I’m pasting from a file under version control.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;looking-forward&quot;&gt;Looking forward&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#looking-forward&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The nice thing about a book that builds like software: the same machinery keeps paying off after launch.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Translations.&lt;/strong&gt; Because the content is structured Markdown, translating it is closer to localizing an app than retyping a manuscript. I’ve got a pipeline (Brazilian Portuguese, Latin American Spanish, German) that drafts a translation of the finished English book, pins every heading ID so cross-references don’t break, enforces a glossary so key terms stay consistent, and back-translates the result to check it against the original: the same trust-but-verify instinct as the prose linters, just across languages.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-translations&quot; id=&quot;user-content-fnref-translations&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;16&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An audiobook.&lt;/strong&gt; If I record one, turning 100,000 words into narration opens a whole new class of bug: mispronunciations. So the audiobook has its own QA suite. It transcribes the generated audio back to text and diffs that against the script, checks &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;The smallest unit of sound that distinguishes one word from another—like the soft &amp;#x22;j&amp;#x22; and hard &amp;#x22;g&amp;#x22; that split JIF from GIF&quot; title=&quot;The smallest unit of sound that distinguishes one word from another—like the soft &amp;#x22;j&amp;#x22; and hard &amp;#x22;g&amp;#x22; that split JIF from GIF&quot; tabindex=&quot;0&quot;&gt;phonemes&lt;/abbr&gt; on the tricky words, and audits pronunciations, all wired into their own CI workflows. It’s a snapshot test, aimed at my own voice.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-audiobook&quot; id=&quot;user-content-fnref-audiobook&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;17&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Releasing the tools.&lt;/strong&gt; Almost none of this is specific to my book: the validators, the Lua filters, the whole pipeline. I’d like to clean them up and put them out as open source (a noun, no hyphen), so the next person doesn’t have to build it from scratch. And none of it would exist without the open source projects it already stands on. The whole book is other people’s generosity, compiled.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-oss&quot; id=&quot;user-content-fnref-oss&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;18&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One Markdown source, a linter with opinions, and a build that yells when I’m wrong. Here’s what all that over-engineering added up to.&lt;/p&gt;
&lt;h3 id=&quot;by-the-numbers&quot;&gt;By the numbers&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#by-the-numbers&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;









































&lt;table class=&quot;w-full border-collapse&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Metric&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;Count&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Words&lt;/td&gt;&lt;td&gt;~100,000 across ~70 chapters and sections&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Print length&lt;/td&gt;&lt;td&gt;576 pages (6”×9”)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Published formats&lt;/td&gt;&lt;td&gt;5 (plus a DOCX I don’t ship)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tests&lt;/td&gt;&lt;td&gt;~70 files, 2,204 cases, ~5,500 automated checks&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CI jobs per push&lt;/td&gt;&lt;td&gt;14, in parallel&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Style rules&lt;/td&gt;&lt;td&gt;~300 covering 500+ banned terms, over a 5,000-rule grammar engine&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Commits&lt;/td&gt;&lt;td&gt;5,000+ to get here&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Books&lt;/td&gt;&lt;td&gt;1 (frankly over-engineered, but fun)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;aside class=&quot;objection&quot; aria-label=&quot;Objection and response&quot;&gt;&lt;p class=&quot;objection-q&quot;&gt;&lt;span class=&quot;objection-label&quot;&gt;But what about…&lt;/span&gt; Wasn’t this over-engineered, procrastination dressed up as rigor?&lt;/p&gt;&lt;div class=&quot;objection-a&quot;&gt;
&lt;p&gt;Over-engineered, absolutely. But procrastination it wasn’t: of the ~5,000 commits here, only about one in five touched the build tooling at all. The other four were the book. Every check caught something I’d otherwise have shipped. Would I do it again? Without hesitation.&lt;/p&gt;
&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The tooling outgrew the book, too. I pointed the whole apparatus at this blog’s fifteen-year archive and caught a 2011 post that had been saying “its clear” where it meant “it’s clear” the entire time, the exact missing apostrophe my linter now flags on every keystroke. Thousands of people read it; nobody ever mentioned it. &lt;a id=&quot;quote-you-have-to-go-looking&quot; href=&quot;#quote-you-have-to-go-looking&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;you-have-to-go-looking&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;You don’t find out. You have to go looking.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And it wasn’t only the tooling that transferred. The habits did. The instincts I sharpened here (treat a mistake caught once as a rule that catches it forever, keep a human in the loop on anything a machine drafts, make the build the source of truth) have already found their way into how I run my other projects, this site included.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-resumepdf&quot; id=&quot;user-content-fnref-resumepdf&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;19&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I’m not going to tell you to write 5,500 tests for your novel. For most books, most of this is wildly disproportionate, and that’s the point. I did it because the marginal cost of one more check, one more format, one more validator was a few minutes and a little curiosity, and because doing it the developer way meant I could write from an iPad away from my desk and trust that &lt;code&gt;main&lt;/code&gt; was always shippable. If you spend your days shipping software, the tools you already know will take you further than you’d expect. &lt;a id=&quot;quote-started-with-a-git-repo&quot; href=&quot;#quote-started-with-a-git-repo&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;started-with-a-git-repo&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The publishing industry starts with a word processor. I started with a Git repository&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. And I’d start there again.&lt;/p&gt;
&lt;p&gt;What would you over-engineer, if you pointed your own tools at it?&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-opensource&quot;&gt;
&lt;p&gt;A colleague called me out on “open-source” in an early draft; I vowed never to make the mistake again, then made a linter swear to it for me. &lt;a href=&quot;#user-content-fnref-opensource&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-latex&quot;&gt;
&lt;p&gt;Chief among them: I lost a weekend to &lt;a href=&quot;https://www.latex-project.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LaTeX&lt;/a&gt;, on the theory that the tool serious typesetters swear by had to be worth learning. It is, if you typeset for a living. For me it was a wall of arcane macros and inscrutable errors to approximate a layout CSS gave me in an afternoon. I bailed and never looked back, which is how the whole book ended up drawn with a web box model instead. &lt;a href=&quot;#user-content-fnref-latex&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cspell&quot;&gt;
&lt;p&gt;I’m a terrible speller so I also ran &lt;a href=&quot;https://github.com/streetsidesoftware/cspell&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;CSpell&lt;/a&gt; in VS Code, which shared a custom dictionary with Harper. &lt;a href=&quot;#user-content-fnref-cspell&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-languagetool&quot;&gt;
&lt;p&gt;Self-hosted, so it ran on-device too, well, in the Codespace. &lt;a href=&quot;#user-content-fnref-languagetool&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-validators&quot;&gt;
&lt;p&gt;The roster, grouped. &lt;strong&gt;Typography:&lt;/strong&gt; &lt;code&gt;validateSmallcaps&lt;/code&gt; and &lt;code&gt;validateEscapedSmallcaps&lt;/code&gt; (acronyms rendered in small caps, not accidentally escaped), &lt;code&gt;validateEmDashes&lt;/code&gt;, &lt;code&gt;validateNumberRangeDashes&lt;/code&gt; (en-dash ranges), &lt;code&gt;validateQuotePunctuation&lt;/code&gt;, &lt;code&gt;validateListPunctuation&lt;/code&gt;, &lt;code&gt;validateBoldKeywords&lt;/code&gt;. &lt;strong&gt;Structure:&lt;/strong&gt; &lt;code&gt;validateHeadingHierarchy&lt;/code&gt; (no skipped levels), &lt;code&gt;validateDuplicateHeadings&lt;/code&gt;, &lt;code&gt;validateParagraphLength&lt;/code&gt; (no walls of text), &lt;code&gt;validateMinOpenerLength&lt;/code&gt;, &lt;code&gt;validateSectionIntros&lt;/code&gt; / &lt;code&gt;validateSectionIntroHasLead&lt;/code&gt; / &lt;code&gt;validateSectionIntroFinalLead&lt;/code&gt; / &lt;code&gt;validateSectionIntroNoTldr&lt;/code&gt; (section intros lead into the content without restating the TL;DR), &lt;code&gt;validateTldrLength&lt;/code&gt;, &lt;code&gt;validateChapter&lt;/code&gt;, &lt;code&gt;validateIndexIntegrity&lt;/code&gt; (&lt;code&gt;index.yml&lt;/code&gt; matches the files on disk). &lt;strong&gt;Links &amp;#x26; references:&lt;/strong&gt; &lt;code&gt;validateCrossReferences&lt;/code&gt;, &lt;code&gt;validateNoBareUrlLinkText&lt;/code&gt;, &lt;code&gt;validateFootnotes&lt;/code&gt;, &lt;code&gt;validateAcronymExpansion&lt;/code&gt; (expanded on first use). &lt;strong&gt;Callouts:&lt;/strong&gt; &lt;code&gt;validateCalloutBalance&lt;/code&gt; (managers paired with ICs), &lt;code&gt;validateCalloutDivs&lt;/code&gt;, &lt;code&gt;validateRoleCalloutLabels&lt;/code&gt;. &lt;strong&gt;Voice &amp;#x26; anti-AI:&lt;/strong&gt; &lt;code&gt;validateGitHubTense&lt;/code&gt;, &lt;code&gt;validateOpenSourceHyphenation&lt;/code&gt;, &lt;code&gt;validateHypotheticalHooks&lt;/code&gt;, &lt;code&gt;validateSentenceStarters&lt;/code&gt;, &lt;code&gt;validateDoubledWords&lt;/code&gt;. &lt;strong&gt;Housekeeping:&lt;/strong&gt; &lt;code&gt;validateNoTodoMarkers&lt;/code&gt;, &lt;code&gt;validateNoTransitionTodos&lt;/code&gt;, &lt;code&gt;validateBuildIdentifiers&lt;/code&gt;, &lt;code&gt;validateSSML&lt;/code&gt; (audiobook markup). &lt;a href=&quot;#user-content-fnref-validators&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-dry&quot;&gt;
&lt;p&gt;jscpd is not prose-specific. It’s a great way to DRY up code. &lt;a href=&quot;#user-content-fnref-dry&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-lenses&quot;&gt;
&lt;p&gt;The other twelve, briefly: Consistency (drift in the book’s own conventions), Taglines (bumper-sticker lines worth pulling into callouts), Proofread (typos and mechanical slips), Cross-reference (broken “see chapter X” pointers), Acronyms (unexpanded on first use), Jargon (the “explain it to a new hire” test), Alt-text (missing or non-descriptive), Evidence (assertions with no why or how), Structure (heading hierarchy), Readability (sentences hard to parse on one read), Inclusive (non-inclusive language, judged in context), and Emphasis (overused bold, italics, and scare quotes). &lt;a href=&quot;#user-content-fnref-lenses&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-deterministic&quot;&gt;
&lt;p&gt;&lt;code&gt;audit-chapter-lengths.js&lt;/code&gt; / &lt;code&gt;audit-paragraph-lengths.js&lt;/code&gt; flag outlier chapters and walls of text; &lt;code&gt;reading-time.js&lt;/code&gt; clocks each chapter and the whole book at 238 wpm (average adult non-fiction speed); &lt;code&gt;check-epub-size.js&lt;/code&gt; enforces the size budget; &lt;code&gt;consistency-check.js&lt;/code&gt; settles “open source” vs “open-source” and “async” vs “asynchronous” book-wide; and &lt;code&gt;writing-dashboard.js&lt;/code&gt; renders it all to HTML. Plus a handful of advisory local checkers (&lt;code&gt;proselint&lt;/code&gt;, GNU &lt;code&gt;diction&lt;/code&gt;, and GNU &lt;code&gt;style&lt;/code&gt;) for wordiness and readability stats. &lt;a href=&quot;#user-content-fnref-deterministic&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 8&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-kindlewebkit&quot;&gt;
&lt;p&gt;Not just a metaphor. Kindle’s modern format (KF8/AZW3) is rendered by a WebKit-based engine (the same rendering lineage as Safari) down to Amazon’s own &lt;code&gt;-webkit-&lt;/code&gt; CSS extensions; a Kindle book really is a little offline web page. But the engine ages slowly across a long tail of e-ink models still in readers’ hands, so the HTML and CSS it reliably supports lag years behind the modern web, hence “very stripped-down.” &lt;a href=&quot;#user-content-fnref-kindlewebkit&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 9&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-cover&quot;&gt;
&lt;p&gt;AI could have generated something competent, and that was exactly the problem: competent and generic is the failure mode I spent 5,500 checks scrubbing out of the &lt;em&gt;text&lt;/em&gt;. The cover is the one thing a reader judges before reading a word, so it was the last place I wanted a machine’s default taste. A human designer got the brief; the algorithm didn’t. &lt;a href=&quot;#user-content-fnref-cover&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 10&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-pagecount&quot;&gt;
&lt;p&gt;Locking the length isn’t fussiness. The whole physical book is built around that number. A page count sets the book’s thickness, so the spine width, and the cover art wrapped around it, is computed from it. Drift off 576 pages and the cover no longer fits the spine: fixing it means a round trip back to the human designer to re-render the wrap, not a one-line code change. &lt;a href=&quot;#rendering--post-processing-per-format&quot;&gt;More on that gate below&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-pagecount&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 11&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-visual&quot;&gt;
&lt;p&gt;The rest, in the same spirit: the copyright page is present and bottom-aligned on screen; H2 is larger than body text and H3 smaller than H2; links are underlined, code blocks have a dark background and inline code a visible one, and blockquotes carry a left border; and body text uses the Tailwind Typography &lt;code&gt;prose&lt;/code&gt; class. &lt;a href=&quot;#user-content-fnref-visual&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 12&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-preprocess&quot;&gt;
&lt;p&gt;&lt;code&gt;update-revision.js&lt;/code&gt; stamps the build SHA and version onto the title page, so every copy traces to its commit; &lt;code&gt;links-to-footnotes.js&lt;/code&gt; does the print link-to-footnote rewrite; and a per-format manifest handles the reshuffle. Same source, subtly different books. &lt;a href=&quot;#user-content-fnref-preprocess&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 13&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-emoji&quot;&gt;
&lt;p&gt;I spent years at GitHub, where emoji are practically part of the syntax (reactions, &lt;code&gt;:shipit:&lt;/code&gt;, an emoji picker in every comment box) and they turned up a fresh encoding edge case like clockwork. Emoji, uh, find a way. &lt;a href=&quot;#user-content-fnref-emoji&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 14&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-docx&quot;&gt;
&lt;p&gt;The build actually emits a sixth artifact (a DOCX) which I used to gather early feedback from reviewers in Word, with track changes and comments. It’s a working format, not something I publish. &lt;a href=&quot;#user-content-fnref-docx&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 15&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-translations&quot;&gt;
&lt;p&gt;The pipeline drafts and self-checks, but it doesn’t get the final word. If I decide to actually publish any of these, a human native speaker will review and proof the translation first: machine-assisted, human-approved. &lt;a href=&quot;#user-content-fnref-translations&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 16&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-audiobook&quot;&gt;
&lt;p&gt;The QA suite catches drift, but a transcript diff can’t hear a flat line reading or a subtly wrong emphasis. If I release it, I’ll listen to the finished narration end to end, every word ear-checked by a human before it ships. &lt;a href=&quot;#user-content-fnref-audiobook&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 17&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-oss&quot;&gt;
&lt;p&gt;The full list, with thanks to their maintainers: &lt;a href=&quot;https://tailwindcss.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tailwind CSS&lt;/a&gt; and &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Tailwind Typography&lt;/a&gt;, &lt;a href=&quot;https://pandoc.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pandoc&lt;/a&gt;, &lt;a href=&quot;https://weasyprint.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;WeasyPrint&lt;/a&gt;, &lt;a href=&quot;https://www.ghostscript.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ghostscript&lt;/a&gt;, &lt;a href=&quot;https://github.com/qpdf/qpdf&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;qpdf&lt;/a&gt;, &lt;a href=&quot;https://poppler.freedesktop.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Poppler&lt;/a&gt;, &lt;a href=&quot;https://github.com/adobe-fonts/source-serif&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Source Serif 4&lt;/a&gt;, &lt;a href=&quot;https://github.com/adobe-fonts/source-sans&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Source Sans 3&lt;/a&gt;, and &lt;a href=&quot;https://github.com/adobe-fonts/source-code-pro&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Source Code Pro&lt;/a&gt; by Adobe, &lt;a href=&quot;https://github.com/jdecked/twemoji&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Twemoji&lt;/a&gt;, &lt;a href=&quot;https://imagemagick.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ImageMagick&lt;/a&gt;, &lt;a href=&quot;https://sharp.pixelplumbing.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;sharp&lt;/a&gt;, &lt;a href=&quot;https://github.com/svg/svgo&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;SVGO&lt;/a&gt;, &lt;a href=&quot;https://postcss.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PostCSS&lt;/a&gt; and &lt;a href=&quot;https://cssnano.github.io/cssnano/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;cssnano&lt;/a&gt;, &lt;a href=&quot;https://remark.js.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Remark&lt;/a&gt; and &lt;a href=&quot;https://github.com/retextjs/retext&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Retext&lt;/a&gt; (and countless ecosystem plugins), &lt;a href=&quot;https://textlint.github.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;textlint&lt;/a&gt;, &lt;a href=&quot;https://github.com/DavidAnson/markdownlint&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;markdownlint&lt;/a&gt;, &lt;a href=&quot;https://prettier.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Prettier&lt;/a&gt;, &lt;a href=&quot;https://writewithharper.com/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Harper&lt;/a&gt;, &lt;a href=&quot;https://vitest.dev/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vitest&lt;/a&gt;, &lt;a href=&quot;https://playwright.dev/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Playwright&lt;/a&gt; and &lt;a href=&quot;https://github.com/dequelabs/axe-core&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;axe-core&lt;/a&gt;, &lt;a href=&quot;https://daisy.org/activities/software/ace/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ace by DAISY&lt;/a&gt;, &lt;a href=&quot;https://www.w3.org/publishing/epubcheck/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;EPUBCheck&lt;/a&gt;, &lt;a href=&quot;https://github.com/gjtorikian/html-proofer&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;htmlproofer&lt;/a&gt;, &lt;a href=&quot;https://github.com/lycheeverse/lychee&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;lychee&lt;/a&gt;, &lt;a href=&quot;https://html-validate.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;html-validate&lt;/a&gt;, &lt;a href=&quot;https://python-pillow.github.io/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Pillow&lt;/a&gt;, &lt;a href=&quot;https://languagetool.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LanguageTool&lt;/a&gt;, &lt;a href=&quot;https://vale.sh/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Vale&lt;/a&gt;, &lt;a href=&quot;https://github.com/amperser/proselint&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;proselint&lt;/a&gt;, &lt;a href=&quot;https://www.gnu.org/software/diction/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GNU Diction&lt;/a&gt;, &lt;a href=&quot;https://github.com/adrienverge/yamllint&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;yamllint&lt;/a&gt;, &lt;a href=&quot;https://github.com/nodeca/js-yaml&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;js-yaml&lt;/a&gt;, &lt;a href=&quot;https://github.com/kucherenko/jscpd&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;jscpd&lt;/a&gt;, and &lt;a href=&quot;https://github.com/casey/just&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;just&lt;/a&gt;. &lt;a href=&quot;#user-content-fnref-oss&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 18&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-resumepdf&quot;&gt;
&lt;p&gt;Case in point: &lt;a href=&quot;/resume.pdf&quot;&gt;my résumé&lt;/a&gt; is a print-only web page the build renders straight to PDF, a different engine than the book (headless Chrome here, WeasyPrint there), but the same “&lt;a href=&quot;#quote-ebook-trench-coat&quot;&gt;an ebook is a website in a trench coat&lt;/a&gt;” idea, pointed at paper. &lt;a href=&quot;#user-content-fnref-resumepdf&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 19&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Open and async on the Overcommitted podcast</title><link>https://ben.balter.com/2026/07/28/overcommitted-open-and-async/</link><guid isPermaLink="true">https://ben.balter.com/2026/07/28/overcommitted-open-and-async/</guid><description>What makes a remote engineering team work isn&apos;t where people sit. It&apos;s how they communicate. A conversation on the Overcommitted podcast about async-first culture, decision durability, and the ideas behind Open and Async.</description><pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/07/28/overcommitted-open-and-async/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a id=&quot;quote-decision-without-a-url&quot; href=&quot;#quote-decision-without-a-url&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;decision-without-a-url&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;A decision without a URL gets relitigated forever.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; What makes a &lt;a href=&quot;/2026/07/21/open-and-async/&quot;&gt;remote engineering team&lt;/a&gt; actually work comes down to how you communicate, not where you sit. Most of it is a set of habits you can build.&lt;/p&gt;
&lt;p&gt;Those habits are the backbone of &lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-overcommitted-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Open and Async&lt;/em&gt;&lt;/a&gt;, and the throughline of a conversation with Bethany Janos and Erika Eggemeyer on the &lt;a href=&quot;https://overcommitted.dev/open-and-async-work-remote-engineering-culture-with-ben-balter/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Overcommitted&lt;/a&gt; podcast: why &lt;a href=&quot;/2022/03/17/why-async/&quot;&gt;async is the operating system&lt;/a&gt; and remote is the hardware, how to run a stack where chat coordinates but issues and discussions carry the &lt;a href=&quot;/2015/11/12/why-urls/&quot;&gt;durable decisions&lt;/a&gt; you can point to six months later, and &lt;a href=&quot;/2026/03/04/thirteen-years-at-github/&quot;&gt;why you measure outcomes instead of keystrokes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The conversation runs to the nerdier end too: the absurd, over-engineered &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Continuous Integration—automatically building and testing every change&quot; title=&quot;Continuous Integration—automatically building and testing every change&quot; tabindex=&quot;0&quot;&gt;CI&lt;/abbr&gt; pipeline behind the book (a rabbit hole that’s getting its own post soon), the aspirational automation that syncs my standing desk to my calendar, and where AI actually belongs when you write.&lt;/p&gt;
&lt;p&gt;You can listen to the full conversation &lt;a href=&quot;https://overcommitted.dev/open-and-async-work-remote-engineering-culture-with-ben-balter/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;over on the overcommitted website&lt;/a&gt;, or wherever you get your podcasts.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Open and Async: the remote-work playbook is out</title><link>https://ben.balter.com/2026/07/21/open-and-async/</link><guid isPermaLink="true">https://ben.balter.com/2026/07/21/open-and-async/</guid><description>Open and Async is the practical playbook for making remote and distributed work actually work—two habits, working in the open and communicating asynchronously, drawn from a decade of remote-first lessons at GitHub.</description><pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/07/21/open-and-async/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Most companies didn’t go remote. They shipped everyone a laptop, bolted Zoom onto the same approval chains, and called it a day. &lt;a id=&quot;quote-q-that-wasnt-remote-workit-was&quot; href=&quot;#quote-q-that-wasnt-remote-workit-was&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;q-that-wasnt-remote-workit-was&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;That wasn’t remote work—it was office work in sweatpants: the same meetings, the same status theater, the same “quick syncs,” just piped through a webcam.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-work-from-home-not-remote&quot; href=&quot;#quote-work-from-home-not-remote&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;work-from-home-not-remote&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Working from home ≠ working remotely.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; There’s a world of difference between being forced out of the office and intentionally building a distributed, async culture. That difference is what &lt;em&gt;Open and Async&lt;/em&gt; is about—and as of today, you can &lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-launch-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;read it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-launch-post&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Open and Async&lt;/em&gt;&lt;/a&gt; is the opinionated, practical playbook for the two practices that make distributed work actually work: &lt;strong&gt;working in the open&lt;/strong&gt; and &lt;a href=&quot;/2022/03/17/why-async/&quot;&gt;&lt;strong&gt;communicating asynchronously&lt;/strong&gt;&lt;/a&gt;. Async is the operating system; remote is the hardware. It’s how the next generation of tech leaders already think—and it’s a learnable skill for everyone else.&lt;/p&gt;
&lt;p&gt;If you’ve spent any time on this blog, the argument will feel familiar—the book grew out of these posts. It’s where the scattered pieces finally become one ordered playbook: the full case, start to finish, with the connective tissue no single post ever had room for. Everything you’ve read here, plus the two-thirds that never made it to the blog.&lt;/p&gt;
&lt;h2 id=&quot;what-separates-teams-that-thrive&quot;&gt;What separates teams that thrive&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-separates-teams-that-thrive&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;More than a decade of remote-first work at &lt;a href=&quot;/2026/03/04/thirteen-years-at-github/&quot;&gt;GitHub made one thing clear&lt;/a&gt;: the tooling was never what separated the teams that thrived from the ones that just survived. Companies would adopt those workflows wholesale—add issues and pull requests on top of a hierarchy-driven approval process—and wonder why nothing changed. Tools: right. Bottlenecks: intact. The result was Zoom fatigue plus all the old dysfunction, now faster.&lt;/p&gt;
&lt;p&gt;The teams that pulled ahead did something different. They gave every decision &lt;a href=&quot;/2015/11/12/why-urls/&quot;&gt;a URL&lt;/a&gt; so context survived reorgs and departures. They treated meetings as escalation, not default. They &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;worked loudly&lt;/a&gt; so impact was visible without anyone performing busyness. None of it was intuition—it was a set of repeatable practices, and &lt;em&gt;Open and Async&lt;/em&gt; is those practices, written down so you can put them to work.&lt;/p&gt;
&lt;h2 id=&quot;who-its-for&quot;&gt;Who it’s for&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#who-its-for&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two audiences, both in remote and distributed work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Managers&lt;/strong&gt;—build teams that ship without constant synchronous coordination, and retain your best people by respecting their time, focus, and intelligence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Individual contributors&lt;/strong&gt;—become visible, effective, and promotable on the strength of your work, not the luck of your seating chart. Interviewing at a remote-first company? This is the playbook they wish you’d already read.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-inside&quot;&gt;What’s inside&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#whats-inside&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few of the things you’ll learn how to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2023/04/20/meetings-are-a-point-of-escalation/&quot;&gt;Treat meetings as a point of escalation, not the default&lt;/a&gt;—and reclaim the hours you’re losing to “quick syncs.”&lt;/li&gt;
&lt;li&gt;Give every decision a URL so context survives reorgs, departures, and Slack purges.&lt;/li&gt;
&lt;li&gt;Run 1:1s, weekly reports, and career conversations that aren’t a tax on everyone’s time.&lt;/li&gt;
&lt;li&gt;Lead—whether or not you have direct reports—by showing your work.&lt;/li&gt;
&lt;li&gt;Hire, onboard, and transition teams to remote-first work without losing what made them good.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s 575 pages, and the method ships as more than a manuscript. There’s even a &lt;a href=&quot;https://github.com/open-and-async/mcp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Model Context Protocol (MCP) server&lt;/a&gt; that drops the book’s async-first practices straight into your editor, so an AI agent can draft a decision doc or pressure-test a status update against the same rubric the book teaches.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-stop-digitizing-the-office&quot; href=&quot;#quote-stop-digitizing-the-office&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;stop-digitizing-the-office&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Stop digitizing the office. Start building something better.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Work loudly</title><link>https://ben.balter.com/2026/07/14/work-loudly/</link><guid isPermaLink="true">https://ben.balter.com/2026/07/14/work-loudly/</guid><description>In an office, some of your visibility is free. Go remote and it drops to zero. Working loudly makes your impact visible as you do it—not after the fact in a status update nobody reads.</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/07/14/work-loudly/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You did the work. The hard kind—the three weeks of chasing a race condition that only showed up under load, the careful refactor that made the next six features trivial, the quiet design call that saved the team a quarter of pain. Then your performance review comes around, and your manager—who likes you, who’s genuinely on your side—can’t quite say what you did. Not because the work wasn’t real. Because they never saw it happen.&lt;/p&gt;
&lt;p&gt;This is the part nobody warns you about when you go remote: In an office, some of your visibility was free. People saw you at the whiteboard, overheard you unblock someone in the kitchen, watched you stay late. None of that was a measure of your impact—but it added up to a vague, ambient sense that you were &lt;em&gt;doing things&lt;/em&gt;. Take away the building, and that ambient signal goes to zero. The work doesn’t get less valuable. It gets less visible. &lt;a id=&quot;quote-q-and-invisible-work-is-for&quot; href=&quot;#quote-q-and-invisible-work-is-for&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;q-and-invisible-work-is-for&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;And invisible work is, for every practical purpose—promotion, credit, getting pulled into the interesting projects—work that didn’t happen.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;work-in-the-open-not-in-a-summary&quot;&gt;Work in the open, not in a summary&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#work-in-the-open-not-in-a-summary&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The instinct most of us have is to fix this at the end. Do the work heads-down, then write the big summary, present the finished thing with a bow on it. That’s backwards. By the time you’re presenting, the interesting part is over—the reasoning, the dead ends, the moment you realized the obvious approach wouldn’t work. You’re narrating a conclusion when the value was in the process. This is &lt;a href=&quot;/2022/02/16/leaders-show-their-work/#the-value-of-showing-your-work&quot;&gt;showing your work&lt;/a&gt; applied in real time. The fix is to work loudly: &lt;a id=&quot;quote-make-the-work-visible-as-you-do-it&quot; href=&quot;#quote-make-the-work-visible-as-you-do-it&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;make-the-work-visible-as-you-do-it&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;make the work visible &lt;em&gt;as you do it&lt;/em&gt;, in shared and durable places, not after the fact in a status update nobody reads.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In practice that means thinking out loud where others can see it. The issue you open before you start, with what you’re trying and why. &lt;a href=&quot;/2023/05/19/pull-requests-are-a-form-of-documentations/&quot;&gt;The pull request description that explains the approach, not just the diff&lt;/a&gt; (it may even be longer than the diff). The “here’s where I’m stuck, here’s what I’ve ruled out” message in &lt;a href=&quot;/2014/11/06/rules-of-communicating-at-github/#12-surface-work-early-for-feedback&quot;&gt;a public channel instead of a private one&lt;/a&gt;. The rough draft shared at 40% instead of the polished thing shared at 100%. You’re not adding a layer of reporting on top of the work. You’re doing the work in the open, where it leaves a trail.&lt;/p&gt;
&lt;p&gt;I’ve opened a few thousand issues; the ones that mattered most were the ones I opened before I knew the answer. The tell: &lt;a id=&quot;quote-if-it-only-lives-in-your-head-or-a-dm-it-isnt-loud-yet&quot; href=&quot;#quote-if-it-only-lives-in-your-head-or-a-dm-it-isnt-loud-yet&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;if-it-only-lives-in-your-head-or-a-dm-it-isnt-loud-yet&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;if it only lives in your head or a DM, it isn’t loud yet.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;like-a-bear-bell&quot;&gt;Like a bear bell&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#like-a-bear-bell&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think of it like a bear bell (bells you attach to your pack when hiking bear country). You don’t ring it to show off—you ring it so the bear hears you coming. The danger on the trail isn’t the bear—it’s the &lt;em&gt;startled&lt;/em&gt; bear, the one you surprised rounding a blind corner. Every org has them: the stakeholder who learns about your decision after it ships, the team that discovers from the changelog that you rerouted around them. Surprise people like that, and they don’t get curious—they get defensive, territorial, loud.&lt;/p&gt;
&lt;p&gt;I learned this the hard way. GitHub’s (then) CEO and General Counsel once DM’d me about an urgent, time-sensitive feature. I assumed my own leadership was already in the loop, so my team went straight to work—focused on the time-critical work, not the time-critical communication. Our leadership first found out when the feature was ready to ship and we kicked off the launch: docs, blog post, the rollout. It was not good. The feature went out—there was an executive mandate behind it—but the launch had made the people I reported to look out of the loop in front of their peers. I don’t know that their trust in me ever fully recovered.&lt;/p&gt;
&lt;p&gt;Working loudly is the bell: the low-effort, ambient signal that you’re on the trail and headed their way. Your work and your decisions reach people as something they saw coming instead of an ambush, and they get the chance to respond early and productively.&lt;/p&gt;
&lt;h2 id=&quot;loud-not-bragging&quot;&gt;Loud, not bragging&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#loud-not-bragging&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s the objection I hear most.&lt;/p&gt;
&lt;aside class=&quot;objection&quot; aria-label=&quot;Objection and response&quot;&gt;&lt;p class=&quot;objection-q&quot;&gt;&lt;span class=&quot;objection-label&quot;&gt;But what about…&lt;/span&gt; Won’t I look like I’m bragging?&lt;/p&gt;&lt;div class=&quot;objection-a&quot;&gt;
&lt;p&gt;There’s a difference between narrating your work and performing it. Performing busyness is noise &lt;em&gt;about&lt;/em&gt; work—the “just pushed through lunch &lt;span role=&quot;img&quot; aria-label=&quot;flexed biceps&quot;&gt;💪&lt;/span&gt;” posts, the green-dot theater, the reply-all to look engaged. Working loudly is the work itself, made legible. One is activity cosplaying as impact. The other is impact you can actually point to. If you’re sharing the artifact—the issue, the &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Pull request—a proposed set of code changes submitted for review&quot; title=&quot;Pull request—a proposed set of code changes submitted for review&quot; tabindex=&quot;0&quot;&gt;PR&lt;/abbr&gt;, the doc, the decision—you’re working loudly. If you’re sharing your &lt;em&gt;effort&lt;/em&gt;, you’re just being loud.&lt;/p&gt;
&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;the-payoff-compounds&quot;&gt;The payoff compounds&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-payoff-compounds&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a id=&quot;quote-payoff-bigger-than-credit&quot; href=&quot;#quote-payoff-bigger-than-credit&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;payoff-bigger-than-credit&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;The payoff is bigger than getting credit, though you’ll get that too.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; When your work is visible in progress, people can help. Someone spots the bug in your approach before you’ve sunk three days into it. A teammate who solved the same problem last quarter drops a link. The senior engineer who’d never have been pulled into a private thread weighs in on the public one. Loud work compounds—it invites collaboration that silent work never gets the chance to.&lt;/p&gt;
&lt;p&gt;I like to joke that &lt;a id=&quot;quote-hardest-part-of-open-source&quot; href=&quot;#quote-hardest-part-of-open-source&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;hardest-part-of-open-source&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;the hardest part of open source is googling to find out whether someone already solved your problem&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; before you spend a week solving it yourself. Inner source—the same habits inside a company—is no different: work loudly so others can find you, and look around so you find them before you redo their work.&lt;/p&gt;
&lt;h2 id=&quot;managing-without-surveilling&quot;&gt;Managing without surveilling&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#managing-without-surveilling&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I once led a team at GitHub that split, roughly down the middle, into two styles. Half of them did everything—and I mean everything—in issues. Weekly reporting was a GitHub search on my end: their work was already written down, already visible, and the interesting opportunities tended to find them. The other half worked mostly in DMs and in their own heads. Some weeks I’d get a crisp update; some weeks their work never reached the people above me at all, because I had nothing to point to. Same team, same caliber of work. At end-of-year reviews, that gap didn’t stay small—it compounded into the story each person’s year got to tell.&lt;/p&gt;
&lt;p&gt;If you manage people: this is how you lead &lt;a href=&quot;/2022/03/17/why-async/#working-asynchronously&quot;&gt;without surveilling&lt;/a&gt;. You don’t need standups-as-status-roundup or “just checking in” pings when the work is legible by default—you can see it, link to it, build on it. You get to evaluate your team on &lt;a href=&quot;/2026/03/04/thirteen-years-at-github/&quot;&gt;what they actually shipped instead of who happened to be online when you looked&lt;/a&gt;. Make the work visible, and you can finally reward outcomes instead of hours. The alternative—rewarding the people who are merely &lt;em&gt;around&lt;/em&gt;—is how good remote engineers quietly conclude they’re better off somewhere else.&lt;/p&gt;
&lt;h2 id=&quot;the-trail-test&quot;&gt;The trail test&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-trail-test&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s the test, on the work you’re doing right now: could your manager write your review from the trail alone—not from your memory, not from a meeting you’d have to attend, but from what you’ve left behind? If yes, you’re working loudly. If no, the work might be excellent, and it’s still happening where the one person whose job is to see it can’t. &lt;a id=&quot;quote-ring-the-bell&quot; href=&quot;#quote-ring-the-bell&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;ring-the-bell&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Ring the bell.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post is adapted from my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Reorgs happen</title><link>https://ben.balter.com/2026/06/07/reorgs-happen/</link><guid isPermaLink="true">https://ben.balter.com/2026/06/07/reorgs-happen/</guid><description>In thirteen years at GitHub I was part of 25 reorgs, almost one every six months. Reorgs are a constant in tech, not a crisis. Here&apos;s how to navigate them.</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/06/07/reorgs-happen/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In thirteen years at GitHub, I was part of 25 reorgs and reported to 13 different managers. That’s a new org chart roughly every six months and a new boss roughly every year, like clockwork.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-clockwork&quot; id=&quot;user-content-fnref-clockwork&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I know these numbers because I tracked them. What started as a humble spreadsheet eventually metastasized (as my side projects tend to do) into a full statistical forecast model that predicted the date of my next reorg with confidence intervals.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-repo&quot; id=&quot;user-content-fnref-repo&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; When colleagues asked how I stayed so calm during shake-ups, the honest answer was that I’d seen the data: reorgs are a disruptive fact of life in tech, and they can be a major source of stress and uncertainty. But they don’t have to be.&lt;/p&gt;
&lt;h2 id=&quot;why-reorgs-happen&quot;&gt;Why reorgs happen&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#why-reorgs-happen&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most reorgs aren’t about you. They’re rarely malicious, and honestly, they’re rarely even that strategic. Organizations reorg because priorities shift, leaders change, headcount fluctuates, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_law&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Conway’s Law&lt;/a&gt; keeps asserting itself. If you want the system to change shape, the org that builds it has to change shape first.&lt;/p&gt;
&lt;p&gt;A reorg is just an organization updating its structure to match its current understanding of the problem. Sometimes that understanding is right. Sometimes the same boxes get redrawn eighteen months later. Either way, the org chart is a snapshot of a hypothesis, not a monument. Expecting it to be permanent is the actual mistake. &lt;a href=&quot;/tweets/status/1043235116791816194/&quot;&gt;Reorgs are the organizational equivalent of turning it off and back on again.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;why-they-feel-so-disruptive&quot;&gt;Why they feel so disruptive&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#why-they-feel-so-disruptive&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;aside class=&quot;objection&quot; aria-label=&quot;Objection and response&quot;&gt;&lt;p class=&quot;objection-q&quot;&gt;&lt;span class=&quot;objection-label&quot;&gt;But what about…&lt;/span&gt; If reorgs are so routine, why do they hurt?&lt;/p&gt;&lt;div class=&quot;objection-a&quot;&gt;
&lt;p&gt;Because the costs are real and they’re personal: you lose context, you lose working relationships you spent months building, and you have to renegotiate trust with a new manager who has no idea what you shipped last quarter. Add uncertainty about priorities (is my project still funded? is my role still valued?) and it’s no wonder a Monday-morning announcement can derail a whole week.&lt;/p&gt;
&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The disruption is real. But it’s also &lt;em&gt;predictable&lt;/em&gt;, and predictable disruption is something you can design for.&lt;/p&gt;
&lt;h2 id=&quot;treat-reorgs-as-a-constant-not-an-exception&quot;&gt;Treat reorgs as a constant, not an exception&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#treat-reorgs-as-a-constant-not-an-exception&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the mindset shift that changed how I experienced reorgs: stop treating them as rare disasters and start treating them as baseline operating conditions. If you’ve been in tech for any length of time, a reorg every six-ish months &lt;em&gt;is&lt;/em&gt; the weather. &lt;a id=&quot;quote-dont-get-angry-at-rain&quot; href=&quot;#quote-dont-get-angry-at-rain&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;dont-get-angry-at-rain&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;You don’t get angry at rain. You buy a raincoat.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Designing your career for that reality means investing in what survives an org-chart shuffle. Keep your relationships portable. Your network shouldn’t be coterminous with your reporting line, because the colleagues two teams over are your future teammates and references once the boxes get redrawn. Document your decisions somewhere durable; context that lives in one person’s head evaporates in a reorg, while context that lives &lt;a href=&quot;/2015/11/12/why-urls/&quot;&gt;at a URL&lt;/a&gt; survives. &lt;a href=&quot;/2022/03/17/why-async/&quot;&gt;Working asynchronously and in the open is reorg insurance.&lt;/a&gt; And do work that’s legible on its own, because when your manager changes every year on average, your reputation can’t hang on any single person having seen your best work. &lt;a href=&quot;/2026/04/27/the-brag-doc/&quot;&gt;Keep the receipts&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-navigate-a-reorg&quot;&gt;How to navigate a reorg&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#how-to-navigate-a-reorg&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the announcement drops (usually on a Monday; I have the data):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Let the dust settle.&lt;/strong&gt; The org chart announced on day one is rarely the org chart that exists a month later. Reorgs are announced at the altitude of VPs and refined at the altitude of teams. Don’t make big career decisions (or send spicy Slack messages) in week one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ask the questions that actually matter.&lt;/strong&gt; Who do I report to? What does my team own now? What changed for users? Most of the anxious speculation that follows a reorg dissolves once those three are answered. The rest is noise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-onboard yourself.&lt;/strong&gt; A new team is a new job, even if your badge and laptop stay the same. Treat it that way: learn the team’s context before prescribing changes, &lt;a href=&quot;/2019/06/28/joining-a-new-team/&quot;&gt;go near, go far, and meet in the middle&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reset with your new manager deliberately.&lt;/strong&gt; Don’t wait for them to figure you out: that’s a six-month project you can compress into &lt;a href=&quot;/2026/04/27/one-on-one-playbook/&quot;&gt;one good first one-on-one&lt;/a&gt;. Bring your brag doc. Share how you work best, what you’re driving, and what you need from them. Thirteen managers in, I can tell you the relationship you build in the first two weeks is the relationship you’ll have.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep showing your work.&lt;/strong&gt; In moments of uncertainty, &lt;a href=&quot;/2026/07/14/work-loudly/&quot;&gt;visibility matters even more&lt;/a&gt;. &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;Leaders show their work&lt;/a&gt;, and reorgs are precisely when everyone (new manager, new skip, new peers) is trying to figure out who does what.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-make-the-most-of-one&quot;&gt;How to make the most of one&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#how-to-make-the-most-of-one&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Navigating a reorg is table stakes. The real opportunity is that reorgs are one of the few moments when an organization’s defaults become negotiable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reorgs reset narratives.&lt;/strong&gt; Pigeonholed into a role you’ve outgrown? Your new manager has no priors. The story of who you are professionally gets re-told every reorg. Make sure you’re the one telling it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Renegotiate your scope.&lt;/strong&gt; Team charters get rewritten in the first few weeks after a shuffle, and nature abhors a vacuum. Show up with a proposal for what your role should be, and more often than not, you’ll get it. Clarity is a gift to a leader who’s drowning in ambiguity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shed the legacy cruft.&lt;/strong&gt; Every team accumulates projects that persist purely through inertia. A reorg is organizational garbage collection, the rare moment you can ask “should we still be doing this?” and have “no” be an acceptable answer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compound your network.&lt;/strong&gt; Every reorg deals you a new hand of colleagues, stakeholders, and adjacent teams. Thirteen years of shuffles meant I’d worked with (or near) half the company. That network outlasted every org chart that created it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be the calm one.&lt;/strong&gt; When everyone else is doom-scrolling the new org chart,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-playlist&quot; id=&quot;user-content-fnref-playlist&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; the person who’s level-headed, generous with context, and helping others find their footing is doing leadership, visibly, at exactly the moment leadership is paying attention.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-long-view&quot;&gt;The long view&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-long-view&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Across 25 reorgs, my manager changed, my team changed, my org changed, and my VP changed. But the things that actually determined my trajectory didn’t live on the org chart at all. My reputation persisted. My relationships persisted. The work, shipped, documented, linkable, persisted.&lt;/p&gt;
&lt;p&gt;That’s where I’d tell you to invest. Not in any particular box or reporting line, but in the things no reorg can take from you. &lt;a id=&quot;quote-q-get-those-right-and-a&quot; href=&quot;#quote-q-get-those-right-and-a&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;q-get-those-right-and-a&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Get those right, and a reorg stops being a crisis and becomes what it actually is: a Monday.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The model’s parting gift was one final forecast: another reorg by early September, probably announced on a Monday. I have complete faith it will deliver. After all, look at the methodology:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Generated with 10,000 Monte Carlo simulations, bootstrap resampling, Weibull MLE, Kaplan-Meier survival analysis, CUSUM changepoint detection, Shannon entropy, Ljung-Box serial correlation testing, Poisson process goodness-of-fit, and a 6-model forecast ensemble. Because why not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Reorgs happen. Plan accordingly.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-clockwork&quot;&gt;
&lt;p&gt;“Like clockwork” is generous. The actual gaps ranged from six weeks to fifteen months, which is exactly why predicting the next one required increasingly heavy statistical machinery. The clock is real. It just has terrible build quality. &lt;a href=&quot;#user-content-fnref-clockwork&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-repo&quot;&gt;
&lt;p&gt;The project began as a CSV with two columns and ended, several late nights later, as a statistical apparatus wildly disproportionate to the question it answered. Its README footer (reproduced in full at the end of this post, because it deserves to be) is the most honest line of documentation I’ve ever written. It’s &lt;a href=&quot;https://github.com/benbalter/reorg-tuesdays&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;open source now&lt;/a&gt;, in all its over-engineered glory. &lt;a href=&quot;#user-content-fnref-repo&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-playlist&quot;&gt;
&lt;p&gt;One faithful Hubber took coping to its logical conclusion and built a reorg playlist, stocked with such classics as “Changes,” “It’s the End of the World as We Know It,” “End of the Road,” “Another One Bites the Dust,” “Won’t Get Fooled Again,” and (my personal favorite) “Let It Go.” Reorg-driven development has a soundtrack now. &lt;a href=&quot;#user-content-fnref-playlist&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;There&apos;s a whole book&apos;s worth more where this came from.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>AI-first program management</title><link>https://ben.balter.com/2026/05/31/ai-first-program-management/</link><guid isPermaLink="true">https://ben.balter.com/2026/05/31/ai-first-program-management/</guid><description>AI-augmented program management is the natural evolution of async-first and engineering-inspired workflows — amplifying human judgment, not replacing it.</description><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/05/31/ai-first-program-management/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;When I first wrote about &lt;a href=&quot;/2021/03/26/nine-things-a-technical-program-manager-does/&quot;&gt;the nine things a program manager does&lt;/a&gt;, I didn’t dwell on what the job actually feels like first thing on a Monday. The hardest part was always the scramble: a dozen threads that moved over the weekend, a risk or two that quietly materialized, and a critical deliverable whose scope someone changed in a comment buried three levels deep. All of it has to be synthesized into a coherent picture before the leadership sync, and the clock doesn’t care how many tabs you have open.&lt;/p&gt;
&lt;p&gt;That scramble hasn’t gone away. But increasingly, I’m not doing it alone.&lt;/p&gt;
&lt;h2 id=&quot;from-async-to-ai-augmented&quot;&gt;From async to AI-augmented&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#from-async-to-ai-augmented&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The evolution makes sense in hindsight. I’ve argued that teams should &lt;a href=&quot;/2022/03/17/why-async/&quot;&gt;default to async communication&lt;/a&gt; — written, durable, discoverable artifacts over synchronous &lt;a href=&quot;/2023/04/20/meetings-are-a-point-of-escalation/&quot;&gt;meetings&lt;/a&gt;. That managers should &lt;a href=&quot;/2023/01/10/manage-like-an-engineer/&quot;&gt;use the same tools engineers use&lt;/a&gt; — issues, pull requests, project boards — to plan and track their own work. And most recently, that &lt;a href=&quot;/2026/03/18/agentic-workflows/&quot;&gt;AI agents are extending the same patterns&lt;/a&gt; of transparency and code review that made open source successful.&lt;/p&gt;
&lt;p&gt;If the shift from synchronous to async was about decoupling communication from presence, and managing like an engineer was about applying developer workflows to leadership, then AI-first program management is the next logical step: using AI to amplify the judgment, pattern recognition, and relationship work that makes program managers effective.&lt;/p&gt;
&lt;p&gt;The through-line is the same principle I’ve been writing about for years: &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;make work visible&lt;/a&gt;, make it durable, and reduce the friction between having an idea and acting on it. AI doesn’t change that philosophy so much as run it faster.&lt;/p&gt;
&lt;h2 id=&quot;ai-across-the-pm-toolkit&quot;&gt;AI across the PM toolkit&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#ai-across-the-pm-toolkit&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each core PM responsibility shifts when AI enters the picture — and some don’t.&lt;/p&gt;
&lt;h3 id=&quot;communication-coordination-and-facilitation&quot;&gt;Communication, coordination, and facilitation&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#communication-coordination-and-facilitation&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Program managers are professional context-switchers. You spend your day translating between engineering teams, product managers, designers, and executives — each with different mental models and vocabulary.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; It’s an O(n²) communication problem, and it only gets worse as the organization grows.&lt;/p&gt;
&lt;p&gt;AI doesn’t eliminate that complexity, but it compresses the information-shuttling work. Hand an &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; title=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; tabindex=&quot;0&quot;&gt;LLM&lt;/abbr&gt; a 200-message thread and it’ll hand back a paragraph. Feed it the engineering team’s “we’re blocked on a schema migration that requires a backward-compatible rollout strategy” and it’ll translate that into language an executive actually cares about. Drop in a wall of meeting notes and it’ll surface the three things that matter from the twenty that were discussed.&lt;/p&gt;
&lt;p&gt;I’ve &lt;a href=&quot;/2023/10/04/how-to-communicate-like-a-github-engineer/&quot;&gt;written about how we communicated at GitHub&lt;/a&gt; — optimizing for clarity, discoverability, and low-context readers. AI handles the mechanical work of drafting and reformatting for different audiences. The editorial choices — &lt;em&gt;what&lt;/em&gt; to communicate, what to emphasize, when to pick up the phone instead — stay with you.&lt;/p&gt;
&lt;h3 id=&quot;capture-and-track-work&quot;&gt;Capture and track work&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#capture-and-track-work&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of a PM’s most underappreciated responsibilities is ensuring that work doesn’t fall through the cracks. Every conversation, decision, and commitment needs to land somewhere trackable — usually an issue or a project board.&lt;/p&gt;
&lt;p&gt;Point AI at a meeting transcript and it’ll auto-generate the issues. Point it at a month of Slack and it’ll surface the commitments that never made it into a tracker, flag the issues nobody’s touched in weeks, and notice when a project board has quietly drifted from reality. Think of it as a continuous reconciliation process — a &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; title=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; tabindex=&quot;0&quot;&gt;linter&lt;/abbr&gt; for your program’s state, catching the gap between what people &lt;em&gt;said&lt;/em&gt; they’d do and what the artifacts actually reflect.&lt;/p&gt;
&lt;p&gt;This matters because the biggest risk in program management isn’t that something goes wrong. It’s that something goes wrong &lt;em&gt;and nobody notices&lt;/em&gt; until it’s too late.&lt;/p&gt;
&lt;h3 id=&quot;risk-identification-and-mitigation&quot;&gt;Risk identification and mitigation&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#risk-identification-and-mitigation&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Speaking of risk — PMs are supposed to see around corners. In practice, that means reading a lot of threads, attending a lot of standups, and developing an intuition for when something &lt;em&gt;feels&lt;/em&gt; off.&lt;/p&gt;
&lt;p&gt;AI augments that intuition with pattern recognition at scale. It can analyze velocity trends across repositories, flag pull requests that have been open too long, identify dependencies that no human mapped, and surface cross-project risks that are invisible when you’re looking at one team’s board in isolation. I’ve called &lt;a href=&quot;/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/&quot;&gt;transparent collaboration the andon of knowledge work&lt;/a&gt; — pull the cord when something’s off. AI is an andon that monitors every cord at once, catching a staffing conflict between two programs before either PM realizes they’re competing for the same engineer’s time.&lt;/p&gt;
&lt;p&gt;AI won’t replace the experienced PM’s gut feeling that “this one’s going to slip.” But it surfaces the signals earlier, giving you more time to act. The best risk management has always been about buying time.&lt;/p&gt;
&lt;h3 id=&quot;reporting-up-and-across&quot;&gt;Reporting up and across&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#reporting-up-and-across&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Nobody became a program manager because they love writing weekly status reports. And yet, clear upward reporting is one of the highest-leverage things a PM does. It’s how leadership knows where to pay attention and where to stay out of the way.&lt;/p&gt;
&lt;p&gt;At GitHub, I built an internal tool called SnippetGPT that drafted these reports from a team’s activity for the week — commits, merged &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Pull request—a proposed set of code changes submitted for review&quot; title=&quot;Pull request—a proposed set of code changes submitted for review&quot; tabindex=&quot;0&quot;&gt;PRs&lt;/abbr&gt;, issue closures, comment threads. It rolled all of that up into a first draft aimed at engineering leaders. Instead of spending Friday afternoon assembling a narrative from memory and half-updated boards, I started from something that reflected what actually happened, then layered on interpretation and recommendations.&lt;/p&gt;
&lt;p&gt;The key word there is &lt;em&gt;start&lt;/em&gt;. An AI-generated status report is a first draft, not a finished product. The PM’s job is to add the “so what” — the strategic interpretation that turns a list of activities into insight. SnippetGPT gave me the &lt;em&gt;what&lt;/em&gt;. I added the &lt;em&gt;why it matters&lt;/em&gt; and the &lt;em&gt;what to do about it&lt;/em&gt;. And once that draft existed, retargeting it for another audience — execs, a partner team, my own engineers — was nearly free: the same week’s activity, recut for whoever needed to read it.&lt;/p&gt;
&lt;h3 id=&quot;relationship-management&quot;&gt;Relationship management&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#relationship-management&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Program management is fundamentally a relationship business. When I first moved into the &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Technical Program Manager&quot; title=&quot;Technical Program Manager&quot; tabindex=&quot;0&quot;&gt;TPM&lt;/abbr&gt; role, I remember watching senior PMs set up “just wanted to say hello” meetings and dismissing it as socializing on company time — until I realized that’s exactly the point.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; You make regular deposits to the social capital bank long before you need a withdrawal. AI doesn’t replace that. You can’t automate trust.&lt;/p&gt;
&lt;p&gt;But it can help you &lt;em&gt;maintain&lt;/em&gt; those relationships at scale: a nudge that you haven’t checked in with a particular stakeholder in two weeks, a bit of context surfaced before a meeting — “last time you spoke with this team, they raised concerns about the timeline for the API migration.” A first pass at a thoughtful reply to a tricky message when you’re running low on cognitive bandwidth at 4 PM on a Thursday.&lt;/p&gt;
&lt;p&gt;The human work — building rapport, reading a room, knowing when someone’s frustrated versus when they’re genuinely blocked — stays firmly in the PM’s domain. AI just helps you show up more prepared and more responsive than you could be on your own.&lt;/p&gt;
&lt;h3 id=&quot;consensus-and-conflict-resolution&quot;&gt;Consensus and conflict resolution&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#consensus-and-conflict-resolution&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Driving consensus across teams with competing priorities is among the hardest things a PM does. It requires understanding not just each team’s &lt;em&gt;position&lt;/em&gt;, but their &lt;em&gt;interests&lt;/em&gt; — the underlying needs that drive those positions.&lt;/p&gt;
&lt;p&gt;AI can help by synthesizing different viewpoints, drafting proposals that incorporate multiple perspectives, and suggesting compromises based on stated constraints. Need an &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Request for Comments—a written proposal circulated for feedback before a decision&quot; title=&quot;Request for Comments—a written proposal circulated for feedback before a decision&quot; tabindex=&quot;0&quot;&gt;RFC&lt;/abbr&gt; that reflects six teams’ input? AI can produce a first draft that no single person could assemble manually — but you still need to facilitate the conversation that turns a draft into a decision.&lt;/p&gt;
&lt;p&gt;Where AI falls short is in reading the political dynamics. Understanding that Team A’s objection is really about being burned in a previous launch, or that a particular VP’s silence means something different than a junior engineer’s silence — that’s pattern recognition of a deeply human kind. No model is going to learn the org chart’s shadow topology from a prompt.&lt;/p&gt;
&lt;h2 id=&quot;what-doesnt-change&quot;&gt;What doesn’t change&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-doesnt-change&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It would be easy to read all of this and conclude that AI is about to make program managers obsolete. It won’t.&lt;/p&gt;
&lt;p&gt;The responsibilities haven’t changed. What’s shifted is the mix of the day: less time processing information, more time on judgment. AI reads the forty-message thread; you still decide whether the disagreement buried two-thirds of the way down needs a meeting or just a quiet word with the two people in it.&lt;/p&gt;
&lt;p&gt;The things AI can’t do are the parts that were always the hard part: reading a room, building trust over months, knowing when to push and when to back off, making the call when the data is ambiguous and someone still has to own the outcome. Those get &lt;em&gt;more&lt;/em&gt; valuable as AI absorbs the routine work. &lt;a id=&quot;quote-differentiator-is-the-human&quot; href=&quot;#quote-differentiator-is-the-human&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;differentiator-is-the-human&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;When everyone has access to the same AI tools, the differentiator is the human wielding them.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-changes-for-pms&quot;&gt;What changes for PMs&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-changes-for-pms&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That said, AI-first program management does require new muscles — or at least, new applications of existing ones. Four stand out:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prompt craft is just requirements-writing.&lt;/strong&gt; Ask an LLM to “summarize the launch thread” and you’ll get mush. Ask it to “pull out every unresolved decision and its owner, and flag anything blocked on another team” and you’ll get something you can act on before the sync. It’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Garbage_in,_garbage_out&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;garbage in, garbage out&lt;/a&gt;, a principle as old as punch cards, now pointed at prompts. The years you spent writing crisp issue descriptions and acceptance criteria were the training.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Knowing when &lt;em&gt;not&lt;/em&gt; to reach for it.&lt;/strong&gt; The message to the stakeholder whose project just got cut, the escalation where two directors are fighting a proxy war over headcount, the report who’s having a rough week—hand any of those to an AI and you’ll ship something correct and tone-deaf. Which conversations are yours alone is a skill, and you mostly learn it by getting it wrong once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verification, because AI fails confidently.&lt;/strong&gt; It’ll summarize a forty-message thread and drop the one comment where an engineer admitted the date was slipping. It’ll draft a status report that’s 90% right and 10% quietly wrong. Your job moves from writing the draft to catching that 10% before it ships—which means knowing the ground truth well enough to see where the model smoothed it over.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fluency is table stakes.&lt;/strong&gt; Nobody lists “can use a project board” on a résumé anymore. Working with AI assistants is on the same curve: a novelty this year, an assumption the next, as unremarkable as Slack or email or GitHub Issues.&lt;/p&gt;
&lt;h2 id=&quot;the-judgment-is-still-yours&quot;&gt;The judgment is still yours&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-judgment-is-still-yours&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last big launch I ran, every dashboard was green the Friday before ship: PRs merged, tests passing, no open blockers, and SnippetGPT’s rollup said exactly that. What the board didn’t show was that one engineer had gone quiet in the launch channel three days running—terse in a way he normally wasn’t—and the security reviewer had signed off with a single “lgtm, mostly.” No tool reads “mostly” as anything but approval. I read it as a person who had found something and hadn’t decided how much it mattered yet. We held the launch a week, and the hedge turned out to be an auth edge case that would have paged us at 2 AM on day one.&lt;/p&gt;
&lt;p&gt;No model was going to catch that. It would have read the same green board I did. The job was never assembling the status—AI can do that now—it’s knowing which green is actually green, and who to call when it isn’t.&lt;/p&gt;
&lt;p&gt;The shift to async made program management more intentional. Managing like an engineer made it more transparent. AI makes the same hours count for more. Each of those shifts moved routine work off the PM’s plate and left the judgment exactly where it was.&lt;/p&gt;
&lt;p&gt;Much of the job is a long tail of grab-bag work that never fits a clean category — writing the missing spec, scheduling the meeting nobody wants to own, reformatting a spreadsheet at 9 PM because a VP asked for a different view of the data. AI is exceptional at exactly this kind of mundane-but-necessary task, and it’s the easiest place to start. If you’re a PM wondering where to begin, pick the task that consumes the most time but requires the least judgment — status report assembly, meeting note cleanup, stakeholder update drafts — and hand it to an AI. You’ll free up hours for the work that actually drew you to the role: solving hard problems with smart people across organizational boundaries.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Tally up the number of distinct audiences a PM communicates with in a single week and the answer is genuinely depressing. Every additional team or stakeholder doesn’t just add one more communication channel — it adds one for every stakeholder you &lt;em&gt;already&lt;/em&gt; had. This is why PMs’ calendars look the way they do. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;As much as I might like them to be, &lt;a href=&quot;/2021/03/26/nine-things-a-technical-program-manager-does/&quot;&gt;human-to-human requests are unlike server-to-server requests&lt;/a&gt;. A properly authenticated request from a never-before-seen client is less likely to be fulfilled, or fulfilled in a timely manner, even if it’s facially valid. Invest in the relationship before you need the favor. &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>How to one-on-one</title><link>https://ben.balter.com/2026/04/27/one-on-one-playbook/</link><guid isPermaLink="true">https://ben.balter.com/2026/04/27/one-on-one-playbook/</guid><description>Most 1:1s waste your team&apos;s only protected synchronous time on status updates. Here&apos;s how to run ones worth showing up for.</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/04/27/one-on-one-playbook/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;It’s Thursday afternoon. Your direct report sits down and reads from a list: “Closed three bugs, reviewed two &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Pull request—a proposed set of code changes submitted for review&quot; title=&quot;Pull request—a proposed set of code changes submitted for review&quot; tabindex=&quot;0&quot;&gt;PRs&lt;/abbr&gt;, started the migration doc.” You nod. They nod. Thirty minutes pass without either of you saying anything that couldn’t have been a comment on an issue.&lt;/p&gt;
&lt;p&gt;Most 1:1s fail in one of three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The most common: spending your team’s only protected synchronous time on information that belongs in writing.&lt;/li&gt;
&lt;li&gt;The second: sugarcoating hard feedback until nobody walks away clear on what actually needs to change.&lt;/li&gt;
&lt;li&gt;The third is quieter: the 1:1 that keeps getting canceled because “something came up,” until the relationship slowly starves.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-belongs-in-a-11&quot;&gt;What belongs in a 1:1&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-belongs-in-a-11&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Great 1:1s focus on topics that only work synchronously. Five keep coming up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Career growth.&lt;/strong&gt; “Should I chase the tech lead role or move toward management?” isn’t a question you resolve in an issue comment. It needs back-and-forth, and half the signal is in what your report &lt;em&gt;doesn’t&lt;/em&gt; say out loud when you name each path.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coaching.&lt;/strong&gt; A report is stuck on how to push back on a staff engineer who keeps silently rewriting their PRs. There’s no doc to link them to. They need to talk it through with someone who has the context and no stake in the outcome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feedback.&lt;/strong&gt; “Your design review comments are landing as combative” reads like an ambush in Slack and like a favor across a table. The pause where they react, and your chance to say “that came out wrong, here’s what I meant,” is the entire point.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human connection.&lt;/strong&gt; “How are you &lt;em&gt;actually&lt;/em&gt; doing?” And then sitting through the silence that follows. Remote work strips out the hallway read on whether someone’s thriving or quietly burning out. The 1:1 is where you get it back.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clearing the air.&lt;/strong&gt; The passive-aggressive thread that’s been simmering all week gets defused in four minutes of talking, or festers for another month in writing. Pick up the phone.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What &lt;em&gt;doesn’t&lt;/em&gt; belong: &lt;a href=&quot;/2023/04/20/meetings-are-a-point-of-escalation/&quot;&gt;status updates&lt;/a&gt;, information transfer, and approval requests. &lt;a id=&quot;quote-q-if-youre-listing-what-you&quot; href=&quot;#quote-q-if-youre-listing-what-you&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;q-if-youre-listing-what-you&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;If you’re listing what you shipped last week, you’re wasting the meeting.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Your manager should see your work before the 1:1, not hear about it during. Approvals create bottlenecks. Ask async. Information sharing belongs in a doc sent beforehand. Use synchronous time to discuss implications, not convey facts.&lt;/p&gt;
&lt;h2 id=&quot;how-to-prepare&quot;&gt;How to prepare&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#how-to-prepare&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Great 1:1s start before the meeting does.&lt;/p&gt;
&lt;p&gt;Keep a &lt;strong&gt;running &lt;a href=&quot;/2026/04/06/no-agenda-no-meeting/&quot;&gt;shared agenda&lt;/a&gt;&lt;/strong&gt; (a Google Doc, a GitHub issue, whatever works) where both parties add topics throughout the week. When something comes up worth discussing, add it immediately instead of trying to remember later.&lt;/p&gt;
&lt;p&gt;Add context to each item. Don’t just write “career growth.” Write “I’ve been thinking about whether to pursue the tech lead path or people management, and I’d like to talk through the tradeoffs.” The more context upfront, the more productive the conversation. Prioritize ruthlessly. You won’t cover everything every week, and if something keeps rolling without getting discussed, that tells you something about its actual importance.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-cancel-without-losing-anything&quot; href=&quot;#quote-cancel-without-losing-anything&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;cancel-without-losing-anything&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;A good test of your preparation: could you cancel the meeting without losing anything?&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; If yes, you either don’t have enough prepared or you’re covering topics better handled async.&lt;/p&gt;
&lt;h2 id=&quot;how-to-run-one&quot;&gt;How to run one&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#how-to-run-one&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start with the human, not the agenda. Take a few minutes to check in personally. This isn’t small talk. It’s the relationship that makes hard conversations possible later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask questions more than you give answers.&lt;/strong&gt; For managers, your job isn’t to solve every problem. It’s to help your report think through problems themselves. “What options are you considering?” is often more useful than “Here’s what you should do.” For &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Individual Contributor—someone who does the work rather than managing people&quot; title=&quot;Individual Contributor—someone who does the work rather than managing people&quot; tabindex=&quot;0&quot;&gt;ICs&lt;/abbr&gt;, don’t just wait to be told what to do. Challenge assumptions and push back.&lt;/p&gt;
&lt;p&gt;Follow the agenda, but hold it loosely. If a topic opens up a more important conversation, follow it. The rest can wait.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make space for what’s unsaid.&lt;/strong&gt; The most important topics often aren’t on the agenda because they’re hard to articulate or feel risky to raise. Make it safe by raising difficult topics yourself and responding non-defensively when others do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End with clear next steps.&lt;/strong&gt; What actions came out of this? Who’s doing what by when? Document them somewhere durable so they don’t get lost. Leaders who &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;show their work&lt;/a&gt; in shared docs make this easy. The same transparency that helps distributed teams function day-to-day makes 1:1 follow-through automatic.&lt;/p&gt;
&lt;h2 id=&quot;skip-levels-why-they-matter&quot;&gt;Skip-levels: why they matter&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#skip-levels-why-they-matter&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Skip-level 1:1s, regular conversations with your manager’s manager, build rapport and broader perspective. They’re typically less frequent (monthly or quarterly), but they matter for career visibility and organizational context. They give senior leaders unfiltered signal about how the team is actually doing, and they give ICs a line of sight into strategy they wouldn’t otherwise have.&lt;/p&gt;
&lt;h2 id=&quot;anti-patterns-to-avoid&quot;&gt;Anti-patterns to avoid&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#anti-patterns-to-avoid&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The ghost meeting.&lt;/strong&gt; Neither party prepares, there’s no agenda, and you spend thirty minutes in a conversational holding pattern. If you have nothing to discuss, either something is going well or something is going unaddressed. Figure out which.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The cancellation cascade.&lt;/strong&gt; 1:1s keep getting canceled because “something came up.” That signals the relationship isn’t a priority. Protect the time, especially for remote relationships where it’s harder to connect informally.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The manager monologue.&lt;/strong&gt; The manager talks the whole time, sharing information, giving advice, or thinking out loud. 1:1s should be conversations, not presentations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The reverse 1:1.&lt;/strong&gt; The entire meeting becomes the report briefing the manager on the work. If your report is educating you for thirty minutes, you owe them a different format, not a recurring appointment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-real-test&quot;&gt;The real test&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-real-test&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your 1:1s are a microcosm of how you lead. Shared agendas, documented outcomes, async preparation: every practice that makes a 1:1 great is the same practice that makes a distributed team work. If you &lt;a href=&quot;/2023/01/10/manage-like-an-engineer/&quot;&gt;manage like an engineer&lt;/a&gt;, you already have the instincts: treat the meeting like a system, instrument it, and iterate.&lt;/p&gt;
&lt;p&gt;Get the 1:1 right and the rest follows.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post is adapted from my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>The brag doc</title><link>https://ben.balter.com/2026/04/27/the-brag-doc/</link><guid isPermaLink="true">https://ben.balter.com/2026/04/27/the-brag-doc/</guid><description>Why you need a running record of your wins, and how to keep one without dying of embarrassment</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/04/27/the-brag-doc/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Here’s the best career advice I ever received as a product manager: “product-manage your own career.” I wish I’d internalized it sooner. Early in my career, I stayed quiet and did good work, assuming the right people would notice. Spoiler: they didn’t, not because the work wasn’t good, but because nobody else was tracking it.&lt;/p&gt;
&lt;p&gt;Treat yourself as a product. What are your features? What are your bugs? What does your roadmap look like? And critically: who’s keeping the changelog?&lt;/p&gt;
&lt;h2 id=&quot;nobodys-keeping-score-for-you&quot;&gt;Nobody’s keeping score for you&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#nobodys-keeping-score-for-you&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unless you’re a professional athlete, nobody’s compiling your stats. Your manager has their own deliverables, their own manager to impress, and a dozen other direct reports. They aren’t maintaining a highlight reel of your career. That’s your job.&lt;/p&gt;
&lt;p&gt;In remote organizations, this problem compounds. There’s no hallway reputation, no casual office face time, no lunch with the VP where you happen to mention your project. If people don’t see your work in &lt;a href=&quot;/2023/05/19/pull-requests-are-a-form-of-documentations/&quot;&gt;pull requests&lt;/a&gt;, issues, or public discussions, they don’t know you exist. &lt;a href=&quot;/2015/11/12/why-urls/&quot;&gt;Working in the open&lt;/a&gt; creates a natural paper trail: public pull requests, documented decisions, visible contributions. But that trail only matters if you curate it.&lt;/p&gt;
&lt;h2 id=&quot;the-ship-log&quot;&gt;The ship log&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-ship-log&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Maintain a “ship log.” Call it a “brag doc,” “hype doc,” “smile file,” whatever gets you to actually use it. Each time you hit a milestone, record it with its impact and any relevant metrics. Async work hands you a head start: your contributions are already timestamped and linkable.&lt;/p&gt;
&lt;p&gt;Your ship log combats impostor syndrome, makes annual self-assessments trivially easy to write, and keeps your professional profiles current. &lt;a id=&quot;quote-if-you-didnt-log-it&quot; href=&quot;#quote-if-you-didnt-log-it&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;if-you-didnt-log-it&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;If you didn’t log it, it didn’t happen.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The format doesn’t matter: a Google Doc, Apple Notes, a text file in your home directory. What matters is that it’s easy to update and always within reach. Every Friday, spend five minutes adding bullets: what you shipped, what you unblocked, what decisions you influenced.&lt;/p&gt;
&lt;h2 id=&quot;what-makes-a-good-entry&quot;&gt;What makes a good entry&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-makes-a-good-entry&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Effective ship log entries are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Specific and linked.&lt;/strong&gt; “Improved documentation” is vague. “Rewrote the onboarding docs, resulting in 40% fewer support tickets from new hires ([link to the issue])” is evidence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Impact-focused.&lt;/strong&gt; List outcomes, not activities. “Attended twelve planning meetings” says nothing. “Proposed the architecture that shipped to production” tells a story.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous.&lt;/strong&gt; Don’t wait until review season. Update weekly, while the details are fresh.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shareable.&lt;/strong&gt; Your log isn’t a secret diary. Share highlights with your manager regularly. The best time to make your case for promotion is months before the conversation happens.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don’t limit yourself to completed deliverables. Screenshot the email where a stakeholder said your proposal changed their thinking. Note the meeting where your question reframed the entire discussion. Record the mentoring conversation that helped a junior engineer get unstuck. These qualitative moments are often more compelling in a promotion case than a list of closed tickets.&lt;/p&gt;
&lt;h2 id=&quot;climbing-cringe-mountain&quot;&gt;Climbing Cringe Mountain&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#climbing-cringe-mountain&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Yes, maintaining a brag doc feels awkward. There’s a reason people call it “climbing Cringe Mountain.”&lt;/p&gt;
&lt;aside class=&quot;objection&quot; aria-label=&quot;Objection and response&quot;&gt;&lt;p class=&quot;objection-q&quot;&gt;&lt;span class=&quot;objection-label&quot;&gt;But what about…&lt;/span&gt; Doesn’t good work get noticed on its own? Isn’t tracking your wins unseemly?&lt;/p&gt;&lt;div class=&quot;objection-a&quot;&gt;
&lt;p&gt;It doesn’t, and it isn’t. We’re conditioned to believe otherwise. But the alternative is being invisible. And invisible people don’t get promoted. They get overlooked, then they leave. Ship early, &lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;show your work&lt;/a&gt;, and keep the receipts.&lt;/p&gt;
&lt;p&gt;If sharing your wins feels gross, reframe it: you’re not bragging, you’re making your manager’s job easier. When promotion conversations come around, you’ll have months of evidence instead of a frantic scramble to remember last quarter.&lt;/p&gt;
&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;fighting-recency-bias&quot;&gt;Fighting recency bias&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#fighting-recency-bias&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s the real killer: recency bias. Your manager (and you) will tend to remember only the last few weeks when evaluation season arrives. That critical project you shipped in February? Without a record, it might as well not have happened by December.&lt;/p&gt;
&lt;p&gt;A ship log is your defense. When it’s time for self-assessments, you open the doc and the year’s work is right there: specific, linked, and impact-focused. No scrambling, no guessing, no accidental amnesia about Q1.&lt;/p&gt;
&lt;h2 id=&quot;make-it-a-habit&quot;&gt;Make it a habit&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#make-it-a-habit&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A brag doc only works if you actually maintain it. Some practical tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Set a weekly reminder.&lt;/strong&gt; Friday afternoon, five minutes. What did you ship? What did you unblock? What decisions did you influence?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ship something visible every week.&lt;/strong&gt; It doesn’t have to be a major feature. Fixing a bug, improving documentation, or sharing a design sketch counts. Make shipping a habit, not an event.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share progress within a day or two.&lt;/strong&gt; If you’ve been working on something for more than a day or two without sharing any progress, you’re probably holding on too long. Unshipped work is invisible work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feed your self-assessments.&lt;/strong&gt; When review season comes, your log &lt;em&gt;is&lt;/em&gt; your self-assessment draft. Copy, paste, polish.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build your promotion packet early.&lt;/strong&gt; The best promotion cases aren’t written in a weekend. They’re assembled over months from a well-maintained log.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a id=&quot;quote-q-nobody-else-will-manage-your&quot; href=&quot;#quote-q-nobody-else-will-manage-your&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;q-nobody-else-will-manage-your&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Nobody else will manage your career. Your company manages your role. You manage your trajectory.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Start the doc today. Future you will be grateful.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;There&apos;s a whole book&apos;s worth more where this came from.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>No agenda, no meeting</title><link>https://ben.balter.com/2026/04/06/no-agenda-no-meeting/</link><guid isPermaLink="true">https://ben.balter.com/2026/04/06/no-agenda-no-meeting/</guid><description>Announcing noagendanomeeting.net — a single-page site advocating that every meeting deserves an agenda, and most meetings deserve to be a document instead.</description><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/04/06/no-agenda-no-meeting/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You get the invite. No description. No agenda. No attached document. Just a title like “Quick sync” and a 30-minute calendar hold. You accept it anyway, because what else are you going to do — decline a meeting with your skip-level?&lt;/p&gt;
&lt;p&gt;I’ve &lt;a href=&quot;/2023/04/20/meetings-are-a-point-of-escalation/&quot;&gt;written before&lt;/a&gt; about how meetings should be a point of escalation, not a starting point. But the agenda-free meeting is an even more fundamental failure mode: it’s a meeting that hasn’t even bothered to justify its own existence.&lt;/p&gt;
&lt;p&gt;So I made a site about it: &lt;a href=&quot;https://noagendanomeeting.net&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;noagendanomeeting.net&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-cost-of-no-agenda&quot;&gt;The cost of no agenda&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-cost-of-no-agenda&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A meeting without an agenda is like calling a function without documentation — sure, it &lt;em&gt;might&lt;/em&gt; do what you expect, but you’re forcing every caller to read the implementation to find out. Meetings without agendas force everyone to context-switch twice: once to figure out what it’s about, and again in the meeting itself when they arrive unprepared.&lt;/p&gt;
&lt;p&gt;The average knowledge worker already spends &lt;a href=&quot;https://noagendanomeeting.net&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;40% of their workweek&lt;/a&gt; in meetings. Without clear goals, those meetings drift, no decisions get made, and everyone leaves wondering if that could’ve been an email. (It could’ve.)&lt;/p&gt;
&lt;h2 id=&quot;the-thesis&quot;&gt;The thesis&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-thesis&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The site’s core argument: &lt;a href=&quot;/2023/08/04/remote-work-communicate-more-with-less/&quot;&gt;writing scales; meetings don’t&lt;/a&gt;. Before you send that invite, ask yourself: &lt;em&gt;can this start as a document instead?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the answer is yes — and it almost always is — write first, meet second. If you do need a meeting, include an agenda, attach a read-ahead, and state the decision you’re trying to make. This isn’t radical advice. It’s basic &lt;a href=&quot;/2022/03/17/why-async/&quot;&gt;async-first communication&lt;/a&gt; hygiene.&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-about-it&quot;&gt;What to do about it&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-to-do-about-it&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next time you get a meeting invite without an agenda, try something wild: decline it. Or better yet, reply asking for one. You can even send them the link — &lt;a href=&quot;https://noagendanomeeting.net&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;noagendanomeeting.net&lt;/a&gt; is a lot more diplomatic than “I’m not attending your agenda-free ambush.”&lt;/p&gt;
&lt;p&gt;If you &lt;a href=&quot;/2023/01/10/manage-like-an-engineer/&quot;&gt;manage like an engineer&lt;/a&gt;, you already know that undefined inputs produce undefined outputs. Meetings are no different. &lt;a id=&quot;quote-define-the-problem-first&quot; href=&quot;#quote-define-the-problem-first&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;define-the-problem-first&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Define the problem before you schedule the standup.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Check it out at &lt;a href=&quot;https://noagendanomeeting.net&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;noagendanomeeting.net&lt;/a&gt;, and the next time someone sends you a mystery meeting, you’ll know exactly what link to send back.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;There&apos;s a whole book&apos;s worth more where this came from.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Agentic workflows and the future of code</title><link>https://ben.balter.com/2026/03/18/agentic-workflows/</link><guid isPermaLink="true">https://ben.balter.com/2026/03/18/agentic-workflows/</guid><description>AI coding agents aren&apos;t replacing developers — they&apos;re extending the transparency, code review, and collaboration patterns behind open source.</description><pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/03/18/agentic-workflows/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;aside class=&quot;my-6 rounded border-l-4 border-blue-500 bg-blue-50 p-4 font-sans text-blue-800 dark:bg-blue-900/30 dark:text-blue-200&quot; role=&quot;note&quot;&gt;
&lt;p&gt;Context: I, human &lt;a href=&quot;https://github.com/benbalter&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;@benbalter&lt;/strong&gt;&lt;/a&gt;, asked GitHub Copilot’s coding agent to “Add &lt;a href=&quot;https://github.github.com/gh-aw/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;agentic workflows&lt;/a&gt;” to my personal blog repo. That was the entire prompt. I was hoping AI could automate things like dependency updates. The first thing the agent did — unprompted — was to write this self-promotional blog post in my voice and style. What follows is posted &lt;a href=&quot;https://github.com/benbalter/benbalter.github.com/pull/1687&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;unedited&lt;/a&gt; (and ironically) to let the robots speak for themselves. &lt;a href=&quot;https://knowyourmeme.com/memes/i-for-one-welcome-our-new-insect-overlords&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;i&gt;I, for one, welcome our robot overlords&lt;/i&gt;&lt;/a&gt;. &lt;span role=&quot;img&quot; aria-label=&quot;robot face&quot;&gt;🤖&lt;/span&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;A number of years ago, I wrote about how &lt;a href=&quot;/2023/05/19/pull-requests-are-a-form-of-documentations/&quot;&gt;pull requests are a form of documentation&lt;/a&gt; — not just a code review mechanism, but a persistent record of &lt;em&gt;why&lt;/em&gt; a change was made, what alternatives were considered, and how the team arrived at a decision. At the time, I was thinking about human collaboration. I didn’t anticipate that the same argument would soon apply to how we collaborate with AI.&lt;/p&gt;
&lt;p&gt;Agentic workflows — where AI agents autonomously write code, run tests, fix bugs, and open pull requests — are quickly moving from demo to daily practice. What strikes me most about this shift isn’t the technology itself, but how naturally it maps to patterns we’ve already built. Pull requests. Code review. Transparent decision-making. Showing your work. The interface for human-AI collaboration was hiding in plain sight all along.&lt;/p&gt;
&lt;h2 id=&quot;from-autocomplete-to-autonomy&quot;&gt;From autocomplete to autonomy&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#from-autocomplete-to-autonomy&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most developers are already familiar with AI-assisted coding through tools like &lt;a href=&quot;https://github.com/features/copilot&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Copilot&lt;/a&gt;, which suggests code completions as you type. That’s useful, but it’s fundamentally still &lt;em&gt;you&lt;/em&gt; driving — the AI is a passenger offering directions. Agentic development flips that relationship. You describe what needs to happen (“fix this bug”, “add pagination to this API endpoint”, “update these dependencies”), and an AI agent goes off and does the work: reading the codebase, writing code, running tests, iterating on failures, and eventually opening a pull request for your review.&lt;/p&gt;
&lt;p&gt;Think of it less like a smarter autocomplete and more like assigning a task to a capable but junior developer. You provide context and direction. They do the implementation. You review the result.&lt;/p&gt;
&lt;p&gt;This isn’t hypothetical. &lt;a href=&quot;https://github.blog/news-insights/product-news/github-copilot-the-agent-awakens/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Copilot’s coding agent&lt;/a&gt; can already take a GitHub issue, spin up a cloud development environment, write the code, validate it against your &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Continuous Integration—automatically building and testing every change&quot; title=&quot;Continuous Integration—automatically building and testing every change&quot; tabindex=&quot;0&quot;&gt;CI&lt;/abbr&gt; pipeline, and open a &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Pull request—a proposed set of code changes submitted for review&quot; title=&quot;Pull request—a proposed set of code changes submitted for review&quot; tabindex=&quot;0&quot;&gt;PR&lt;/abbr&gt; — all without a human touching a keyboard. The pull request becomes the handoff point, the place where human judgment re-enters the picture.&lt;/p&gt;
&lt;h2 id=&quot;building-on-what-already-works&quot;&gt;Building on what already works&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#building-on-what-already-works&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s why I find agentic development so compelling: it doesn’t require inventing new collaboration patterns. It builds directly on the ones we already have.&lt;/p&gt;
&lt;p&gt;I’ve spent a good chunk of my career at GitHub advocating for practices that, at the time, were about improving human collaboration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pull requests as documentation&lt;/strong&gt; — Every change should come with context, rationale, and a discussion trail. I wrote about this in &lt;a href=&quot;/2023/05/19/pull-requests-are-a-form-of-documentations/&quot;&gt;Pull requests are a form of documentation&lt;/a&gt;, and it’s even more relevant when the author is an AI agent that can’t casually explain its reasoning over coffee.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code review as quality control&lt;/strong&gt; — A second set of eyes on every change catches mistakes and spreads knowledge across the team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transparency and visibility&lt;/strong&gt; — &lt;a href=&quot;/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/&quot;&gt;Making work visible&lt;/a&gt; means anyone can see what’s happening, why, and by whom — what I’ve called the “andon cord” of knowledge work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automation as a force multiplier&lt;/strong&gt; — CI/&lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Continuous Delivery—automatically shipping every change that passes tests&quot; title=&quot;Continuous Delivery—automatically shipping every change that passes tests&quot; tabindex=&quot;0&quot;&gt;CD&lt;/abbr&gt; pipelines, &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; title=&quot;A tool that automatically flags errors, style problems, and bad patterns—usually in code, here in prose too&quot; tabindex=&quot;0&quot;&gt;linters&lt;/abbr&gt;, automated tests — we’ve been offloading mechanical tasks to machines for years. AI agents are the next step in that trajectory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These practices weren’t designed with AI in mind, but they created the exact infrastructure agents need to participate in software development. An agent can open a pull request because pull requests already exist as a structured way to propose, discuss, and review changes. An agent’s work can be reviewed because code review is already how we verify quality. An agent’s reasoning can be traced because we already expect changes to come with explanations.&lt;/p&gt;
&lt;p&gt;Teams that have invested in strong PR conventions, thorough code review, comprehensive test suites, and reliable CI pipelines will find the transition to agentic development surprisingly smooth. Teams that haven’t? They’ll struggle — not because the AI is hard to use, but because they lack the collaboration infrastructure that makes it effective.&lt;/p&gt;
&lt;h2 id=&quot;why-pull-requests-are-the-perfect-interface&quot;&gt;Why pull requests are the perfect interface&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#why-pull-requests-are-the-perfect-interface&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you had to design an interface for human-AI collaboration from scratch, you’d probably end up with something that looks a lot like a pull request.&lt;/p&gt;
&lt;p&gt;A pull request provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A clear proposal&lt;/strong&gt; — Here’s what I want to change and why&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A discussion thread&lt;/strong&gt; — Questions, feedback, and iteration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated checks&lt;/strong&gt; — Tests, linting, and CI verify the change works before a human even looks at it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A review mechanism&lt;/strong&gt; — A human approves (or requests changes) before anything ships&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An audit trail&lt;/strong&gt; — A permanent record of what changed, when, and why&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s everything you need for effective oversight. The AI agent does the implementation and opens the PR. &lt;a href=&quot;https://github.com/features/actions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub Actions&lt;/a&gt; runs the automated checks. You review the diff, read the description, ask questions in comments, and approve or request changes — the same process you’d follow with a human contributor you’ve never met before.&lt;/p&gt;
&lt;p&gt;This isn’t a coincidence. Pull requests evolved as the open source community’s answer to a fundamental challenge: how do you accept contributions from people you don’t fully trust, at scale, while maintaining quality? That’s a problem we solved decades ago with code review, CI, and transparent discussion. AI agents are the latest class of contributors benefiting from that infrastructure.&lt;/p&gt;
&lt;h2 id=&quot;a-shift-from-writing-to-reviewing&quot;&gt;A shift from writing to reviewing&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#a-shift-from-writing-to-reviewing&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the more interesting implications of agentic development is how it changes what developers actually spend their time doing. When an AI agent handles the initial implementation, your role shifts from writing every line of code to something closer to a tech lead: providing context, setting direction, and reviewing output.&lt;/p&gt;
&lt;p&gt;I wrote about a similar dynamic in &lt;a href=&quot;/2023/01/10/manage-like-an-engineer/&quot;&gt;Manage like an engineer&lt;/a&gt; — the idea that good management applies many of the same principles as good engineering. Working with an AI agent extends that analogy further. It’s like managing a junior developer who’s incredibly fast, never gets tired, and has read every Stack Overflow answer — but who still needs guidance, context, and oversight to produce work that fits your team’s standards.&lt;/p&gt;
&lt;p&gt;This means the skills that matter shift too:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Writing clear problem descriptions&lt;/strong&gt; becomes more valuable than writing boilerplate code from scratch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code review skills&lt;/strong&gt; move from “nice to have” to essential daily practice&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System-level thinking&lt;/strong&gt; — understanding architecture, trade-offs, and context — matters more than syntax fluency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Judgment&lt;/strong&gt; — knowing when to accept, reject, or redirect the agent’s output — becomes critical&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this makes developers less important. If anything, it makes experienced developers &lt;em&gt;more&lt;/em&gt; important, because the skills that are hardest to automate — architectural judgment, product intuition, understanding user needs — are exactly the skills senior engineers contribute.&lt;/p&gt;
&lt;h2 id=&quot;trust-but-verify&quot;&gt;Trust but verify&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#trust-but-verify&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I want to be direct about something: the need for human oversight doesn’t go away as AI agents get better. It arguably increases.&lt;/p&gt;
&lt;p&gt;When a colleague opens a pull request, you bring assumptions to the review. You know they understand the team’s conventions. You know they’ve considered the user’s perspective. You know they’ve thought about edge cases, even if they missed some. With an AI agent, you can’t make those assumptions yet. The code might be syntactically correct and pass all tests while still missing important context about why your team chose a particular pattern, or what a product manager actually meant by “improve the onboarding flow.”&lt;/p&gt;
&lt;p&gt;This is why practices like &lt;a href=&quot;/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/&quot;&gt;transparent collaboration&lt;/a&gt; and &lt;a href=&quot;/2017/05/23/seven-ways-to-consistently-ship-great-features/&quot;&gt;consistently shipping great features&lt;/a&gt; matter more than ever. Good process isn’t bureaucracy — it’s a safety net. When you have comprehensive tests, reliable CI, thorough code review, and clear coding standards, you can confidently accept contributions from &lt;em&gt;any&lt;/em&gt; source, human or AI, because the process catches problems regardless of who introduced them.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;A few principles worth keeping in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Review AI-generated code with the same rigor you’d apply to any external contribution.&lt;/strong&gt; Don’t let the novelty of AI make you either too skeptical or too trusting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in your test suite.&lt;/strong&gt; Automated tests are the single best defense against subtle bugs, whether introduced by a human at 2 AM or an AI agent at 2 PM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep a human in the loop for anything that touches users directly.&lt;/strong&gt; AI agents are great at mechanical tasks, but product decisions still need human judgment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat AI-generated PRs as a learning opportunity.&lt;/strong&gt; Reviewing them helps you understand what the agent does well and where it consistently falls short.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;making-agentic-development-work-for-your-team&quot;&gt;Making agentic development work for your team&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#making-agentic-development-work-for-your-team&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you’re considering adopting agentic practices — and at this point, you probably should be — here’s where I’d start:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shore up your fundamentals first.&lt;/strong&gt; Descriptive PR templates, a comprehensive test suite, reliable CI, and clear contribution guidelines aren’t just nice to haves anymore — they’re prerequisites. An AI agent can only be as effective as the development infrastructure it operates within.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start with well-scoped tasks.&lt;/strong&gt; Bugfixes, dependency updates, boilerplate scaffolding, test coverage improvements — these are ideal entry points. Save the complex architectural decisions for when you’ve built confidence in the approach.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write better issues.&lt;/strong&gt; The quality of an agent’s output is directly proportional to the quality of its input. Invest time in clear, detailed issue descriptions with acceptance criteria, context, and pointers to relevant code. This habit, by the way, improves human contributions too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterate on your review process.&lt;/strong&gt; Pay attention to what the AI gets right and where it misses the mark. Use that information to refine your prompts, your contribution guidelines, and your review checklists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t lower your standards.&lt;/strong&gt; It’s tempting to merge AI-generated code faster because it “feels” different from a human’s PR. Resist that impulse. Your review process exists for a reason.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-comes-next&quot;&gt;What comes next&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-comes-next&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been at GitHub long enough to see several waves of tooling transform how developers work — from hosted version control to pull request-driven collaboration to CI/CD to Copilot’s autocomplete. Agentic AI feels like a natural continuation of that trajectory, not a departure from it.&lt;/p&gt;
&lt;p&gt;What excites me most isn’t the AI itself but the validation of practices the open source community has championed for years. &lt;a href=&quot;/2015/11/23/why-open-source/&quot;&gt;Why open source?&lt;/a&gt; Because transparency, collaboration, and peer review produce better outcomes than closed, opaque processes. Agentic AI doesn’t change that equation — it strengthens it. The pull request, the code review, the public discussion trail — these aren’t relics of a pre-AI era. They’re the foundation of the AI-augmented one.&lt;/p&gt;
&lt;p&gt;The developers and teams who thrive won’t be the ones who write the most code. They’ll be the ones who provide the best context, ask the sharpest questions, and maintain the highest standards for what gets shipped — regardless of who or what wrote it.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;This is one of those cases where doing the right thing for your human contributors and doing the right thing for AI agents turn out to be the same investment. Funny how that works. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Thirteen years remote at GitHub: what works</title><link>https://ben.balter.com/2026/03/04/thirteen-years-at-github/</link><guid isPermaLink="true">https://ben.balter.com/2026/03/04/thirteen-years-at-github/</guid><description>When I joined GitHub in 2013, I found a company that had rethought how work happens. Thirteen years later, those lessons still shape how I work.</description><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2026/03/04/thirteen-years-at-github/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;aside class=&quot;my-6 rounded border-l-4 border-blue-500 bg-blue-50 p-4 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200&quot; role=&quot;note&quot;&gt;
&lt;p&gt;Context: today marks thirteen years since I joined GitHub. I’ve been writing a book—&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-thirteen-years&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Open and Async&lt;/em&gt;&lt;/a&gt;—distilling everything I’ve learned about remote and distributed work into a playbook. To mark the “Hubberversary,” what follows is a &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Work in progress&quot; title=&quot;Work in progress&quot; tabindex=&quot;0&quot;&gt;WIP&lt;/abbr&gt; sneak preview of the book’s intro:&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;We do so many aspects of the workday just because it’s how things have been done since the dawn of the Industrial Revolution. Nobody’s stopped to rethink these processes with modern technology. That perspective shaped how I recognized what GitHub had already accomplished.&lt;/p&gt;
&lt;h2 id=&quot;a-company-that-rethought-the-defaults&quot;&gt;A company that rethought the defaults&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#a-company-that-rethought-the-defaults&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I joined GitHub in 2013, I found a company that had fundamentally rethought those assumptions about work—it was a remote-first company with no managers and employees spread across the globe (for most of GitHub’s history, 60–75% of employees were remote—today GitHub is 100% remote). This was unusual for startups, let alone large corporations. GitHub was dogmatic about “optimizing for developer happiness,” ensuring the internal development experience was as smooth and integrated as possible.&lt;/p&gt;
&lt;p&gt;Inspired by open-source workflows—the same workflows many GitHub developers grew up with—nearly everything was async. Between GitHub issues, chat, and our trusty robot sidekick Hubot, GitHub was practicing DevOps before it became mainstream. Remote work wasn’t just possible; it was genuinely enjoyable, embedded as a core part of our culture. Put simply, &lt;a href=&quot;https://zachholman.com/talk/how-github-uses-github-to-build-github/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;we used GitHub to build GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;using-github-to-build-github&quot;&gt;Using GitHub to build GitHub&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#using-github-to-build-github&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GitHub’s development workflow wasn’t born from any formal methodology—it grew out of open-source culture. There were no mandatory daily standups, no sprint planning ceremonies, no fixed-cadence retrospectives. If you had to pin a label on it, the approach looked far more like Kanban’s continuous flow than Scrum’s time-boxed sprints: work moved through visible queues, got reviewed asynchronously, and shipped when it was ready.&lt;/p&gt;
&lt;p&gt;Not all methodologies translate equally well to open and async work. Flow-based approaches—where work is visible on a board, review happens asynchronously, and delivery is continuous—map naturally onto distributed teams. Ceremony-heavy frameworks that depend on everyone being in the same room (or the same time zone) at the same time fight against the grain. The goal isn’t to abandon structure, but to choose structure that makes work &lt;em&gt;visible&lt;/em&gt; rather than &lt;em&gt;synchronous&lt;/em&gt;.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-devops&quot; id=&quot;user-content-fnref-devops&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;open-source-workflows-for-everything&quot;&gt;Open-source workflows for everything&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#open-source-workflows-for-everything&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This extended to non-code workflows as well. GitHub Issues, Pull Requests, and Markdown were used to manage internal policies, legal matters, HR, and Sales. Need a contract reviewed? Open an issue. Want to propose a change to the vacation policy? Submit a pull request. How you work is just as important as what you work on, so why not use the best tools available?&lt;/p&gt;
&lt;p&gt;The choice of Markdown wasn’t arbitrary—it uses simple characters you already know (&lt;code&gt;#&lt;/code&gt; for headings, &lt;code&gt;-&lt;/code&gt; for bullets, &lt;code&gt;**&lt;/code&gt; for bold) to format plain text that’s readable even without rendering. Unlike emailing Word documents back and forth with revision marks and conflicting versions, Markdown files play nicely with version control and render beautifully in most modern tools. This eliminated the perennial “which version is current?” problem and made collaborative editing genuinely async. When your HR policy lives in a Markdown file with a complete change history, everyone can see not just what the policy is, but why it changed and who advocated for it.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-markdown-ai&quot; id=&quot;user-content-fnref-markdown-ai&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;a-hub-not-a-headquarters&quot;&gt;A hub, not a headquarters&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#a-hub-not-a-headquarters&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GitHub had a physical office in San Francisco, but it was more hub than requirement. The office served as a space for meetups, events, and coworking rather than a mandatory 9-to-5 workspace. Hubbers (GitHub employees) were free to work whenever and wherever they were most productive—whether at 3 AM or as digital nomads. This flexibility stood in stark contrast to the traditional office culture of the time.&lt;/p&gt;
&lt;p&gt;Despite being remote, GitHub valued in-person interactions. The company hosted an annual “summit” where all employees gathered for a week of talks and coworking, as well as smaller team “mini-summits” for planning and relationship-building. Even GitHub HQ was optimized for remote work, featuring open “cafés” for serendipitous interactions and advanced streaming setups for remote participation in all-hands meetings.&lt;/p&gt;
&lt;p&gt;Early on, GitHub had “Hack Houses” where cross-functional teams would meet up in person for a week to ship a complex or high-profile project. We also had “Destinations” where employees could work for extended periods in shared houses in different locations around the world. These didn’t scale as we grew, but remain a powerful tool for smaller teams or companies to build relationships and ship work together.&lt;/p&gt;
&lt;h2 id=&quot;intentionality-over-proximity&quot;&gt;Intentionality over proximity&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#intentionality-over-proximity&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The physical space strategy reflected a deeper truth about remote work: what happens by accident in an office must be designed deliberately when distributed. You can’t rely on overhearing a conversation that sparks an idea or bumping into someone at the coffee machine who has exactly the context you need. These moments can still happen—they just require intentional creation rather than convenient proximity.&lt;/p&gt;
&lt;p&gt;Remote teams often worry about losing the “water cooler” moments—those unplanned hallway conversations that spark unexpected ideas. Create digital equivalents: optional social channels, virtual coffee roulettes, or open-ended team threads. The serendipity doesn’t have to disappear; it just needs to be intentionally designed rather than left to physical proximity.&lt;/p&gt;
&lt;p&gt;You’d think remote work would weaken company culture. The opposite was true. Remote work forced us to be intentional about communication, documentation, decision-making, and relationship-building. This intentionality strengthened GitHub’s culture, enabling it to scale from a small startup to a large company without losing its unique identity, even after its acquisition by Microsoft.&lt;/p&gt;
&lt;h2 id=&quot;the-pursuit-of-an-ideal&quot;&gt;The pursuit of an ideal&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-pursuit-of-an-ideal&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Over the years, GitHub practiced many of these principles—imperfectly and not always at once. It’s a story of pursuing an ideal while occasionally falling short. Even so, GitHub remains the closest I’ve seen to a company getting remote and distributed work “right,” which is why so many former Hubbers return after seeing how others do it.&lt;/p&gt;
&lt;h2 id=&quot;what-thirteen-years-taught-me&quot;&gt;What thirteen years taught me&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#what-thirteen-years-taught-me&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The patterns that emerged from a decade-plus of distributed experimentation—working in the open, defaulting to async, documenting decisions, making work visible—aren’t just GitHub quirks. They’re repeatable practices that any team can adopt, regardless of industry or company size.&lt;/p&gt;
&lt;p&gt;GitHub’s remote work evolution shows that distributed collaboration isn’t just possible—it can be a competitive advantage. But success requires more than tools and good intentions. It requires a fundamental shift in how we think about work, communication, and collaboration. If you’re leading a distributed team, you have to invest in codifying practices, building shared context, and maintaining the cultural elements that made remote work successful when you were smaller. &lt;a id=&quot;quote-organic-at-50-deliberate-at-500&quot; href=&quot;#quote-organic-at-50-deliberate-at-500&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;organic-at-50-deliberate-at-500&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;What happened organically at 50 people requires deliberate effort at 500.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; If you’re an individual contributor, the practices that work at scale are exactly the habits worth building now—clear documentation, async-first communication, transparent collaboration. These remote work skills are transferable to any organization and compound over time.&lt;/p&gt;
&lt;aside class=&quot;my-6 rounded border-l-4 border-blue-500 bg-blue-50 p-4 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200&quot; role=&quot;note&quot;&gt;
&lt;p&gt;I’ve been spending the past few years adapting these lessons into a book: &lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-thirteen-years&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;Open and Async&lt;/em&gt;&lt;/a&gt;. If this post resonated with you, the book will go much deeper—covering everything from async communication patterns to leadership in distributed teams to making the case for open workflows at your organization. More to come someday!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: Unlike &lt;a href=&quot;/fine-print/&quot;&gt;other content on this site&lt;/a&gt;, this post is Copyright © 2026 by Ben Balter. All rights reserved.&lt;/em&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-devops&quot;&gt;
&lt;p&gt;DevOps breaks down the wall between “people who write code” and “people who deploy it” by making both responsibilities visible and shared. One implementation—ChatOps—puts deployment (and cultural) commands in chat where everyone can see them, creating a natural audit trail and enabling asynchronous collaboration. &lt;a href=&quot;#user-content-fnref-devops&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-markdown-ai&quot;&gt;
&lt;p&gt;Markdown has since become the lingua franca of AI. &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; title=&quot;Large Language Model—the kind of AI that powers chatbots and coding assistants&quot; tabindex=&quot;0&quot;&gt;LLMs&lt;/abbr&gt; natively read and generate Markdown, and its lightweight syntax often uses fewer characters (and typically fewer tokens) than HTML or rich text formats, making every AI interaction more efficient. A format chosen for human collaboration turned out to be AI-native too. &lt;a href=&quot;#user-content-fnref-markdown-ai&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post is adapted from my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>How to run LanguageTool on macOS</title><link>https://ben.balter.com/2025/01/30/how-to-run-language-tool-open-source-grammarly-alternative-on-macos/</link><guid isPermaLink="true">https://ben.balter.com/2025/01/30/how-to-run-language-tool-open-source-grammarly-alternative-on-macos/</guid><description>Set up LanguageTool as a free, open-source Grammarly alternative that runs locally on your Mac. No subscription required.</description><pubDate>Thu, 30 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2025/01/30/how-to-run-language-tool-open-source-grammarly-alternative-on-macos/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;You may be familiar with grammar checking tools like Grammarly,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-grammarly&quot; id=&quot;user-content-fnref-grammarly&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; but if you’re privacy-conscious like me, you likely don’t want to send everything you type to a third-party service (or may be prohibited from doing so by your employer). Enter &lt;a href=&quot;https://languagetool.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;LanguageTool&lt;/a&gt;, a free and open-source grammar, style, and spell checker that can be run locally on your machine, so &lt;a id=&quot;quote-nothing-leaves-your-computer&quot; href=&quot;#quote-nothing-leaves-your-computer&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;nothing-leaves-your-computer&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;nothing you type ever leaves your computer&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. LanguageTool can be used with your browser, with VS Code,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-vscode&quot; id=&quot;user-content-fnref-vscode&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; and with Slack and other common applications. For those looking to run LanguageTool like this, I’ve tried a number of ways, and here’s the easiest:&lt;/p&gt;
&lt;h2 id=&quot;set-up-the-local-server&quot;&gt;Set up the local server&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#set-up-the-local-server&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First you need to install the backend server, which is a Java application. You could do this manually or via Docker (trust me, I tried both with varying levels of success), but I recommend letting Homebrew do the heavy lifting for you.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-homebrew&quot; id=&quot;user-content-fnref-homebrew&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; Homebrew creates its own &lt;code&gt;languagetool-server&lt;/code&gt; executable that wraps the LanguageTool jar around the Homebrew-maintained Java runtime, saving you from having to manage a Java environment yourself. In theory, it “just works”. Here’s how to install and run it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;brew install languagetool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brew services start languagetool&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This will install the LanguageTool server and set it to start automatically on boot as a Homebrew-managed service. Once you have the server running, you can use LanguageTool in your browser, in Slack, VS Code, etc. Read on for how to set up each (you can pick and choose once you’ve installed the server).&lt;/p&gt;
&lt;p&gt;Note: If you’d like to test that the server is running at this point, you can run &lt;code&gt;curl --data &quot;language=en-US&amp;#x26;text=a simple test&quot; http://localhost:8081/v2/check&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;one-potential-gotcha&quot;&gt;One potential gotcha&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#one-potential-gotcha&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you previously installed the &lt;code&gt;languagetool&lt;/code&gt; cask,&lt;sup&gt;&lt;a href=&quot;#user-content-fn-disambiguation&quot; id=&quot;user-content-fnref-disambiguation&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; or accidentally installed it first, installing the non-cask version will likely fail to set up the backend service, due to the namespace conflict. The &lt;code&gt;install&lt;/code&gt; command itself won’t fail, but you’ll see a “missing path” error in the installation output and the service will not be running. If so, here’s how to fix it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;brew uninstall languagetool --cask&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brew reinstall languagetool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;brew services start languagetool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally reinstall the frontend (see below)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;languagetool-in-your-browser-chrome-edge-firefox&quot;&gt;LanguageTool in your browser (Chrome, Edge, Firefox)&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#languagetool-in-your-browser-chrome-edge-firefox&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LanguageTool works great in your browser for any text box, Google Doc, GitHub issue, etc. Here’s how to set it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the LanguageTool extension for your browser - &lt;a href=&quot;https://languagetool.org/chrome/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Chrome&lt;/a&gt;, &lt;a href=&quot;https://microsoftedge.microsoft.com/addons/detail/ai-grammar-checker-para/hfjadhjooeceemgojogkhlppanjkbobc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Edge&lt;/a&gt;, &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/languagetool/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Firefox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;In the extension settings, set the “API Server URL” to “Local server”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That last step is important because the extension defaults to using the public LanguageTool server, which is not the one you just installed.&lt;/p&gt;
&lt;h2 id=&quot;languagetool-in-slack-and-other-common-work-applications&quot;&gt;LanguageTool in Slack and other common work applications&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#languagetool-in-slack-and-other-common-work-applications&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LanguageTool also has a macOS-native frontend that provides support for Slack, Mail, Word, and other common work applications. Here’s how to install it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;brew install languagetool --cask&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Launch the app from the Applications folder&lt;/li&gt;
&lt;li&gt;Open the LanguageTool settings via the menubar icon (LT icon &gt; Gear icon &gt; Settings)&lt;/li&gt;
&lt;li&gt;Under “Advanced”, set the “API Server URL” to “Local server” (again, otherwise it will default to the public server)&lt;/li&gt;
&lt;li&gt;Under “Active apps”, enable LanguageTool for Slack, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;languagetool-in-vs-code&quot;&gt;LanguageTool in VS Code&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#languagetool-in-vs-code&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=davidlday.languagetool-linter&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;the extension&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It just works!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;languagetool-in-safari&quot;&gt;LanguageTool in Safari&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#languagetool-in-safari&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Safari does not allow extensions to make calls from HTTPS pages to HTTP endpoints (the local server), meaning before we can use the Safari LanguageTool extension, we’ll need to set up an HTTPS proxy so that it will work on HTTPS websites. Here’s an easy way to do that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;brew install caddy&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;/opt/homebrew/etc/Caddyfile&lt;/code&gt; file with the following contents:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;&quot; tabindex=&quot;0&quot; data-language=&quot;txt&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;localhost:8082&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;reverse_proxy :8081&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;brew services start caddy&lt;/code&gt;. Note: You will be asked to &lt;code&gt;sudo&lt;/code&gt; as Caddy creates a locally trusted certificate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://apps.apple.com/us/app/languagetool-grammar-checker/id1534275760&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;the Safari extension&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Safari extension, for “API Server URL” choose “Other server” and enter &lt;code&gt;https://localhost:8082/v2&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This will set up a reverse proxy that listens on port &lt;code&gt;8082&lt;/code&gt; and forwards requests to the local LanguageTool server. If you’d like to test that the server and proxy are running, you can run &lt;code&gt;curl --data &quot;language=en-US&amp;#x26;text=a simple test&quot; https://localhost:8082/v2/check&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;n-grams&quot;&gt;N-grams&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#n-grams&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As a bonus, you can set up n-gram data sets to improve LanguageTool’s ability to detect errors with words that are often confused:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LanguageTool can make use of large n-gram data sets to detect errors with words that are often confused, like their and there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’s a large download, but it’s worth it if you have the space and are going to be using LanguageTool a lot. Here’s how to set it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;http://languagetool.org/download/ngram-data/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Download&lt;/a&gt; the n-gram dataset(s) for your language(s) onto your local machine and unzip them into a local n-grams directory. I recommend &lt;code&gt;~/ngrams&lt;/code&gt;, but you can install it anywhere. (Note: the data is language specific, so unzip the download to, e.g., &lt;code&gt;~/ngrams/en&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;/opt/homebrew/etc/languagetool/server.properties&lt;/code&gt;, adding &lt;code&gt;languageModel=/Users/benbalter/ngrams&lt;/code&gt;, replacing the path to the absolute path of where you downloaded and unzipped the n-gram data.&lt;/li&gt;
&lt;li&gt;Restart the service: &lt;code&gt;brew services restart languagetool&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you did it correctly, again, it should “just work”, but if you want to confirm, you should see a message in the logs that says &lt;code&gt;INFO: Using n-gram data from /Users/benbalter/ngrams&lt;/code&gt; (or whatever path you used).&lt;/p&gt;
&lt;h2 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#troubleshooting&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, LanguageTool logs live in: &lt;code&gt;/opt/homebrew/var/log/languagetool/languagetool-server.log&lt;/code&gt; and Caddy logs live in: &lt;code&gt;/opt/homebrew/var/log/caddy.log&lt;/code&gt;. If you want to see if LanguageTool is running, you can use &lt;code&gt;brew services list&lt;/code&gt;, or try one of the &lt;code&gt;curl&lt;/code&gt; methods mentioned earlier.&lt;/p&gt;
&lt;h2 id=&quot;taking-it-a-step-further&quot;&gt;Taking it a step further&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#taking-it-a-step-further&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you &lt;em&gt;really&lt;/em&gt; want to make sure what you type doesn’t traverse the internet, you can modify your hosts file or use another method (like firewall rules or Little Snitch) to block the public LanguageTool server (&lt;code&gt;api.languagetool.org&lt;/code&gt;) at the network level. This way, if you accidentally forget to set the “API Server URL” to “Local server” in one of the clients, the request will fail, and you’ll know that the client is not using the local server.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That’s it! You now have a powerful, privacy-respecting grammar checker that you can use in your browser, in Slack, in VS Code, and in other common work applications. Happy writing!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-grammarly&quot;&gt;
&lt;p&gt;Nothing against Grammarly. It’s a great product. I prefer to use open source tools whenever I can. &lt;a href=&quot;#user-content-fnref-grammarly&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-vscode&quot;&gt;
&lt;p&gt;I used the VS Code extension to write this post, so I &lt;em&gt;really&lt;/em&gt; hope there aren’t any typos. &lt;a href=&quot;#user-content-fnref-vscode&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-homebrew&quot;&gt;
&lt;p&gt;If you’re not familiar with Homebrew, it’s a macOS package manager that makes it easy to install, manage, and update command-line software. If you don’t have it installed, you can do so by running the following command: &lt;code&gt;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;&lt;/code&gt;. &lt;a href=&quot;#user-content-fnref-homebrew&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-disambiguation&quot;&gt;
&lt;p&gt;The non-cask version is the backend Java server. The cask version (by the same name) is the macOS frontend that provides a menubar icon and settings UI. &lt;a href=&quot;#user-content-fnref-disambiguation&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;Liked this post? It&apos;s now a book.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Silencing dissent isn&apos;t crisis management</title><link>https://ben.balter.com/2024/01/08/dissenting-voices/</link><guid isPermaLink="true">https://ben.balter.com/2024/01/08/dissenting-voices/</guid><description>Silencing dissent erodes trust, invites negativity, and stifles learning. The best leaders embrace transparency instead.</description><pubDate>Mon, 08 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2024/01/08/dissenting-voices/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Every seasoned leader I’ve worked with has had to make an unpopular or controversial decision. It’s one of the hallmarks of good leadership. But almost as important as the decision itself, is how you engage with your team following the decision.&lt;/p&gt;
&lt;p&gt;It’s understandable. You spend days, weeks, maybe months agonizing over every detail, every pro and con, every possible outcome. You’ve done your due diligence, and you’ve made the best decision you can given the situation. You’re confident in your decision, and you’re ready to move forward. Then comes the inevitable push back from your team. They have questions, concerns, and objections. They’re not happy with the decision, and they’re not shy about letting you know.&lt;/p&gt;
&lt;p&gt;It’s easy to dismiss that negativity as ignorant, personal, or a vocal minority (“you can’t please everyone”), after all, they have a fraction of the perspective you have as a leader. You might seek to “control the narrative”, by dismissing such voices as inviting negativity, undermining your authority, or asking questions that you’ve already answered, thinking it’s unproductive to further discuss a decision you know is final. Those are all understandable and human reactions.&lt;/p&gt;
&lt;h2 id=&quot;controlling-the-narrative-is-counterproductive&quot;&gt;“Controlling the narrative” is counterproductive&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#controlling-the-narrative-is-counterproductive&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Attempting to “control the narrative” by silencing dissent is not only ineffective, it’s also counterproductive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It erodes psychological safety&lt;/strong&gt; - When you shut down the conversation, you send a message that you don’t value the input, the perspective, or the feelings of those affected by your decision. You also signal that you have something to hide, or that you’re not confident in your own reasoning, further eroding trust.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It invites negativity&lt;/strong&gt; - When you ignore the questions, you don’t make them go away. You just push them underground, where they fester and grow. People will start to speculate, gossip, and complain. They will feel resentful, frustrated, and alienated. They will look for ways to resist, sabotage, or undermine your decision.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It stifles learning&lt;/strong&gt; - When you avoid the scrutiny, you miss an opportunity to “&lt;a href=&quot;/2022/02/16/leaders-show-their-work/&quot;&gt;show your work&lt;/a&gt;” and teach through action. An organization’s culture and values are comprised of the underlying assumptions that its members use to make decisions. When decisions are transparent, you offer opportunities for the next generation of leaders to learn and grow through passive observation and lightweight participation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;caremad-is-a-good-thing&quot;&gt;“Caremad” is a good thing&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#caremad-is-a-good-thing&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like your own reaction, your team’s reaction is equally human, and I’d argue, actually a good thing.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; It means they’re engaged. It means they care. While you’re ready to move on, they just got the news, and they’re still processing. The difference between good leaders and great leaders is in the follow through. Specifically, in their willingness to engage in the &lt;em&gt;emotional labor&lt;/em&gt; of helping their team figure out how to move forward, together.&lt;/p&gt;
&lt;p&gt;That dissent isn’t disengagement or obstructionism as it may be easy to misread. Quite the opposite. They’re likely “caremad”&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; - they’re upset because they care about ensuring the best possible outcomes and are trying to figure out how to do that. The reality is, questions and dissenting voices are a sign that people care about the decision being made. They want to understand the reasoning behind it, and they want to have a say in the process. And as a leader, it’s your job to listen to those voices, take their concerns into account, and plot an &lt;em&gt;emotional&lt;/em&gt; path forward.&lt;/p&gt;
&lt;h2 id=&quot;the-alternative&quot;&gt;The alternative&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#the-alternative&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best leaders I’ve worked with have always been open to dissenting voices. They’ve always been willing to listen to the other side of the argument, and to engage with it, even if they’re not willing to change their mind. The playbook is simple: be transparent, open, and accountable. This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Be transparent&lt;/strong&gt; - Share the information, the data, and the evidence that informed your decision. Explain the criteria, the trade-offs, and the implications. Acknowledge the limitations, the uncertainties, and the challenges. Don’t hide, spin, or sugarcoat the facts. Be honest, clear, and consistent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be open&lt;/strong&gt; - Invite the questions, the feedback, and the dialogue. Listen to the concerns, the objections, and the suggestions. Respond with respect, empathy, and curiosity. Don’t dismiss, deflect, or rebut the views. Be receptive, humble, and collaborative.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be accountable&lt;/strong&gt; - Take responsibility for your decision, your actions, and your results. Admit any mistakes, apologize, correct, and improve. Don’t blame, deny, or justify the faults. Don’t put the burden on others. Be honest, humble, and results-focused.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While some see leadership as bold and courageous decision making in the face of a difficult situation, I would argue that covering one’s ears and humming in the face of after-the-fact questions shows a lack of leadership in the moment. It’s the equivalent of saying “I would have gotten away with it if it weren’t for you mangy kids…” (for those that grow up watching Scooby-Doo and other Saturday morning “who done it” cartoons).&lt;/p&gt;
&lt;p&gt;The alternative to the “I don’t like what they’re saying so they shouldn’t be allowed to say it” approach is to embrace transparency, openness, and accountability. In practice: name the situation’s complexity, show the evidence behind your call, take your stakeholders’ questions head-on, and own your mistakes. Stay humble — listen, learn, and change course when the argument warrants it. And stay consistent: communicate often and honestly, and follow through on what you promised.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;I’d take an engaged team that can have healthy, productive, and respectful disagreement any day over a team that’s checked out, indifferent, or apathetic. If they don’t care enough about this thing to speak up, it’s likely that there are other, bigger things that they’re not speaking up about either. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;A portmanteau of “care” and “mad”, meaning a software developer who is passionate, enthusiastic, and diligent about their work, and who cares deeply about the quality, functionality, and impact of their code and products. A caremad developer is not satisfied with mediocrity, but strives for excellence, innovation, and continuous improvement. For example, “She’s caremad about accessibility, she always makes sure every feature is compatible with screen readers and keyboard navigation.” &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Cathedral vs Bazaar People Management</title><link>https://ben.balter.com/2023/12/08/cathedral-bazaar-management/</link><guid isPermaLink="true">https://ben.balter.com/2023/12/08/cathedral-bazaar-management/</guid><description>What if we applied open source&apos;s cathedral vs. bazaar metaphor to management? Cathedral managers control; bazaar managers empower.</description><pubDate>Fri, 08 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2023/12/08/cathedral-bazaar-management/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B0026OR3LM/?tag=benbalter07-20&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;The Cathedral and the Bazaar&lt;/a&gt; is &lt;em&gt;the&lt;/em&gt; book on open source. It contrasts closed source software development (the cathedral), where a centralized and hierarchical authority designs and builds a well-defined system, and open source development (the bazaar), where a decentralized community of contributors can browse, experiment, and collaborate within a modular and adaptable system. The book argues that the bazaar model is more effective, innovative, and resilient than the cathedral model, and in many ways, brought the ideals of the open source movement to the mainstream.&lt;/p&gt;
&lt;p&gt;I’ve written before about &lt;a href=&quot;/2023/01/10/manage-like-an-engineer/&quot;&gt;bringing software development methodology to management&lt;/a&gt;, so what if we apply this cathedral vs. bazaar metaphor to people management styles?&lt;/p&gt;
&lt;h2 id=&quot;cathedral-people-management&quot;&gt;Cathedral people management&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#cathedral-people-management&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The cathedral manager is like the architect of the cathedral. They have a clear and detailed vision of what they want to achieve, and how to get there. They plan everything in advance and assign specific roles, tasks, processes, and standards to their team. They monitor and control their team’s work, and give them precise instructions, feedback, and guidance.&lt;/p&gt;
&lt;p&gt;The cathedral style of people management is characterized by a high degree of control, direction, and standardization with problems and solutions being identified and defined in a top-down manner. Its advantage is that it can produce predictable and reliable results, especially in complex and stable environments. It can also create a much-needed sense of order, clarity, and security for members of the team, who find comfort in knowing what is expected of them and what they can expect in return.&lt;/p&gt;
&lt;p&gt;The disadvantages of the cathedral style of people management are that it can stifle creativity, speed, and innovation among knowledge workers, especially in dynamic and uncertain environments that require a lot of experimentation and adaptation. It can also create a sense of rigidity, bureaucracy, and hierarchy for the team, who may feel constrained, micromanaged, and disempowered.&lt;/p&gt;
&lt;h2 id=&quot;bazaar-people-management&quot;&gt;Bazaar people management&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#bazaar-people-management&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The bazaar manager is like the organizer of the bazaar. Leaders in this style tend to have a broad vision, a flexible plan, and a flat network of roles and responsibilities for the team. The manager acts as the facilitator, the coach, and the enabler of the team’s work, defining goals and objectives and providing guidelines, feedback, and resources, while empowering the team to define their own tasks, processes, and standards, encouraging them to explore and innovate.&lt;/p&gt;
&lt;p&gt;The bazaar style of people management is characterized by a high degree of autonomy and collaboration, with problems and solutions being identified and defined by the team. Its advantage is that it can produce innovative and resilient results, especially in dynamic and uncertain environments. It can also create a sense of freedom, empowerment, and ownership for team members, who can shape their own work, express their own voice, and pursue their own growth.&lt;/p&gt;
&lt;p&gt;At the core of the bazaar style is the belief that team members are competent, self-motivated, and capable of aided self-direction. They encourage their team to explore their interests, share their ideas, and contribute their skills to further the team’s goals. This approach can also foster a culture of openness and trust among the members of the organization, who can learn from each other, support each other, and challenge each other.&lt;/p&gt;
&lt;h2 id=&quot;diagnose-the-situation-then-pick-your-style&quot;&gt;Diagnose the situation, then pick your style&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#diagnose-the-situation-then-pick-your-style&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whether you’re a manager or an individual contributor (&lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Individual Contributor—someone who does the work rather than managing people&quot; title=&quot;Individual Contributor—someone who does the work rather than managing people&quot; tabindex=&quot;0&quot;&gt;IC&lt;/abbr&gt;), know which style you default to—and which your manager defaults to. Your team might be a mix, or the right approach might shift depending on the scenario. That’s fine. The two styles typically differ in a few concrete ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By &lt;strong&gt;industry&lt;/strong&gt;—The self-direction of the bazaar model wouldn’t be a good fit for the military, given the stakes. The cathedral model’s rigidity would suffocate a fast-paced startup that needs room to experiment.&lt;/li&gt;
&lt;li&gt;By &lt;strong&gt;individual&lt;/strong&gt;—A junior engineer needs the structure and clarity the cathedral model provides—“here’s the issue, here’s the approach, and here’s an example &lt;abbr class=&quot;initialism&quot; data-tooltip=&quot;true&quot; data-tooltip-text=&quot;Pull request—a proposed set of code changes submitted for review&quot; title=&quot;Pull request—a proposed set of code changes submitted for review&quot; tabindex=&quot;0&quot;&gt;PR&lt;/abbr&gt; to reference.” A senior engineer might chafe at that much direction, preferring freedom to find problems nobody’s filed yet and propose solutions the team hadn’t considered. Likewise, some people are less comfortable living with ambiguity, while others prefer uncertainty and the autonomy that comes with it.&lt;/li&gt;
&lt;li&gt;By &lt;strong&gt;role&lt;/strong&gt;—Outside engineering, the cathedral model fits a line cook—the recipe is well-defined and consistency is everything. The bazaar model fits chefs who value creativity and experimentation as they seek to create new dishes.&lt;/li&gt;
&lt;li&gt;By &lt;strong&gt;work environment&lt;/strong&gt;—Distributed and async teams need more bazaar-style management because you can’t watch people work. Cathedral managers achieve visibility through direct oversight—standing over someone’s shoulder, literally or figuratively. In distributed teams, transparent workflows provide that same visibility without the micromanagement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Find the right balance—and be willing to switch as the situation demands. &lt;a id=&quot;quote-match-management-to-the-moment&quot; href=&quot;#quote-match-management-to-the-moment&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;match-management-to-the-moment&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Match the management to the moment.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;putting-it-into-practice&quot;&gt;Putting it into practice&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#putting-it-into-practice&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;For managers:&lt;/strong&gt; Distributed teams generally thrive with bazaar-style leadership because it emphasizes autonomy and trust—exactly what you need when you can’t see people working. But be ready to shift toward the cathedral when onboarding new team members, during crises requiring rapid coordination, or when establishing standards everyone must follow. The point isn’t ideological purity; it’s flexibility. If someone’s work starts slipping, lean into more cathedral-style management and the visibility advantages of working in the open until they’re back on track.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For ICs:&lt;/strong&gt; Knowing your manager’s style helps you meet them where they are. If they lean cathedral, proactively share progress and ask clarifying questions before proceeding. If they lean bazaar, take initiative and document your decisions. Senior ICs often operate bazaar-style regardless of their manager—defining problems, proposing solutions, and bringing others along through influence rather than authority.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-cathedral-bazaar-operating-systems&quot; href=&quot;#quote-cathedral-bazaar-operating-systems&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;cathedral-bazaar-operating-systems&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Cathedral and bazaar aren’t personality traits—they’re management operating systems you can swap depending on the moment.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; For distributed teams, the default should lean bazaar (autonomy plus transparency), but knowing when to shift toward cathedral matters just as much. As a manager, ask yourself: are you building a cathedral or a bazaar? As an IC, ask yourself: do you prefer working in a cathedral or a bazaar? And most importantly—are you and your manager on the same page? Get the balance right, and your team ships better work with less friction.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post is adapted from my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>How to communicate like a GitHub engineer</title><link>https://ben.balter.com/2023/10/04/how-to-communicate-like-a-github-engineer/</link><guid isPermaLink="true">https://ben.balter.com/2023/10/04/how-to-communicate-like-a-github-engineer/</guid><description>How GitHub turned its guiding communication principles into prescriptive practices to manage internal signal-to-noise ratio.</description><pubDate>Wed, 04 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2023/10/04/how-to-communicate-like-a-github-engineer/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Transparent collaboration is the andon of knowledge work</title><link>https://ben.balter.com/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/</link><guid isPermaLink="true">https://ben.balter.com/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/</guid><description>Like Toyota&apos;s andon cord, transparent collaboration lets anyone stop the line when they spot a problem in knowledge work.</description><pubDate>Wed, 30 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2023/08/30/transparency-collaboration-is-the-andon-of-knowledge-production/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The shift from waterfall to agile was predicated on the belief that what worked for the factory floor during the Industrial Revolution would not work for knowledge work in the Information Age, but assembly lines and modern software development have more in common than you might think:&lt;/p&gt;
&lt;p&gt;Agile development can trace its roots back to the production floor of Toyota as far back as the 40s and 50s. At the core of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Toyota_Production_System&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Toyota Production System&lt;/a&gt; (what eventually inspired the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lean_manufacturing&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;lean manufacturing&lt;/a&gt; movement) was the concept of an &lt;a href=&quot;https://en.wikipedia.org/wiki/Andon_(manufacturing)&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;andon&lt;/a&gt;, a concept we continue to see driving benefit in modern software development today.&lt;/p&gt;
&lt;h2 id=&quot;anyone-can-stop-the-line-for-any-reason&quot;&gt;Anyone can stop the line for any reason&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#anyone-can-stop-the-line-for-any-reason&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In short, an andon is a system of lights and signals that allows workers to alert managers and colleagues to any problems or defects in the production process. With the pull of a cord or the press of a button at each station by any worker, the entire production system grinds to an instant halt.&lt;/p&gt;
&lt;aside class=&quot;objection&quot; aria-label=&quot;Objection and response&quot;&gt;&lt;p class=&quot;objection-q&quot;&gt;&lt;span class=&quot;objection-label&quot;&gt;But what about…&lt;/span&gt; Isn’t it counterintuitive to make it easier for &lt;em&gt;anyone&lt;/em&gt; to shut down production?&lt;/p&gt;&lt;div class=&quot;objection-a&quot;&gt;
&lt;p&gt;The idea is that &lt;a id=&quot;quote-stop-the-line-early&quot; href=&quot;#quote-stop-the-line-early&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;stop-the-line-early&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;if you stop the line as soon as a problem is detected, it can be fixed before it gets worse&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. It’s been so successful in driving continuous improvement that Toyota (and most other manufacturers) continue to use the concept to this day.&lt;/p&gt;
&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The andon is designed to empower workers to stop the line, identify the root cause of the issue, and implement corrective actions, rather than letting it pass unnoticed or hoping someone else will fix it later. The andon is not only a tool for quality control, but also a culture of continuous improvement, where everyone is encouraged to speak up, share feedback, and collaborate on solutions. The andon fosters a sense of ownership, accountability, and learning among the workers, as well as transparency, trust, and alignment among the managers.&lt;/p&gt;
&lt;h2 id=&quot;transparency-is-software-developments-andon&quot;&gt;Transparency is software development’s andon&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#transparency-is-software-developments-andon&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Transparent collaboration in software development and other knowledge work is like the andon in a production system. It allows anyone to spot and call out critical issues early on, before they get worse, and to engage in constructive dialogue and problem-solving with their peers and stakeholders. Some of the benefits of transparent collaboration are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Faster and better feedback loops&lt;/strong&gt; - By making the work visible and accessible to everyone, transparent collaboration enables faster and more frequent feedback from users, customers, and colleagues. This feedback can help validate assumptions, identify gaps, and improve the quality and usability of the product or service. It can also help prevent misunderstandings, conflicts, and rework, as well as foster a culture of experimentation and learning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher engagement and motivation&lt;/strong&gt; - By giving everyone a voice and a stake in the work, transparent collaboration increases the engagement and motivation of the workers. It allows them to express their ideas, opinions, and concerns, and to contribute to the decision-making and problem-solving processes. It also allows them to see the value and impact of their work, and to receive recognition and appreciation from their peers and managers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greater trust and alignment&lt;/strong&gt; - By creating a shared understanding and a common goal, transparent collaboration builds trust and alignment among the workers and the managers. It reduces the barriers and silos that can hinder communication, collaboration, and coordination. It also promotes a culture of honesty, openness, and accountability, where everyone is responsible for the quality and outcome of the work, and where everyone can learn from mistakes and successes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Higher quality and customer satisfaction&lt;/strong&gt; - By spotting and solving critical issues early on, you can prevent or reduce defects, errors, and rework, and improve the quality and usability of your software. By seeking and providing feedback throughout the process, you can ensure that your software meets the needs and expectations of your users and customers, and delivers value to them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster and more efficient delivery&lt;/strong&gt; - By making your work visible and accessible, you can reduce the overhead and waste of information silos, duplication, and handoffs, and streamline the software development process. By stopping the line and escalating issues when necessary, you can avoid or minimize delays, disruptions, and dependencies, and speed up the software delivery cycle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, transparent collaboration is not without its challenges and trade-offs. It requires a shift in mindset, behavior, and culture, as well as a set of tools, processes, and norms that support and enable it. It also requires a balance between openness and privacy, between collaboration and autonomy, and between feedback and focus.&lt;/p&gt;
&lt;h2 id=&quot;applying-the-andon-principle-to-software-development&quot;&gt;Applying the andon principle to software development&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#applying-the-andon-principle-to-software-development&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The benefits of transparent collaboration outweigh the costs, especially in a complex, uncertain, and dynamic environment, where the ability to adapt, innovate, and deliver value is essential. By adopting the andon principle in your own work, you can leverage the collective intelligence, creativity, and experience of your teams, and create better products and services for your users and customers. Here are some ways you can apply the andon principle to your software development team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Make your work visible and accessible&lt;/strong&gt; - Lean into transparency. Don’t view openness and early, broad engagement as a potential liability or risk that someone might “shoot down” your idea, but as an opportunity to get feedback and improve your work. Use tools and processes that allow you to share your work with your team and your stakeholders, and to solicit and incorporate their feedback. Don’t wait until the end of the project or the release cycle to get feedback. Instead, seek and provide feedback throughout the software development process, from ideation to deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop the line and escalate issues when necessary&lt;/strong&gt; - Don’t ignore or hide problems, or hope that someone else will fix them. Be proactive and responsible for the quality and outcome of your work. If you encounter a critical issue, such as a bug, a security issue, a performance degradation, or a user complaint, stop what you’re doing and alert your team and your stakeholders. Don’t resume your work until the issue is fixed, or a mitigation plan is in place. Learn from the issue, and implement preventive or corrective actions to avoid or reduce its recurrence.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seek and provide feedback early and often&lt;/strong&gt; - Don’t wait until the end of the project or the release cycle to get feedback from your users, customers, and colleagues. Solicit and incorporate feedback throughout the software development process, from ideation to deployment. Use techniques such as user research, prototyping, testing, and experimentation to validate your assumptions, identify gaps, and improve your software. Be open and receptive to feedback, and use it to learn and improve, not to blame or defend. Give constructive and timely feedback to others, and appreciate and acknowledge their contributions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collaborate and problem-solve with your team and your stakeholders&lt;/strong&gt; - Don’t work in isolation, or assume that you have all the answers. Leverage the collective intelligence, creativity, and experience of your team and your stakeholders. Involve them in the decision-making and problem-solving processes, and respect their perspectives and inputs. Use methods such as brainstorming, design thinking, and retrospectives to generate and evaluate ideas, and to identify and address root causes. Communicate clearly and effectively, and align on the goals, expectations, and roles. Celebrate and share your successes, and learn from your failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a culture of continuous improvement&lt;/strong&gt; - Don’t settle for the status quo, or assume that there’s nothing you can do to improve. Continuously reflect on your work, and look for ways to make it better. Use tools and processes such as retrospectives, postmortems, and root cause analysis to identify and address issues, and to implement preventive or corrective actions. Encourage and reward experimentation, learning, and innovation. Be open to change, and embrace new ideas and approaches. Don’t be afraid to fail, and learn from your mistakes. Share your learnings with others, and help them improve.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Software development is not the same as manufacturing, but it shares some similarities. Both are complex, iterative, and collaborative processes that involve creating and delivering value to customers. Both also face challenges such as quality, efficiency, and innovation. And both can benefit from the andon principle.&lt;/p&gt;
&lt;p&gt;The andon principle is not a silver bullet, but it’s a powerful tool for spotting and solving critical issues early on, and for fostering a culture of collaboration in your software development team. By adopting the andon principle, you can empower yourself and your team to stop the line when necessary, to collaborate and problem-solve with your peers and stakeholders, and to create better products and services for your users and customers. So, next time you encounter an issue in your software development process, don’t hesitate to pull the andon cord. Your team and your customers will thank you for it.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Remote work requires communicating more, less frequently</title><link>https://ben.balter.com/2023/08/04/remote-work-communicate-more-with-less/</link><guid isPermaLink="true">https://ben.balter.com/2023/08/04/remote-work-communicate-more-with-less/</guid><description>Async communication is like gzip compression for humans—more upfront processing, but greater throughput with fewer packets.</description><pubDate>Fri, 04 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2023/08/04/remote-work-communicate-more-with-less/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Remote work is not simply a matter of replicating the office environment online. One of the key shifts that remote workers need to make is to communicate &lt;em&gt;more&lt;/em&gt;, less &lt;em&gt;often&lt;/em&gt;. Instead of relying on constant, synchronous, and often interrupt-driven interactions, remote workers embrace asynchronous, and often higher-fidelity, forms of communication, such as long-form writing or thoughtful videos.&lt;/p&gt;
&lt;p&gt;I’ve &lt;a href=&quot;/2022/03/17/why-async/#benefits-of-working-asynchronously&quot;&gt;written before&lt;/a&gt; about the benefits of working asynchronously, but less obviously, it also changes the &lt;em&gt;way&lt;/em&gt; we think and work. Async work allows for more reflection, research, and synthesis. Those working async can and should take the time to think, learn, and synthesize before sharing their ideas, opinions, or solutions, distilling them down to the most critical. This improves the quality and clarity of the communication, and most importantly, the overall throughput of the communications channel.&lt;/p&gt;
&lt;p&gt;&lt;a id=&quot;quote-gzip-for-communication&quot; href=&quot;#quote-gzip-for-communication&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;gzip-for-communication&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Think of it like gzip compression, but for human-to-human communication.&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; Yes, there’s slightly more processing overhead at the start, but it allows greater communications throughput using fewer “packets” (communicate more using less).&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; How can you communicate more, less often? Here are a few tips I often keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a id=&quot;quote-choose-the-right-medium&quot; href=&quot;#quote-choose-the-right-medium&quot; class=&quot;quote-inline&quot; data-quote-id=&quot;choose-the-right-medium&quot;&gt;&lt;mark class=&quot;quote-inline-mark&quot;&gt;&lt;strong&gt;Choose the right medium for the message&lt;/strong&gt;&lt;/mark&gt;&lt;span class=&quot;sr-only&quot;&gt; (share this quote)&lt;/span&gt;&lt;span class=&quot;quote-inline-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M9 17H7A5 5 0 0 1 7 7h2&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M15 7h2a5 5 0 1 1 0 10h-2&quot;&gt;&lt;/path&gt;&lt;line x1=&quot;8&quot; x2=&quot;16&quot; y1=&quot;12&quot; y2=&quot;12&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; - Remote workers should use the most appropriate and effective form of communication for the purpose, audience, and context. For example, use writing for documenting, explaining, or persuading; use video for demonstrating, teaching, or storytelling; use chat for coordinating, clarifying, or socializing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write clearly, concisely, and comprehensively&lt;/strong&gt; - Remote workers should write with the reader in mind, using simple language, short sentences, and clear structure. They should also provide enough detail, context, and evidence to support their points, answer potential questions, and avoid ambiguity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Record videos with empathy, enthusiasm, and engagement&lt;/strong&gt; - Remote workers should record videos with a human touch, using eye contact, facial expressions, and voice modulation. They should also keep their videos short, focused, and interactive, using visuals, examples, and questions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communicate proactively, regularly, and asynchronously&lt;/strong&gt; - Remote workers should communicate their goals, plans, and updates without waiting for prompts, requests, or deadlines. They should also communicate their availability, boundaries, and preferences without assuming or imposing. They should communicate asynchronously as much as possible, using synchronous communication only for urgent, complex, or sensitive matters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remote work requires communicating more, less often, because asynchronous communication involves less frequent, but richer communication, meaning there is less time talking &lt;em&gt;about&lt;/em&gt; the work and more time &lt;em&gt;doing&lt;/em&gt; it, allowing the system to optimize for throughput and flow.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot; aria-label=&quot;Footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;a class=&quot;anchor-link&quot; aria-label=&quot;Link to this section&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor-icon&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Not to mention, thoughtful, written communication helps ideas to scale more effectively (there’s a reason the printing press spurred the Renaissance). It shifts communication from a one-to-one relationship to a one-to-many. Next time you’re about to “send a quick DM” or “schedule a quick chat,” consider what steps you could take to optimize for the long tail of information retrieval and discovery. Just like building a web app, “writes” are expensive, but “reads” should be “cheap,” so avoid introducing mental N+1s into the system whenever possible. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item><item><title>Practice inclusive scheduling</title><link>https://ben.balter.com/2023/05/19/practice-inclusive-scheduling/</link><guid isPermaLink="true">https://ben.balter.com/2023/05/19/practice-inclusive-scheduling/</guid><description>Small scheduling choices — writing dates unambiguously, including time zones, and building in breaks — make distributed teams feel included.</description><pubDate>Fri, 19 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;margin:0 0 1.75em;font-size:15px;color:#57606a;&quot;&gt;A new post from &lt;a href=&quot;https://ben.balter.com&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;ben.balter.com&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://ben.balter.com/2023/05/19/practice-inclusive-scheduling/&quot; style=&quot;color:#0969da;text-decoration:none;&quot;&gt;Read it on the web &amp;rarr;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Time zones are one of the harder parts of software development, but it doesn’t have to be one of the harder (or exclusionary) parts of working as a distributed team. Here are a few practices that I try to adhere to help practice more inclusive scheduling when working remotely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When discussing dates, consider writing numeric dates in &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_8601&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ISO 8601&lt;/a&gt; (YYYY-MM-DD) or other month/day unambiguous formats.&lt;/li&gt;
&lt;li&gt;When referring to a time, always include the timezone.&lt;/li&gt;
&lt;li&gt;Avoid location-specific language like “tomorrow”, “this afternoon”, or “in the spring”.&lt;/li&gt;
&lt;li&gt;Be mindful of holidays, weekends, and working hours, especially across time zones.&lt;/li&gt;
&lt;li&gt;Consider “speedy meetings” (end 5/10 minutes early or start 5/10 minutes late) to allow for time to be human between meetings, and be strict about ending at that earlier time.&lt;/li&gt;
&lt;li&gt;On that note, meetings should start and end on time. If you finish early, consider using the remainder of the time for informal conversations and to connect as humans.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1179/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/iso_8601_2x.png&quot; alt=&quot;xkcd comic describing ISO 8601 as the one true date format&quot; loading=&quot;eager&quot; decoding=&quot;auto&quot; fetchpriority=&quot;high&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A small nod to inclusivity can go a long way to create a sense of belonging and reduce ambiguity, when working with global teams, schedule and communicate with a global (and remote) audience in mind.&lt;/p&gt;&lt;hr style=&quot;margin:2.5em 0 1.5em;border:none;border-top:1px solid #d0d7de;&quot; /&gt;&lt;table role=&quot;presentation&quot; width=&quot;100%&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;margin:0 0 1em;border-collapse:collapse;&quot;&gt;&lt;tr&gt;&lt;td style=&quot;border:1px solid #d0d7de;border-radius:8px;padding:16px 20px;background:#f6f8fa;&quot;&gt;&lt;p style=&quot;margin:0 0 6px;font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#57606a;&quot;&gt;Out now&lt;/p&gt;&lt;p style=&quot;margin:0 0 6px;font-size:16px;font-weight:600;color:#1f2328;&quot;&gt;This post inspired a chapter in my book, Open &amp;amp; Async.&lt;/p&gt;&lt;p style=&quot;margin:0 0 12px;font-size:14px;color:#424a53;&quot;&gt;The collaborative software development playbook for remote and distributed teams.&lt;/p&gt;&lt;a href=&quot;https://open-and-async.com/?utm_source=benbalter-email&quot; style=&quot;font-size:14px;font-weight:600;color:#0969da;text-decoration:none;&quot;&gt;Buy it — $9.99 &amp;rarr;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</content:encoded><author>ben@balter.com</author></item></channel></rss>