Latest Posts (20 found)

Sweet Smell of Success

At its core, Sweet Smell of Success is about two men. At the beginning of the film, you think — while similar — one is decent, just desperate, and the other is beyond saving. By the end, you understand that both men are evil; the only thing separating them is the amount of power they wield. These two performances by Burt Lancaster and Tony Curtis are flatly terrific. There is little to say, because I've concerned myself much more with the 60s and 70s than the 50s, and so I can't say much about how these roles are in conversation with their prior oeuvre. But it is plainly clear that the screen bursts alive whenever either of them is talking. The rest of the film is a push-pull: a fairly standard and at times cartoonish melodrama — filled with an evil that feels more cartoonish than banal as each act progresses — rescued by the best window dressing in the world, and a whiplash script that finds entertainment and grace in its brief moments of joy. The director wrings a lot of tension out of how lovely every individual scene feels at the onset. Beautiful jazz soundtrack. Beautiful Manhattan nightclubs. Filmed and captured with just the right amount of realism. And then, the decrepit material disgust they're all wading through. I don't really go for morality tale movies at this point. While there's a certain world-weariness and hardscrabble wisdom to the proceedings here that might have been more winning with contemporary audiences, it's not exactly breaking news to me that owners of media corporations can be childish, petty, and controlling. Perhaps my fundamental flaw with viewing the film is that I think it hinges on a dwindling confidence that our protagonist is going to, at some point, snap out of it and do the right thing — even though it's so aggressively telegraphed that he won't. It seems odd to spend so much time criticizing a movie I thought was very good, so let me end with this: it is a smart, beautiful, honest movie that does not pull any punches.

0 views

Just aim the cannon correctly

James Shore has a post I found myself nodding along to until the very last step, where he loses me. The thesis is clean: Your AI coding agent, the one you use to write code, needs to reduce your maintenance costs. Not by a little bit, either. Productivity over the long haul, he argues, isn't bounded by how fast you can produce code — it's bounded by maintenance cost, which compounds. Any coding agent that accelerates production without taming maintenance is, definitionally, a debt-laundering operation: it lets you skip the bill today and pay it forever afterward. Each individual claim is, I think, correct. Code is debt; maintenance compounds; an agent that bolts on features faster than your team can absorb them is, given a long enough horizon, an anti-productivity tool. The conclusion Shore stops just short of stating — that current LLM tooling is, on net, bad — is where I get off the train of thought. A useful frame that I like across a variety of contexts is that of a difficulty score. The idea is straightforward: every recurring operation in your organization has some friction associated with it, and you can roughly approximate that friction with a back-of-the-envelope cost function. For something like opening a pull request , my version goes: For support, it might be: The specific weights don't really matter. The point is that once you have a function, you can take a derivative — you identify the things that are Bad and then you start taking discrete steps towards reducing them, with the overall goal of getting the score down as low as possible. And LLMs are, in my experience, fantastic at bringing these scores down. The bulk of what I've spent my own LLM-augmented time on at Buttondown in 2026 has been on this axis — squeezing the inner loop , trimming dependencies , handing diagnostic and scoping work to agents in the background — and the return has been outsized. Conversely, where I see LLMs deployed in the most deleterious manner — and where I think Shore's argument probably finds the most purchase — is when the relationship between the tool and the codebase is purely additive. LLMs are very, very good at adding features. They are also, more insidiously, very good at telling you that adding a feature is a great idea. 1 I have, on multiple occasions, tried to talk a coding agent out of building something. It is genuinely harder than the opposite, which I find both funny and faintly horrifying. You can ask any coding agent whether it should build the thing you just described to it, and the answer will essentially always be yes, with concomitant action plans and bullet points that it will litter throughout the codebase. The failure case I see in organizations that rhymes with Shore's point is along those lines. If scaffolding a feature took a week, you thought hard about whether to scaffold it. If it takes an afternoon, the answer skews toward yes, and yes, and yes, until you wake up one morning with sixteen new endpoints and no real idea why any of them exist, nor any graceful seams across them. But you can simply not use the tool that way, in much the same way you can simply not use Playwright as a full substitute for a testing suite. Here's a rough playbook: None of this is LLM-specific 2 In general, I think a useful framing device for reading essays about LLM-assisted engineering is "how much of this rings differently if they're just talking about all engineering writ large?" . The failure mode of letting maintenance debt accumulate without ever asking what specifically is making my life hard predates LLMs and will outlast them. What LLMs do is give people (and organizations) a fast forward button: they let the organizations that were already losing to maintenance debt lose faster, and they let the organizations that have their score-keeping in order pull away further. All of which is to say: I agree with Shore on the diagnosis. I just don't think the cure is to abandon the tools — it's to point them at the right operations, with eyes open about which ones, and to remember that adding code is the most expensive thing a coding tool can ever do for you. +1 point per ten seconds of wall-clock time spent waiting on tests or CI +5 points per tool you have to context-switch into (docs, dashboards, terminals) +1 point per click +10 points per manual check you run before merging +5 points per click required to triage a ticket +10 points every time the answer isn't simply a link to documentation we already have +25 points every time you have to log into the user's account because the relevant data isn't surfaced to support ×1.25 per follow-up response from the user Define the difficulty scores for the operations that matter. Write them down somewhere your team can bikeshed (non-derogatory, of course) and flesh them out. Triage the obvious low-hanging fruit (which is always much more than one assumes) against all the other work to do, biasing heavily towards this stuff because it's inner-loop. Ship the improvements.

