Two companies that I started following (with no small amount of envy) back in 2021: Hype (fka Pico) sold to an MMA-themed holdco earlier this year. Raised a $4.5m seed from Stripe and Bloomberg Beta and a $10m Series A; crossed the finish line at $200K ARR after eight years. Stir (which raised $16M from a16z at a $100M valuation) informally...
Hello! Lots of writing in October (what early parenthood takes away in terms of deep flow, it gives back in terms of twenty-minute pockets of time and thought): On the content front, I wrote about Monster Sanctuary (a delightful JRPG), The Anderson Tapes (a solid late-period Sean Connery heist), Trust (a rare postmodern novel that I disliked),...
Kevin Twohy has a list of heuristics for new projects/clients, and my favorite is simple: No Bozos. Simple policy. No exceptions. You know it when you see it. Every founder has a story about the time that they ignored the red flags and bent over backwards for a particularly standoffish or problematic customer/prospect/client/hire. It's easy to...
(Epistemic disclaimer: there are few Extremely Big Tech Companies to whom I feel apathy more vividly than Meta. I had a Facebook account in high school and college and got rid of it at some point post-graduation, though I'd be hard-pressed to tell you when exactly that was.) It was fun to listen to Acquired's six-hour episode on Meta. Ben and...
Lots of kind words poured in as a response to My approach to GTD and PKM, and one question was asked so frequently that I decided to write about it. Why is “Get mom a birthday present” a project and not a task? GTD is very orthodox in what a “project” is: it’s anything that: you think you’ll complete in the next year requires more than one step...
Earlier this week, I stumbled upon this brilliant marketing-slash-documentation idea from SingleStore — notebooks as a first-party page! There are a handful of nice things about this idea: Very easy to fan out. You're not going to really run out of sample notebooks from which you can create pages. Easy to share and 'productize'. Lends itself...
One pernicious thing with writing about productivity and knowledge systems: you only change systems that aren’t working, and you tend to write about things during times of change. So most writing about productivity systems stem from people whose systems have failed them. It is with this ironic prologue that I answer a question I've received from...
The advice might seem dated these days, but I think the stair step approach to bootstrapping is evergreen: It’s much easier to sell an add-on to an existing ecosystem like a WordPress plugin, a Shopify app, a Heroku add-on – they’re usually small things to build, relatively inexpensive for customers to purchase, and you have built in discovery...
A prior iteration of this site had a page called "Project ideas" that listed a bunch of things that I'd like to build. This was a good idea and useful in its own right (I got a few people to build companies and projects based on them, and it was a useful avenue by which folks could tell me which ideas were already implemented.) I still haven't...
The total LOC delta to migrate Buttondown from Django 4 to Django 5 was +143, -150. (I was incentivized to do so because our search right now is quite slow, and the lowest hanging piece of fruit is to move some of the tsvector generation out of band, and I'd rather do that using the fancy new GeneratedField abstraction than rolling the SQL...
There are few pleasures greater than getting to be profiled for an interview series that you've been reading for months, and last week I got to do exactly that. To browse Manu's site — not just this interview series — feels a bit like walking on a quiet beach in autumn: there's a sense of peace and timelessness that is hard to find in the hustle...
You say that these numbers mean dial it down. I say they mean dial it up. You haven't gotten through. There are people you haven't persuaded yet. These number mean dial it up. Otherwise you're like the French radical, watching the crowd run by and saying, "There go my people. I must find out where they're going so I can lead them." I ran into a...
A little over six months ago, I wrote Notes on Zed. My conclusion at that time was that Zed made a lot of great choices and felt really good to use, but lacked parity with VS Code's feature/ecosystem to its detriment. Six months later, I spent a few days using it as my daily driver to see what had changed: It's still really, really fast — and in...
We've had Lucy for two weeks, which qualifies us as experts, which means it is time to write about parenthood. (In all seriousness, consider the below descriptive and not prescriptive: mostly, it's a notepad filled with things that were remarkable or surprising or divergent from popular consensus.) American pop culture puts too much emphasis on...
Chatted with Jess and Jeremy about a whole slew of things, from pricing strategy to terrifying and arcane differences between various Markdown parsers (including why I hate Slack.) They were particularly kind and gracious given that the amount of production-quality Rails I've written is ~zero; you'll enjoy this even if Ruby/Rails is not your cup...
When I was first starting my career at Amazon — even more bright-eyed and rosy-cheeked than I am now — I was thrilled by the concept of an "architecture review", and by extension the concept of a "Principal Engineer" (Amazon's term for a staff-level engineer, someone beyond career level) who was always treated with some level of mystique and...
In Notes on buttondown.com and How Buttondown uses HAProxy, I outlined the slightly kludgy way we serve buttondown.com both as a marketing site (public-facing, Next/Vercel, largely just content pushed by non-developers) and an author-facing app (behind a login, Django/Heroku/Vue) and recommended developers not do that and instead just do the...
Yesterday, I was trying to set a unique constraint for comments in Buttondown to prevent accidental double-commenting, and I ran into a problem that I hadn't seen before: index row size 2816 exceeds btree version 4 maximum 2704 for index "emails_comment_email_id_subscriber_id_text_0542cca9_uniq" DETAIL: Index row references tuple (165,7) in...
In Paul Graham’s latest essay, he writes: The theme of Brian's talk was that the conventional wisdom about how to run larger companies is mistaken. As Airbnb grew, well-meaning people advised him that he had to run the company in a certain way for it to scale. Their advice could be optimistically summarized as "hire good people and give them room...
Someone emailed me in response to Two years as an independent technologist, in which I wrote: I miss of being at a large company, which is dealing with deeply cutting-edge technical problems, but my ability to analyze information, make decisions, and perform at a high-level has grown very quickly. They followed up: I had a lot of trepidation...
There are few technical decisions I regret more with Buttondown than the decision to combine the author-facing app, the subscriber-facing app, and the marketing site all under a single domain. Most technical decisions are reversible with sufficient grit and dedication; this one is not, because it requires customers to change their URLs and...
We spent $85,000 for buttondown.com in April; this was the biggest capital expenditure I've ever made, and though it was coming from cash flow generated by Buttondown rather than my own checking account it was by rough estimation the largest non-house purchase I've ever made. As of August, we're officially migrated over from buttondown.com to...
When it comes to AI tooling, I am equal parts optimist and cynic. I have no moral qualm with using these tools (Supermaven is a pretty heavy part of my day-to-day work), but have found most tools quite bad by the metric of "do they make me more productive on Buttondown's code base?" I think it's important to be able to taste the kool-aid with...
Buttondown's API calls are very fast, and one of the reasons why is that we've removed every single possible database query that we can. The most recent was what looked like a fairly benign COUNT(*) query, coming from the default Django paginator; if you're gonna paginate things, you need to know how many to paginate, fair enough. However, it...
A handful of folks sent me this quip from Nate Silver a few days ago: Slightly against interest to admit this (I don't want more competition lol) but I think we're still probably a year or two away from Peak Newsletter. It's just a really good distribution mechanism for certain types of writers. It does take some time to build up momentum,...
Andrew Rea with an interesting and increasingly familiar take about how AI will disrupt software-focused private equity: Distribution and brand moats can protect your legacy products for a while (esp in enterprise) but eventually you get lapped by competitors with better products, service, pricing, etc. Software is too competitive and changes too...
There’s a nascent trend of releasing ostensibly-private material (changelogs, public wikis, handbooks, etc.) to the public as a bit of a marketing push. This is essentially a form of debt, to the extent that you’re taking a lump-sum payment now in exchange for the implicit cost of keeping these things “up to date” indefinitely (and if you don’t,...
Glyph (whose writing and contributions to the Python ecosystem I am deeply grateful for) wrote Against Innovation Tokens yesterday: In 2015, Dan McKinley laid out a model for software teams selecting technologies. He proposed that each team have a limited supply of “innovation tokens”, and, when selecting a technology, they can choose boring ones...
Inspired by Adam Johnson's test for pending migrations, and of course in conversation with my own love of weird tests, I offer a similar concept: a test for finding stray print statements in your codebase, with a ratchet array for stuff to ignore. import glob PATH = "**/*.py" irrelevant_paths = ( "/commands/", "node_modules", ) def...
I watched Gary Bernhardt's talk on static routing back a few years ago and — I'm not sure if I would call it formative, but it stuck in my craw as a platonic ideal of sorts, as something I couldn't really justify adopting within Buttondown but really wanted. I built out and open-sourced some feints in this direction — see...
Lots of writing this month: Why you should use Rails A reminder that things take time How shadcn/ui's previews work Why I hate that 1.01% meme A lovely term, 'grace note' Au revoir, invoke! A quick check-in on 11ty A rant/snippet for auth.js The first edition of what will be a long, living document on ActivityPub A handy git one-liner Why it's...
Unlike stories, real life, when it has passed, inclines toward obscurity, not clarity. There’s a relatively famous line from Bezos, circulated first at YC in ’08 and then more recently by the folks at Acquired (a great pod!): Jeff uses this analogy for AWS. He talks about European beer breweries around the turn of the 20th century. Electricity...
I wanted to get a commit that was temporally some distance back as part of my experimentation with git cliff. This took some time to do, but here's what I ended up with: git log --since="72 hours ago" --until="now" --reverse --pretty=format:"%h" | head -1 Nothing particularly fancy, but I hope it's useful!
Buried in a snarky thread about why Google Calendar doesn’t support calendar syncing is a long, detailed explanation of why shipping this sort of thing is hard: Designing a consent experience for the enterprise admin and enterprise end user Creating a special "read only" object type that's only populated via one-way sync from a consumer account....
By far the single most-fruitful tactic has been "just look at raw GET responses from Mastodon and see what things are shaped like." I know that "ActivityPub is under-specified" is a bit of a meme, but it's wild how little prior art there is. Something that gives distinctly bad vibes: ostatus.org (which ostensibly describes a bunch of the schemae...
Internal tools and small, well-scoped projects are a great avenue to tinker with technologies on the periphery of your understanding, and a Third South project has led me to spin up a small Next project using Bun [1] and Auth.js (nee next-auth), which has been quite bad and I think successfully dissuaded me from using it in any more serious...
It's not quite interesting or noteworthy enough to warrant a full-on essay, but yesterday we unshipped the last remaining Invoke commands and ported them over to just. I think Invoke is a good, cool project, and I wish it well. If you're at the precise intersection of "you have shell commands that need to be encapsulated in some source of truth",...
I'm not loving Unreasonable Hospitality, but it did supply me with a phrase that I've been looking for: Eventually, that gesture became one of our steps of service. The host would ask guests, “How’d you get here tonight?” If they responded, “Oh, we drove,” he’d follow up with, “Cool” Where’d you park?” If they told him they were by a meter on the...
If you spend enough time digesting hackneyed business or self-improvement advice, you've probably seen someone wax poetic about the following image: This is meant to be an illustration of the power of incrementalism (often referred to as kaizen when the writer is trying to be particularly obscure/profound) [1]. Get 1% better every single day, and...
I’m increasingly convinced that for developer-first tools, a really good docs experience is a durable, non-trivial advantage. Part of this thesis is that Really Good Docs Experiences, in addition to having great information architecture and strong writing/prose, should be thought of less as ancillary content repositories that can be farmed out to...
Things take time. Nintendo fairly famously was born in 1889, and the modern incarnation — Yamamuchi Nintendo & Co., LTD — was established nearly fifty years later, in 1933. They spent forty years selling playing cards, then another decade operating merely as a distributor of electronics before coming out with their first piece of electronic...
I have a good number of people ask me what software stack they should use. I always have a two-part answer: Use what you're familiar with. If there's something that you've spent a good amount of time using, stick with that one. Rails. People are usually surprised when I say this because I don't use Rails, and while I spent some time writing Ruby...
Welcome to spring, bona fide and humid. Lots of writing this month: I wrote about spending the past two years as an independent technologist. On the media side of things: brief words on Murderville (meh), The Parallax View (incredible), This is Personal (meh), Cribsheet (solid!). Lots of postging: on friction logs, on AI hype, on Stripe Sessions...
Postgres' JSONB functionality is fast and useful but when I find myself dropping down from the Django ORM into SQL to do weird things, the syntax strikes me as confusing and arcane. As such, when I need to do esoteric things it takes me longer time than I'd like, and in hopes that this saves you ten minutes of Stack Overflow trawling: SELECT id,...
Lots of people have spent the past few days discussing the perceived increase in difficulty in getting an entry-level programming job relative to the halcyon ZIRP days of yesteryear. I am sympathetic to new grads running into this; I am dismayed that when I ask some [1] of them what they've been doing their answer boils down to "sending out my...
Cognition, a six-month-old startup in the AI coding space just raised $175m at a $2b valuation: Despite the skepticism surrounding Devin’s launch, the AI coding assistant has shown promising results. According to the SWE-Bench benchmark, which evaluates AI models on software engineering tasks, Devin achieved a 13.86% accuracy in resolving issues...
Stripe held the keynote for Sessions, their annual WWDC/re:invent-esque conference, this morning. I wanted to jot down some thoughts while they’re still fresh. (I think the changelog is the best way to poke around both the changes I’m writing about and the ones that I omitted.) Caveat / disclosure I am a Stripe user and shareholder. That being...
I wrote last month about using weird tests. Here's another example: checking for broken internal links in our upcoming docsite redesign! const extractInternalLinks = (content: string): string[] => { // Check for internal links only, in the form of [text](/path). const internalLinks = content.match(/\[.*?\]\(\/.*?\)/g); return ( ...
I wrote two days ago about a real and useful application of Tailwind black magic; here's another. Buttondown has a dropzone component lets folks drag-and-drop items or click on it to get a file-picker. It's used for importing images, archives, CSVs, the works: because it's so flexible, we expose a slot so that components can customize the text...
The new version of the Buttondown docs site is all in on Keystatic, Markdoc, and Tailwind's typography plugin — which makes it really easy to author beautiful docs in plaintext. We ran into one small issue, which is that the Markdoc renderer likes to place paragraph tags in table cells, such that: | Column |...
For a long time, my goal with Buttondown was largely around failure avoidance: "I want to get my first paying customer so I know it's not a fake product"; "I want to hit a thousand dollars in revenue so I know it's not just friends humoring me"; "I want to make $10K/mo or else I won't be able to work on it full-time if I want to"; and so on. It's...
One of the more interesting theses advanced by Zero to One [1] is that monopolies are good to the extent that they afford companies the agency and comfort to engage in long-term activities: Monopolists can think about things other than making money; non-monopolists can’t. In perfect competition, a business is so focused on today’s margins that it...
Via HN I ran into not one but two extremely neat and pleasant-looking libraries for URL manipulation. They look like great libraries, and a prior version of me would have taken a brief set of cursory glances at the hodgepodge of janky URL manipulation code that I wrote for Buttondown and said "okay, time to rip out all of this and replace it with...
We've been using Keystatic in Buttondown for around six months now: we migrated most of the content on the marketing site (which is backed by Next) from MDX onto Keystatic, and were so happy with the experience that the upcoming rebuild of the docs site will be featuring Keystatic as well. It's hard to accurately describe why Keystatic, because...
Histoire, like so many other tools in the Vue ecosystem, is a bit of a neglected younger sibling to Storybook — a little bit uglier, with worse documentation and a couple rough edges, but much more tightly integrated with Vue and Vite. [1] One thing that was not particularly obvious with using Histoire was how to declare a global variable. (There...
I was late to the VS Code zeitgeist, and as penitence I try to go out of my way to try new editors whenever I see them — which is why this morning I installed Zed, which makes its bones on performance (yay!) and teams functionality (irrelevant for my use cases, but seems abstractly fertile.) First: it is quite fast, and feels good in many of the...
I had bookmarked Kolo many months ago to try out and finally got a chance to integrate it with Buttondown — a process that I expected to take a couple hours on a lazy Sunday and in fact took ten minutes and three lines of code. If you have a Django app, I think you should drop everything you're doing and install it right now. It is tremendously,...
XH asks: How do indie developers/small teams keep track of and prioritize long-term roadmaps? I've been basically work off my gut + Feeling of the Day for the past few years, and that's getting a bit unsustainable Buttondown's roadmapping has existed for the past three years in a bimodal fashion: In November—December, I spend a lot of active and...
.njk as the default templating language is an odd choice, and I find myself stubbing my toe on it a good amount. Maybe that's a me thing! It is extremely fast. This site has around 2300 pages; Eleventy is compiling it in around two seconds. The extensibility ergonomics are terrific. This is all it takes to declare a new filter, for instance:...
Was digging through old issues of the personal site and found this draft snippet: I was evaluating HelpScout as a potential first step in a series of many, many steps to onboard a dedicated support engineer for Buttondown, and it turns out that the process of adding custom content to a given conversation (ie the Stripe information, account...
Working on a new analytics engine — a scant eleven months after the previous 'new analytics engine'. Calling this 3.0 is a bit of a misnomer: most of the code, design, and plumbing from the 2023 redesign is sticking around, just in a more modular format. The goal here is to address a couple shortfalls in the previous architecture: Business and...
Virginia has draconian liquor laws, which means I have to get interesting bottles shipped from [REDACTED], a site on which I am very prone to judging a book by its cover; every month I'll end up purchasing a bottle of something purely because it looks interesting and I can vaguely imagine it fitting into a couple cocktails down the line. This...
Most Stripe accounts on Substack are “Standard Connect”, which essentially means that: the author has full agency over their account and is the merchant of record; they can revoke OAuth access from Substack (or whomever) at any time; Substack continues to take that 10% as an application fee, though the author (or another connected account) can...
One of many two-liners to come as I migrate things from the old site onto Obsidian: brew install rename rename "s/.mdx/.md/" **.md
It hasn't failed me yet: BATCH_SIZE = 100 def batch_proess(queryset) -> None: count = queryset.count() if count == 0: time.sleep(10) return with transaction.atomic(): batch = list( queryset.select_for_update(of=("self",), skip_locked=True).values_list( "id", flat=True ...