diff --git a/astro.config.mjs b/astro.config.mjs index b757e56b7..34f3dac58 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -181,6 +181,7 @@ export default defineConfig({ './src/components/GitHubCulture.astro', './src/components/FossAtScale.astro', './src/components/YouTube.astro', + './src/components/BookLaunchCta.astro', // astro-embed components for zero-JS social embeds { 'astro-embed': ['Tweet', 'Vimeo', 'LinkPreview'], diff --git a/src/components/BookCta.astro b/src/components/BookCta.astro index 2ff3bbfa2..4d99f78e0 100644 --- a/src/components/BookCta.astro +++ b/src/components/BookCta.astro @@ -2,10 +2,12 @@ /** * Book CTA Component * - * Promotes the Open & Async book with a link to the book site. + * Promotes the Open & Async book with a buy link to the book site. * Supports two variants: inline (for posts) and featured (for homepage). * Both quote the marketing site (open-and-async.com): a dark-navy "book * object" anchored by the 3D cover mockup, with the lime→pink accent system. + * Now that the book has launched, both variants sell ("Buy it") rather than + * capture emails. For the dedicated launch announcement use BookLaunchCta. */ import { Image } from 'astro:assets'; @@ -63,17 +65,17 @@ const commitGraphPaths = `

- Coming {siteConfig.bookLaunch} + Out now

- {siteConfig.bookDescription}. Drawing on a decade at GitHub — sign up to get notified when it launches. + {siteConfig.bookDescription}. Drawing on a decade at GitHub — out now on Kindle, Apple Books, Kobo & more.

- Get notified + Buy it — {siteConfig.bookPrice}
@@ -94,10 +96,10 @@ const commitGraphPaths = ` />

- Coming {siteConfig.bookLaunch} + Out now

- Like this post? It's becoming a book. + Liked this post? It's now a book.

- Get notified when it launches + Buy Open & Async — {siteConfig.bookPrice}
diff --git a/src/components/BookLaunchCta.astro b/src/components/BookLaunchCta.astro new file mode 100644 index 000000000..0b907bacc --- /dev/null +++ b/src/components/BookLaunchCta.astro @@ -0,0 +1,136 @@ +--- +/** + * Book Launch CTA — the bespoke, high-impact "buy now" panel reserved for the + * launch announcement post. Louder than BookCta's inline/featured variants: a + * glowing lime→pink gradient ring around a full-width navy panel, the 3D cover, + * an oversized title, and dual buy buttons (ebook + paperback). Use BookCta for + * the everyday inline/homepage promos; use this once, on the launch post. + */ + +import { Image } from 'astro:assets'; +import { siteConfig } from '../config'; +import coverImage from '../../assets/img/open-and-async-cover.webp'; + +// Reuse the marketing site's gradient-ampersand treatment for the title. +const bookTitleHtml = siteConfig.bookTitle.replace( + /\s*&\s*/g, + '&', +); + +// Commit-graph motif (matches BookCta): faint branch graph along the bottom. +const commitGraphPaths = ` + + + + + + + +`; +--- + +
+ +
+
+ + +
+ +
+ Open & Async by Ben Balter — book cover +
+ + +
+

+ + Out now · {siteConfig.bookLaunch} + +

+ +

+ +

+ +

+ {siteConfig.bookDescription}. +

+ + + +

+ One link, every store — Kindle, Apple Books, Kobo & more. +

+
+
+
+
+
+ + diff --git a/src/config.ts b/src/config.ts index 59c352802..3739c9143 100644 --- a/src/config.ts +++ b/src/config.ts @@ -65,11 +65,14 @@ export const siteConfig = { // Find this in your Kit dashboard under Forms → form embed code. kitFormId: '9381290', - // Book + // Book — now launched, so the callouts sell ("buy now") rather than capture + // emails. bookPrice leads the CTAs; paperback is the secondary option. bookUrl: 'https://open-and-async.com/', bookTitle: 'Open & Async', bookDescription: 'The collaborative software development playbook for remote and distributed teams', bookLaunch: 'July 21, 2026', + bookPrice: '$9.99', + bookPricePaperback: '$29.99', // About page metadata aboutDescription: 'Ben Balter is a writer, open source developer, and attorney whose open source projects have hundreds of millions of downloads. Formerly Director of Hubber Enablement at GitHub.', diff --git a/src/content.config.ts b/src/content.config.ts index 067beb649..29596a803 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -55,7 +55,8 @@ const postsCollection = defineCollection({ // Post metadata image: z.string().optional(), // Open Graph image - + hideBookCta: z.boolean().default(false), // Suppress the auto-appended BookCta (e.g. the launch post supplies its own BookLaunchCta) + // SEO metadata sitemap: z.boolean().default(true), // Include in sitemap by default robots: z.string().optional(), // Robots meta tag diff --git a/src/content/posts/2026-07-21-open-and-async.mdx b/src/content/posts/2026-07-21-open-and-async.mdx new file mode 100644 index 000000000..30ffebd68 --- /dev/null +++ b/src/content/posts/2026-07-21-open-and-async.mdx @@ -0,0 +1,46 @@ +--- +title: "Open and Async: the remote-work playbook is out today" +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." +tldr: "Open and Async is out today. It's the opinionated, practical playbook for the two habits that make distributed teams actually work—working in the open and communicating asynchronously. Drawn from a decade-plus at GitHub. Get it at open-and-async.com." +image: "https://open-and-async.com/og-image.png" +# This post supplies its own BookLaunchCta, so suppress the generic one that +# PostLayout appends to every post (avoids two buy-now CTAs back to back). +hideBookCta: true +--- + +Most companies didn't go remote. They shipped everyone a laptop, bolted Zoom onto the same approval chains, and called it a day. 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. + +**Working from home ≠ working remotely.** There's a world of difference between being forced out of the office and intentionally building a distributed, async culture. That difference is what *Open and Async* is about—and as of today, you can [read it](https://open-and-async.com). + +[*Open and Async*](https://open-and-async.com) is the opinionated, practical playbook for the two practices that make distributed work actually work: **working in the open** and [**communicating asynchronously**](/2022/03/17/why-async/). 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. + +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. + +## What separates teams that thrive + +More than a decade of remote-first work at GitHub made one thing clear: 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. + +The teams that pulled ahead did something different. They gave every decision [a URL](/2015/11/12/why-urls/) so context survived reorgs and departures. They treated meetings as escalation, not default. They [worked loudly](/2022/02/16/leaders-show-their-work/) so impact was visible without anyone performing busyness. None of it was intuition—it was a set of repeatable practices, and *Open and Async* is those practices, written down so you can put them to work. + +## Who it's for + +Two audiences, both in remote and distributed work: + +* **Managers**—build teams that ship without constant synchronous coordination, and retain your best people by respecting their time, focus, and intelligence. +* **Individual contributors**—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. + +## What's inside + +A few of the things you'll learn how to do: + +* Treat meetings as a point of escalation, not the default—and reclaim the hours you're losing to "quick syncs." +* Give every decision a URL so context survives reorgs, departures, and Slack purges. +* Run 1:1s, weekly reports, and career conversations that aren't a tax on everyone's time. +* Lead—whether or not you have direct reports—by showing your work. +* Hire, onboard, and transition teams to remote-first work without losing what made them good. + +It's 575 pages, and the method ships as more than a manuscript. There's even a [Model Context Protocol (MCP) server](https://github.com/open-and-async/mcp) 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. + +Stop digitizing the office. Start building something better. + + diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index c754c6e85..03622e5b3 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -42,6 +42,7 @@ interface Props { prevPost?: { title: string; url: string; readingTime?: number } | undefined; nextPost?: { title: string; url: string; readingTime?: number } | undefined; archived?: boolean; + hideBookCta?: boolean; postId: string; postFilePath?: string | undefined; posts?: Record | string[] | undefined; @@ -60,6 +61,7 @@ const { prevPost, nextPost, archived = false, + hideBookCta = false, postId, postFilePath, posts: curatedPosts, @@ -149,7 +151,7 @@ const blogPostingSchema = pubDate ? generateBlogPostingSchema({ - + {!hideBookCta && } diff --git a/src/pages/[year]/[month]/[day]/[slug].astro b/src/pages/[year]/[month]/[day]/[slug].astro index bb5fcbbea..ec5c8d2bc 100644 --- a/src/pages/[year]/[month]/[day]/[slug].astro +++ b/src/pages/[year]/[month]/[day]/[slug].astro @@ -72,6 +72,7 @@ const nextPost = nextEntry ? { title: nextEntry.data.title, url: getPostUrl(next prevPost={prevPost} nextPost={nextPost} archived={post.data.archived} + hideBookCta={post.data.hideBookCta} postId={post.id} postFilePath={post.filePath} posts={post.data.posts}