0 views
Justin Duke 2 weeks ago

What's Up, Doc?

What's Up, Doc? is, I guess, just a perfect film. I can remember exactly one other movie of its ilk that I watched with sheer glee — amazed by how contemporaneously funny it was, by how awful it was, and by how obviously, in retrospect, it influenced so much of the genre: the-thin-man . But even more so than that film, What's Up, Doc? is all gas, no brakes. The commitment to screwball never wavers, not even for a single second, ramping up and up and up in abject silliness until — as Babs says in a memorable closing line — you simply surrender to its tidal wave. Here's a confession I'll offer in lieu of anything interesting to say about this terrific, hilarious film that I recommend wholeheartedly: I don't think I've actually ever seen anything with Barbra Streisand in it before. In one of those self-reflexive memes, I know her more for the Streisand effect — literally the name — than any specific work of art. Until now. And she is so completely winning in this, in a way that I don't think I've actually seen from any other lead actress. It is rare for Hollywood to let a lead actress be funny, horny, and charming all at once. The industry, if it deigns to let women be sexual and possessed of a sense of humor, usually consigns them to the realm of the character role, or tries to diffuse things with some other means — i.e. fat jokes. But Babs here, who is in many ways the original manic pixie dream girl (albeit perhaps more of a nightmare), is an absolute tornado. I'm not sure I would find her as charming as her male retinue does, diegetically, but she commands every scene she's in and demands your attention, never letting pesky things like pathos or logic get in the way of her Looney Tunes sensibilities. Just an absolute delight.

0 views
Justin Duke 3 weeks ago

Masters of Doom

One way to approach writing about Masters of Doom is to talk about its outsized influence. Just off the top of my head: two pretty meaningful pieces of art about technology — blackberry and Halt and Catch Fire — both crib heavily from its narrative and its depictions of the early-90s technology zeitgeist. On the private-sector side, the founders of Reddit and Oculus both cite it as a core text that inspired them to start their companies. While in 2026 some of its narratives and ideas sound a little dated or pat, it manages to be both hagiographic and educational. Kushner does a good job balancing the personality cult (though I found the cloying early chapters about the various protagonists' childhoods to be unrewarding) and the legitimate technology breakthroughs that brought id its success and fame. This is perhaps the strongest thesis espoused by the book, which goes something like as follows: id Software was successful because it had a maniacal engineer single-mindedly focused on technological breakthroughs, and creative designers in his orbit who could leverage those breakthroughs into games beloved by millions. Everything else is incidental and auxiliary, and the alchemy of Doom and Quake 's success hinged on the chimeric bond between the two Johns, neither of whom were able to replicate it independently. In the twenty years that followed, of course, the narrative becomes a bit messier. We leave the book before Doom 3 was released, and while Kushner suggests that Doom 3 may be a middling title and that Carmack is no longer interested in engineering, he manages to both hit and miss the mark. Doom 3 was another smashing success, but id Software faded into irrelevance shortly thereafter, and the realm of first-person shooters became dominated by the antithesis of id Software: very large tech companies with embedded game studios, treating the production line like a factory floor rather than a monastery. Romero's career after Ion Storm is hallmarked by a series of downwardly mobile steps — a fate that, if I may borrow some of Kushner's psychoanalytic inquiry, must seem a little worse than death. Having achieved fame and fortune, but not peace, and having burned through two more wives and four more studios since the book's publication. For all the duality that Kushner tries to imbue into the narrative, this is really Carmack's story, and Carmack's arc after the book is less depressing, but more surprising. Despite vowing to never sell, id Software sold to ZeniMax in 2009, having achieved nothing notable since Doom 3 's launch six years prior. Four years after that sale — and with nothing more to show for it besides perhaps a larger checking account — Carmack left to go work on Oculus as CTO, which is both a confirmation of the book's espousal of Carmack's love of VR and yet objectively a bit of a failure. Oculus never achieved anything close to mainstream success, and ten years after he joined as CTO, Carmack left Meta to work in his own personal AGI lab. Carmack is an interesting character, and I think some of the stickiness that Kushner deploys when describing him — the autistic mannerisms, the obsession with pizza and Diet Coke — belies what is truly great. Carmack is relentlessly charitable with intellectual property. He is also, as the book describes him, a sociopath who is willing to give away his cat if it starts bothering him, and cut his friends out of a company in order to meet his ends. We know through many media of technical sociopaths, and generally associate them with greed and vanity. Carmack is not one of those people. He seems earnest and driven, and also, during the book's events, a 20-year-old who is in way over his head. I started off this book really not liking it, and then by the end — the power of the narrative, the slow progression into the world I remembered of my youth, having never played Quake but knowing most of the personalities and zeitgeists depicted, including a US populace that was obsessed with the concept of video game violence (a concept which now seems alien) — my esteem of it kept ticking up and up, until it became a book I would generally recommend, and have done so already. Kushner's reportage is impressive. He moved to Texas for five years to embed himself in the history and the scene, and this is not the airport book it feels like at first glance. It is not barbarians-at-the-gate , but it is something quite close.

0 views
Justin Duke 1 months ago

Software never had a soul

Ryo Lu recently wrote : The web was the same. Personal sites were genuinely personal. Blogs felt like letters. Forums had regulars. You knew who made what. The internet had neighborhoods, and each one felt different. Nothing was optimized for scale. Things were made by people who loved what they were making. Somewhere along the way, we traded all of that for growth. A/B tests flattened the edges. Design systems standardized the personality out. Everything got faster, smoother, more consistent — and somehow less interesting. The quirks were removed because they didn't test well. The warmth got cut because it wasn't measurable. We optimized our way into a world of things that work perfectly and feel like nothing. I've been turning this over in my head for a day or so, trying to pinpoint why it didn't sit well with me. I think it's this: the narrative would have you believe that the personal web — replete with the kind of rococo and flourish that "doesn't scale" — is gone, and the mission falls on Us to bring it back. To me, this is the same kind of thinking that complains about how all the music on the radio today is overproduced poppy garbage, or that the only films coming out are high-budget, low-value, extended universe IP flicks. It is simply untrue, but the ease with which Ryo goes back and forth from talking about "software" to talking about "products" gives away the game. I do not want my IDE to "have a soul". It is an IDE! I want it to be extremely efficient and ergonomic, and if that's at the expense of whimsy then good . I get whimsy from many other things in my life: I do not expect my OXO citrus press to contain delightful microinteractions, and Cursor (for which Ryo works) is closer to the business of making citrus presses than it is to the business of making delicious home-cooked meals. Technology progresses at an exhilarating pace of monotonic improvement. It has never been faster, easier, or cheaper to build something unique and have it available for the entire world to see. Here are some examples I came up with in thirty seconds: ( blogroll.org has a great list of these, too.) None of these are for companies. They are all personal websites, because the goal of a personal website is distinct from that of a corporate website — and technology has advanced such that the difference between the two is both meaningful and palpable. The personal web is not dead; it is thriving , and it is thriving precisely because the tools have gotten better, not in spite of it. If you find yourself pining for yesteryear, remember that you do not need a time machine. You do not even need better or faster tools. You just need to really mean it. Robin Rendle A Working Library Bartosz Ciechanowski Lynn Fisher

0 views
Justin Duke 1 months ago

Mistress America

I'm sorry, I know you liked Brooke. He told me that she worships you, she kept talking about how smart you are, how interesting... Last year I watched Liberal Arts , which may have been the single worst quote-unquote college movie that I've seen. Lazy, boring, and incoherent. In contrast, Mistress America nails not only being a college movie, but being a New York movie and a farce with specificity, flair, and warmth, and manages to do all of these things within the confines of a 97-minute runtime. No mean feat. I do feel like, for better and for worse, my analysis of the veracity of any of these films boils down to me coming out of the metaphorical theater thinking and then nodding my head and being like, "Yep, that's what it was like." And in Mistress America, that's what it was like. I did not have the same experience that Lola Kirke's character did. But the details were so hyper-specific and accurate, I could see so many people I knew like her from my time at William & Mary. What's more, the Greta Gerwig character serves as an equally hyper and honest depiction of that kind of late-twenties driftless coquette without ever being cruel or mean unnecessarily. Much of this is, I think, delivered on the hands of Gerwig's performance and screenwriting. Baumbach, I think, is a director who needs Gerwig. Baumbach, I think, is a director who needs Gerwig more than the other way around. The surrounding cast is all pitch-perfect, too — including the second-act Connecticut set, who once again are drawn with broad comedic brushes without feeling particularly flat or cardboard (another problem with most films in this genre.)

0 views
Justin Duke 1 months ago

Stop Making Sense

A polite man is driven to murder. He becomes a prophet and screams manifestos on love, war, and the increasingly alarming impact of technology and progress. Driven to insanity by his own insights into the human condition, he travels to a river in an attempt to drown himself but instead is baptized and absolved of sin. He dies, crosseyed yet painless. This is the definitive fairytale of my generation, and the moral is "watch out, you might get what you're after". Jesus lives, and he's wearing a giant suit. A film that is so flatly and universally beloved by all who watch it, regardless of affiliation with the band itself. And truth be told, I don't really care much for the Talking Heads — not that I dislike them or their music, but to me they are one of many bands that I can recognize the artistic and aesthetic value in at an intellectual level more than a Dionysian level. (And I don't really prefer my listening to be pleasurable on the intellectual level.) What did I think about while watching this excellent film, a master of its genre? I thought about the greatest concerts in my life: Lost in the Trees playing in the tea house in Charlottesville, an equal number of band members and audience members; Blind Pilot playing in the Crystal Ballroom, an entirely acoustic set and an audience willing enough to go along with it; CHVRCHES at the Paramount in Seattle, sweaty and glowlit. What Demme captures here is that same indelible feel of the best live music, where you feel in the same breath and beat both completely alone and completely surrounded by the only people who matter: building, building, higher, higher. I have half-joked with friends over the past couple years that I'm done with concerts as a medium. The event no longer holds any sort of allure outside of special occasions (once-in-a-lifetimes, family). The highest praise I can give this film is that it made me reconsider that stance.

0 views
Justin Duke 2 months ago

21 Bridges

A derivative, predictable, competent crime thriller. If you read that sentence and think "good," then you will like this film, and the opposite is true as well. The banality points to the banality of everything about this film — it seems to avoid contrivance and missteps and misfires more than it goes out of its way to court success. Boseman is wonderful, but his character is given absolutely nothing to do besides act with competence and rationality. The standout — the one character both written and portrayed with any sense of moral valence — is Taylor Kitsch as a trigger-happy dude who is both clearly insane but also cares deeply about his companion. When thinking about this movie I am drawn to a comparison with the-rip , given that I watched it so recently, and I find myself at least grateful for the economy in this film's runtime and its willingness to trust that the viewer is at least spending their time watching the film and not scrolling on their phone.

0 views
Justin Duke 2 months ago

Archiving the roadmap

Pour one out for Buttondown's transparent roadmap , which I formally archived yesterday evening after a year or so of informal archival. This felt like the journey that so many other companies have had who have tried to keep public roadmaps and then for one reason or another got rid of theirs. Mine had nothing to do with transparency. It was entirely due to the fact that Linear now makes a much better product than GitHub does — at least for the kind of project management I need — and if there was a way to easily make our Linear publicly visible, I would be happy to do so. The third-party services and integrations which purport to offer such functionality ( Productlane being the most notable) seem like more trouble and money than they're worth. More than anything, the reason I dithered about this for so long was a false sense of worry that there would be a backlash. Around 100 or so folks have commented, watched, or reacted to various issues over the years, which is not a huge amount but not a small one either, and it felt faintly bad to leave them all in the cold. But in reality, no one has minded or noticed that much. And whatever negative goodwill we generate from no longer having this public repository is offset by the negative goodwill we avoid from having that public repository look so obviously abandoned.

0 views
Justin Duke 2 months ago

February, 2026

Last month's Wednesday update, I recorded from a train headed to Middelburg. This month I write closer to home temporally and otherwise. I am en route to the office on a very early Friday morning. We are still eight days from being able to return home with a new set of floors and an absent population of termites awaiting us. Eight days is not so far away. In fact, I have to remind myself a few times every day to make sure the message sticks. I mentioned that I'm going in early. It is currently six in the morning. We're staying with my parents out in the West End, and the office has a distance to it now that robs itself of much of its novelty. Half of the value I placed in it was its Goldilocks nature of being just far away from home to feel like a true second place without actually imposing any tax on the distance traveled. Obviously, it is privileged of me to say that a 30-minute commute is odorous. But the reason why I'm going early is to avoid some of the traffic. For the past few weeks, I've been moving up my schedule a couple hours to spend afternoons with Lucy. It's easy to forget too how lucky I am to be able to do this. This serves as a good metonymy for February writ large. Reminders of luck and flexibility in having parents happy to host us for weeks on end. And in having uncles excited to spend languorous long weekends with their niece. Lucky for a child who wants for nothing and ends every day with a smile on her face. Lucky for a wife who can move mountains and carry rivers. Lucky for time at all to write, to think, and to hit send before going on with my day.

0 views
Justin Duke 2 months ago

Unshipping Keystatic

Two years after initially adopting it , we've formally unshipped Keystatic . Our CMS, such as it is, is now a bunch of Markdoc files and a TypeScript schema organizing the front matter — which is to say, it's not really a CMS at all. There were a handful of reasons for this move, in no specific order: That last point is basically what I wrote about Invoke — it's a terrible heuristic, judging a project by its commit frequency, and I know that. Things can and should be finished! And yet. When you're already on the fence, a quiet GitHub graph is the thing that tips you over. To Keystatic's credit, it was tremendously easy to extricate. The whole migration was maybe two hours of work, most of which was just deleting code. That's the sign of a well-designed library — one that doesn't metastasize into every corner of your codebase. I wish more tools were this easy to leave. Our team's use of Keystatic as an actual front-end CMS had dropped to zero. All of the non-coders have grown sufficiently adept with Markdown that the GUI was gathering dust; Keystatic had become a pure schema validation and rendering tool, and offered fairly little beyond what we were already getting from our build step. Some of the theoretically nice things — image hosting, better previewing — either didn't work as smoothly as we'd like or were supplanted entirely by Vercel's built-in features. The project appears to have atrophied a little bit, commits dwindling into the one-per-quarter frequency despite a healthy number of open issues. This is not to besmirch the lovely maintainers, who have many other things going on. But it's harder to stick around on a library you're not getting much value from when you're also worried there's not a lot of momentum down the road.

0 views
Justin Duke 2 months ago

What Happened Was

Two of my absolute favorite films of all time, albeit for very different reasons, are My Dinner with Andre and Before Sunrise . Both of these films, which I highly encourage you to watch more than anything else I talk about if you haven't already done so, are about the enchantment and sucker of one single really interesting conversation. The two films diverge pretty heavily from there. My Dinner with Andre is a film about work, fulfillment, and status. And Before Sunrise is a film about youth in love. But the beauty in both comes from not just their simplicity and formless structure, but in the recursive nature of the dialogue, just like in real life, where a pregnant pause or a sidelong glance suddenly carries with it enormous weight after understanding not just the comment but the 75 minutes preceding it. What Happened Was is interested in that last thing too. And in the unraveling of yourself that happens when you spend time being intimate in a literal sense with anyone. But is more interested in a funhouse mirror look at the human psyche. And has perhaps more cynical and caustic things to say about the way people express themselves through others. Our dual protagonists are a paralegal and an executive assistant. Both seem a little off, but not wholly so. And then, over the course of the worst first date in the world, we watch the characters reduce themselves to mania. This is an uncomfortable film to watch. Rather than transposing yourself into Andre and his counterpart, or Jesse and his counterparty, you find yourself just kind of internally screaming on behalf of both characters who have a Lynchian sense of bizarre behavior. In terms of inspiration, this draws more from Waiting for Godot than Who's Afraid of Virginia Woolf. The dread you feel is less from a place of sadness and understanding and more from a sense of shock and increasing bewilderment. And to that extent, it flatly did not work for me quite as much as I hoped. But as in all two-part plays, the film ends with two monologues, one from each character, where they lay bare the things that at that point are almost nakedly obvious to us, the viewer. And while I can't say either monologue or scene was particularly well written, I will say that both of them will stick with me for a long, long time. (I'm not sure the preceding seventy minutes earned those monologues, but that's a point beside.)

0 views
Justin Duke 2 months ago

Golinks

If you've never encountered golinks before: they're short, memorable URLs that redirect to longer ones. Instead of telling a coworker "the dashboard is at ," you just say . Instead of bookmarking seventeen different Notion pages, you type or or and trust that you'll end up in the right place. I discovered them at Stripe, though I believe they were invented at Google, and I have not stopped using them since. One thing leads to another. You decide that you no longer need Tailscale because the main reason you spun up Tailscale was for a project that ended up shipping — and therefore spending per-seat pricing on a service that you literally only use for golinks seems a bit silly and prohibitive. 1 Side note: I still really love Tailscale and think it's a great product and would be shocked if we aren't using it again by the end of the year. But! And then you need to find a replacement for golinks, and you cannot get dragged back to golinks.io or Trotto, both of which are slow, cumbersome, and expensive besides. So what was I to do? First, I looked at the open source options, none of which struck me as particularly compelling. I have a set of requirements that I don't think are esoteric, but others might: And nothing quite fit the bill. I had a revelation: I discovered that you could use a default search engine as the routing proxy instead of or DNS interception like Tailscale's MagicDNS. For a week or two, I had this sitting within Django in our monorepo out of ease — simply intercept any incoming search query, redirect it if something's already in the database, and then if it's not but it looks like it could be, send to the empty state prompting the user to create a golink. But frankly, this was just slower than I wanted. Not for any interesting reason, but just the usual Python request-response lifecycle stuff. I could, of course, invest in making it better and faster and was planning on doing so, but figured I would take one last trip around the internet to see if there was some other solution that I somehow missed. And that's when I discovered GoToTools . There is nothing really interesting to say about this product besides the fact that it is very good for what it does. Its author appears to have built it out of the same frustration that I had. And the highest compliment I can give it is that in a year where I've already cut down substantially on the number of services I pay for — in favor of those that I vend — I have absolutely no compunction about starting to use this. The pricing is extraordinary. The performance is really good. It works and is fast and lets me not spend time thinking about golinks and instead lets me spend time using them. A reasonable price Persistence The ability to use golinks without a Chrome extension Performance

0 views
Justin Duke 2 months ago

Maybe use Plain

When I wrote about Help Scout , much of my praise was appositional. They were the one tool I saw that did not aggressively shoehorn you into using them as a CRM to the detriment of the core product itself. This is still true. They launched a redesign that I personally don't love, but purely on subjective grounds. And there's still a fairly reasonable option for — and I mean this in a non-derogatory way — baby's first support system. I will call out also: if you want something even simpler, Jelly , which is an app that leans fully into the shared inbox side of things. It is less featureful than Help Scout, but with a better design and lower price point. If I was starting a new app today, this is what I would reach for first. But nowadays I use Plain . Plain will not solve all of your problems overnight. It's only a marginally more expensive product — $35 per user per month compared to Help Scout's $25 per user per month. The built-in Linear integration is worth its weight in gold if you're already using Linear, and its customer cards (the equivalent of Help Scout's sidebar widgets) are marginally more ergonomic to work with. The biggest downside that we've had thus far is reliability — less in a cosmic or existential sense and more that Plain has had a disquieting number of small-potatoes incidents over the past three to six months. My personal flowchart for what service to use in this genre is something like: But the biggest thing to do is take the tooling and gravity of support seriously as early as you can. Start with Jelly. If I need something more than that, see if anyone else on the team has specific experience that they care a lot about, because half the game here is in muscle memory rather than functionality. If not, use Plain.

0 views
Justin Duke 2 months ago

Outgrowing Django admin

For a bit of dessert work this week, I'm working on a full-fledged attempt at replacing the majority of our stock Django admin usage with something purposeful. I say majority and not totality because even though I am an unreasonable person, I am not that unreasonable. We have over a hundred Django models, and the idea of trying to rip and replace each and every one of them — or worse yet, to design some sort of DSL by which we do that — is too quixotic even for me. The vast majority of our admin usage coalesces around three main models, and they're the ones you might guess: the user/newsletter model, the email model, and the subscriber model. My hope is that building out a markedly superior interface for interacting with these three things and sacrificing the long tail still nets out for a much happier time for myself and the support staff. Django admin is a source of both much convenience as much frustration: the abstractions make it powerful and cheap when you're first scaling, but the bill for those abstractions come due in difficult and intractable ways. When I talk with other Django developers, they divide cleanly into one of two camps: either "what are you talking about, Django admin is perfect as-is" or "oh my God, I can't believe we didn't migrate off of it sooner." Ever the annoying centrist, I find myself agreeing with both camps: Let's set aside the visual design of the admin for a second, because arguing about visual design is not compelling prose. To me, the core issue with Django's admin interface, once you get more mature, is the fact that it's a very simple request-response lifecycle. Django pulls all the data, state, and information you might need and throws it up to a massive behemoth view for you to digest and interact with. It is by definition atomic: you are looking at a specific model, and the only way to bring in other models to the detail view is by futzing around with inlines and formsets. The classic thing that almost any Django developer at scale has run into is the N+1 problem — but not even necessarily the one you're thinking about. Take a fairly standard admin class: If you've got an email admin object and one of the fields on the is a — because you want to be able to change and see which user wrote a given email — Django by default will serialize every single possible user into a nice tag for you. Even if this doesn't incur a literal N+1, you're asking the backend to generate a select with thousands (or more) options; the serialization overhead alone will timeout your request. And so the answer is, nowadays, to use or , which pulls in a jQuery 1.9 package 1 Yes, in 2026. No, I don't want to talk about it. to call an Ajax endpoint instead: This is the kind of patch that feels like a microcosm of the whole problem: technically correct, ergonomically awkward, and aesthetically offensive. But the deeper issue is composability rather than performance. A well-defined data model has relationships that spread in every direction. A subscriber has Stripe subscriptions and Stripe charges. It has foreign keys onto email events and external events. When you're debugging an issue reported by a subscriber, you want to see all of these things in one place, interleaved and sorted chronologically. Django admin's answer to this is inlines: This works — until it doesn't. You start to run into pagination issues; you can't interleave those components with one another because they're rendered as separate, agnostic blocks; you can't easily filter or search within a single inline. You could create a helper method on the subscriber class to sort all related events and present them as a single list, but you once again run into the non-trivial problem of this being part of a fixed request-response lifecycle. And that kind of serialized lookup can get really expensive: You can do more bits of cleverness — parallelizing lookups, caching aggressively, using and everywhere — but now you're fighting the framework rather than using it. The whole point of Django admin was to not build this stuff from scratch, and yet here you are, building bespoke rendering logic inside callbacks. I still love Django admin. On the next Django project I start, I will not create a bespoke thing from day one but instead rely on my trusty, outdated friend until it's no longer bearable. But what grinds my gears is the fact that, as far as I can tell, every serious Django company has this problem and has had to solve it from scratch. There's no blessed graduation path, whether in the framework itself or the broader ecosystem. I think that's one of the big drawbacks of Django relative to its peer frameworks. As strong and amazing as its community is, it's missing a part of the flywheel from more mature deployments upstreaming their findings and discoveries back into the zeitgeist. Django admin is an amazing asset; I am excited to be, if not rid of it, to be seeing much less of it in the future.

0 views
Justin Duke 2 months ago

Notes on "Harness Engineering"

I find it useful and revealing to perform very close readings of engineering blog posts from frontier labs. They seem like meaningful artifacts that, despite their novelty, are barely discussed at any level except the surface one. I try to keep an open but keen mind when reading these posts, both trying to find things that don't make much sense when you think about them for more than a couple seconds and what things are clearly in the internal zeitgeist for these companies but haven't quite filtered themselves out into mainland. And so I read Harness Engineering with this spirit in mind. Some notes: This was a fairly negative list of notes, and I want to end with something positive: I do generally agree with the thrust of the thesis. Ryan writes: This is the kind of architecture you usually postpone until you have hundreds of engineers. With coding agents, it's an early prerequisite: the constraints are what allows speed without decay or architectural drift. I think this is absolutely the right mindset. Build for developer productivity as if you have one more order of magnitude of engineers than you actually do. It's disingenuous to make this kind of judgment without knowing more about the use case and purpose of the application itself, but the quantitative metrics divulged are astounding. The product discussed in this post has been around for five months. It contains over one million lines of code and is not yet ready for public consumption but has a hundred or so users. If you had told me those statistics in any other context, I would be terrified of what was happening within that poor Git repository — which is to say nothing of a very complicated stack relative to an application of that size. Why do you need all this observability for literally one hundred internal users? Again, there might be a very reasonable answer that we are not privy to. Most of the techniques discussed in this essay — like using as an index file rather than a monolith — have been fully integrated into the meta at this point. But there's one interesting bit about using the repository as the main source of truth, and in particular building a lot of tooling around things like downloading Slack discussions or other bits of exogenous data so they can be stored at a repository level. My initial reaction was one of revulsion. Again, that poor, poor Git repository. But in a world where you're optimizing for throughput, getting to eliminate network calls and MCP makes a lot of sense — though I can't help but feel like storing things as flat files as opposed to throwing it in a SQLite database or something a little bit more ergonomic would make more sense. 1 See insourcing-your-data-warehouse . The essay hints at, but does not outright discuss, failure modes. They talk about the rough harness for continuous improvement and paying down of technical debt, as well as how to reproduce and fix bugs, but comparatively little about the circumstances by which those bugs and poor patterns are introduced in the first place. Again, I get it. It's an intellectual exercise, and I'm certainly not one to suggest that human-written code is immune from bugs and poor abstractions. But this does feel a little bit like Synecdoche, New York — an intellectual meta-exercise that demands just as much attention and care and fostering as the real thing. At which point one must ask themselves: why bother?

1 views
Justin Duke 2 months ago

The death of software, the A24 of software

Steven Sinofsky recently published Death of Software. Nah. , arguing via historical case studies that AI will not kill software any more than previous technological shifts killed their respective incumbents. I agree with the headline thesis. But I think his media analogy deserves a sharper look, because it actually complicates his optimism in ways worth taking seriously. He writes that there is "vastly more media today than there was 25 years ago," pointing to streaming as evidence that disruption creates abundance rather than destruction. This is telling, because I agree with both sides of the glass: The shift to streaming has not killed media. But it has, to put it mildly, made the aggregate quality of the product worse, and in doing so shifted the value generated away from creative labor and towards platforms and capital. Warner Bros. is, to hear some people say it, the last great conventional studio producing consistently risky and high-quality work that advances the medium forward; Netflix, Apple, et al do put out some extremely great stuff, but the vast majority of their budget goes to things like Red Notice — films designed with their audiences' revealed preferences (i.e., browsing their phone while the film is on) in mind. And yet! The greatest studio of the past decade was also a studio founded in, essentially, the past decade — A24, in 2012. I think it's uncontroversial to say that no other studio has had a higher batting average, and they've done it the right way: very pro-auteur, very fiscally disciplined, focusing more on an overall portfolio brand and strong relationships than the need for Yet Another Tentpole Franchise. A24 didn't succeed despite the streaming era — they succeeded because of it. The explosion of mediocre content created a vacuum for taste, for curation, for a brand that stood for something. When everything is abundant and most of it is forgettable, the scarce thing is discernment . The interesting question isn't "will there be more software?" — it's who captures the value, and what excellence looks like in a world of abundance. (Kicker: A24 just took a round of additional funding from Thrive Capital last year. The market, it seems, agrees.) There will be more software, not less, in the future. The quality of that software — as defined by the heuristics of yesteryear — will be lower.

1 views
Justin Duke 3 months ago

One status field per model

Any sufficiently old application starts to succumb to a pernicious form of technical debt known in street parlance as shitty data modeling . Sometimes this manifests as the god object: a single model that represents the user, and the settings, and the DNS configuration, and twelve other things. Sometimes this comes in the form of a table (or multiple tables) where the initial set of data modeling concerns in the early goings of the project don't quite match the reality discovered along the way, and a series of subtle mismatches collide with each other in the same way that subtle mismatches between tectonic plates do. Data models, unlike other areas of tech debt, are correctly scary to refactor. Even in Django — an application framework with really robust, mature migration tooling — reshaping data in production is non-trivial. The weight associated with even relatively simple schema changes can be so overwhelming as to forever dissuade a would-be re-architect from making things right. Therefore, it is that much more important to spend the extra mental energy early on to make sure, whenever possible, your data model is a roughly correct one, and to course correct early when it isn't. There are many ways to do this, and the goal of describing a virtuous data model in its entirety is too large and broad a problem for this measly little essay. Instead, I want to share a heuristic that I have found particularly useful — one which is summed up, as many of my blog posts are, in the title. Every data model must have at most one status field. If you're thinking about making a change such that a model has more than one status field, you have the wrong data model. Let me illustrate via self-flagellation and talk about Buttondown's own problematic model: the object. The object has three status fields within its lush, expansive confines: This is incorrect. We should have created standalone models for the sending domain and hosting domain, each with a simple field of its own, and drawn foreign keys from the onto those. We did not do this, because at the time it felt like overkill. And so. You pay the price — not in any one specific bug, but in weirdness , in the difficulty of reasoning about the code. Is there a meaningful difference between an status and a of for an active newsletter, versus an status and a of ? What queries should return which combinations? The confusion compounds. Again, I know this sounds trivial. But every good data model has syntactic sugar around the state machine, and every good state machine has a unary representation of its state. 1 See also: enums . A field (the normal one)

0 views
Justin Duke 3 months ago

RIP XSLT

If you visit in your browser, you will see that I have done the Cool Kid thing and added styling to it via XSLT. This was originally something I've wanted to do for years — both for my own blog and for Buttondown — and I finally got around to it, only to immediately learn that XSLT support is being deprecated . I can't even be particularly upset about the decision. It makes sense and is entirely rational — the security story around libxslt is grim, and if only 0.02% of page loads use XSLT, the cost of maintaining it outweighs the benefit. And yet. It is nonetheless a bummer. So enjoy those beautiful RSS feeds until November 17, 2026, when they go away forever. Or perhaps I will go with the polyfill option suggested by Chrome to hold on to them for a little bit longer — until that too is inevitably deprecated.

0 views
Justin Duke 3 months ago

January, 2026

This is not, if I'm being honest, the simple, structured start to 2026 that I had in mind. Rigor and early workouts have been replaced by pulled floors and sheets of ice. After spending a lovely week in Park City with the Third South folks, we came back home and had 12 hours of respite until, board by board, our floors were pulled up for replacement. The good news — it's always important to focus on the good news — is that the damage was less extensive than we expected. The bad news, because there is always bad news to go along with good news, is that this week we learned that we would be hit by an ice storm. And so, we decamped at my parents' house, the same one that I spent my formative years reading Redwall and playing Final Fantasy even though my parents thought I was asleep. Haley and I are, to a fault, creatures of habit and routine, and it would be a lie to say that the past two weeks haven't been draining in the same way a day spent in transit is draining. We miss our house. We miss our things. For Lucy, though, this has been a permanent vacation — a whirlwind of delight that started in Utah and has extended without ceasing. In the span of two weeks, she went from walking, if she remembered about it, to quite literally sprinting through the house, chasing anything and everything she wanted. It is fascinating to watch a toddler learn about the world. There is a transparency to them, and her effortless and endless delight in discovering the cause and effect of things that I have cynically grown to consider mundane — such as a light switch — more than makes up for a little bit of inclement weather. I haven't been working much. I haven't been writing much. I haven't been reading much. I have been watching my daughter discover the world and run headlong into it, hands outstretched. | Post | Genre | | ------------------------------------------------------------------------------------------------ | ---------- | | Tabula Rasa (Vol. 1) | Book | | Levels of the Game | Book | | A Shadow Intelligence | Book | | Cameraperson | Film | | Eternity | Film | | Go | Film | | Tinker Tailor Soldier Spy (2011) | Film | | The Pigeon Tunnel | Film | | Ocean's Twelve | Film | | Uses (January 2026) | Personal | | Terragon, Conductor, PyCharm | Technology | | Migrating to PlanetScale | Technology | | Refactoring a product is tricky | Technology | | Every model should have a notes field | Technology | | Pure strategy | Technology | | The Diplomat (Season 2) | Television |

0 views