Thank you, but I prefer it my way
The LLMs have changed how software gets made, and will keep changing it - for code, for design, for any digital product. Buried under all the productivity talk there is one thing they enable which does not get discussed much: never having to collaborate with another human being, ever again. The library whose maintainer won’t answer, the framework whose author finds your use case silly, the colleague who wants it done just so - you can now route around all of them. You can rewrite the thing in an afternoon, and nobody gets to tell you “no”.
We may soon come to regret this new freedom. Not because the rewrites are bad (they are often fine), but because the thing that forced us to work together is gone - and both sides of the open source bargain have spent years making sure we wouldn’t miss it.
🧭 I am currently available for contract work. Hire me to help make your Rails app better!

Baptiste Senior, in case you have not seen the film, is a petty dictator. That is the role we, software authors, are now auditioning for. Every one of us has a list: the maintainer who closed our PR with a smiley, the architecture guild that needed six weeks to approve a column rename, the designer who wanted the dialog just so, the author of that one library whose square was never a square. For twenty years we had to swallow it, because we needed those people to move the mountain. Now we don’t, and the first thing we are doing with our new power is not building something better - it is getting even. Routing around every one of those horrible other people, one afternoon at a time, and calling it productivity.
Let me introduce a term I will be using a lot: a square is a square. It is something that - if we are letting go of our biases - is evidently, indisputably correct. A programming language should parse 10.05 the same way on every machine. A source map should be served in a way the browser can find it. Squares. Not every disagreement is one - plenty of my squares are rectangles to somebody else, and I will try to flag those - but the clear cases exist, and the entire social cost of shared software is the cost of convincing somebody of them. The LLMs have just made paying that cost optional.
In my recent article I mentioned stationery and sghtmltopdf, two fresh PDF libraries. In the Hacker News thread a commenter pointed out that HexaPDF is also great, and added:
Personally I already find hexapdf to be better than prawn. And you can also try to convince the hexapdf author to improve hexapdf; he was willing to do so when he has time and the suggestion made sense.
This is good advice, and Thomas Leitner is, by every account, a cooperative maintainer. And still, almost nobody is going to email him - because the prompt is right there, and the email requires making a case. Even when the maintainer is willing, we won’t bother anymore. Hold on to that, we will come back to it.
These terrible, terrible other people
Up until very recently software got created in groups. You would not go and write the backend, the web frontend, two native mobile apps and a graphics core all by yourself - and you would not sit through the user interviews and move the rectangles in Figma on top of that. Even in the purely “computering” domain you needed others: a UNIX guy/gal to set up your server and get mail working, a frontend wizard so that your site does not crash Internet Explorer on Windows 2000, that fellow who figured out how to parse a near-complete SQL dialect, because understanding his problem yourself would have taken months.
Bug reporting and sociopath developers describes the arrangement well: you were forced to collaborate, because the mountain was too big to move on your own. Working inside an org stacked a second layer on top of the first - designers, product managers, architecture guilds, platform teams, bastion hosts and Terraform (lots of Terraform) - a whole cohort of well-meaning people who absolutely had to be consulted about every little thing you do.
Now imagine you can move the mountain on your own, without consulting anyone and without having to explain, one more time, that a square is a square. Irresistible.
I have been on the other side of this too. The biggest ask I ever received about tracksperanto was to have it in Python - Ruby was Windows-unfriendly, rarely vetted into studio Linux distributions, and just felt weird to the people who needed tracksperanto the most. Back then I didn’t want to do it, and also couldn’t, because the task was too big. Thanks you, but I prefer it my way - and the mountain was not only an excuse, it was an objective limitation. Today? If folks ask, I will vibe-port in a heartbeat. It would take an agent a fortnight, mostly unattended.
Which is also how the square gets refused, when there is no mountain in the way. The first time I ran into it properly, the software in question had a homegrown scripting language whose tokenizer leaned on the C standard library for parsing numbers - and strtod() respects the user’s locale. On my Dutch colleagues’ machines, a 10.05 in a script would not parse, and the interpreter bailed out with “Invalid value”. I emailed the author with the problem, the workaround and the fix. The answer, paraphrased, was that he was right and I was wrong. The full story is in the aside, for those who enjoy a bit of C.
The curious case of sscanf()
More than a decade ago, in my previous profession, I worked at a post-production company and was an ardent proponent of one particular piece of software. It handled a uniquely specific and crucial part of our workflow, did it faster and better than the competition, cost a fraction of their price - and was written by one single, brilliant engineer with a doctorate in robotics. It had a homegrown scripting language, and since I was already automating things for work I used it heavily: export modules, data translation, that sort of thing.
When we added a second seat, a Linux one, my scripts failed on it with “Invalid value” and no line number. The culprit took a while to find. My colleagues were Dutch, and some of them had export LC_ALL=nl_NL.UTF-8 in their shell profiles so that dates and numbers would look sensible to them - and in a Dutch locale the decimal separator is a comma. The C standard library functions used for parsing numbers - sscanf(), atof(), strtod() - respect that setting, and the tokenizer of the scripting language was built on them. Under a Dutch locale a perfectly ordinary 10.05 literal would not parse. The grammar of the language changed depending on who was running it.
It is an easy trap, and plenty of seasoned C programmers have fallen into it. So, being a fan of both the software and its author, I wrote him an email: here is the problem, here is the workaround we applied (force the locale to US English for that one program), here is how to fix it properly.
The response was terse, and the gist of it - my paraphrase - was: I am right, you are wrong, and what I am doing is perfectly fine. Perhaps he felt the user’s locale ought to be respected everywhere, and there is a version of that argument I could follow, if it weren’t for the literals in the scripts. It did not matter. I had my workaround, and he had his position.
Most of what makes feedback succeed sits with the recipient, not with the giver. A clear-cut “here is a problem, here is the fix” can still yield nothing but “go away, everything is fine”.
Thanks you, but I prefer it my way.
Omakase, but Naru is gone
The sscanf story is the crude version: one author, one “no”. The subtle version is more common and more damaging, and the best specimen I know is Sprockets. In March 2012 somebody asked the Rails asset pipeline for source map support. The maintainers agreed on the spot - “Absolutely”, “Started a branch for this” - and then the feature took seven and a half years to ship, until October 2019. In between, a working pull request got closed in 2014 because the processor API had to be redesigned first, and Josh Peek, the diligent maintainer who wanted that redesign done properly and by himself, made his last commit in June 2015 and left Richard Schneeman to pick up the pieces alone. Nobody was rude. Every single decision was defensible. The sum of them was a feature the whole Rails world wanted arriving after everybody had moved on. The dates and the receipts are in the aside.
Sprocket holes and broken promises
Sprockets started in 2008 as a small JavaScript concatenator by Sam Stephenson at 37signals. In 2011 it became the engine behind the Rails asset pipeline and its stewardship passed to Josh Peek, who did the 2.0 rewrite for Rails 3.1 and the 3.0 rewrite after it, with a great deal of careful design thinking in both. This is not a story about a lazy maintainer. It is a story about a very diligent one.
In March 2012 someone opened an issue asking for source map support. Source maps were brand new, and anyone writing CoffeeScript or SCSS could see they were going to change debugging in the browser completely. Sam replied “Absolutely.” Josh replied “Started a branch for this.” Then the issue went quiet. People kept dropping by - “whats the progress on this?” (2014), “Anything I can do to help?” (also 2014), “can we safely use the 4.0 branch if we’re really desperate?” (2015) - and none of them got an answer. In early 2014 a contributor brought an updated pull request with a working implementation. It was blocked on the processor API, which had to be redone for 3.0 first, and compilation should be a “pure functional” transformation with no temporary files on disk. A perfectly valid position. The pull request was closed a few months later, unmerged.
In September 2014 came the roadmap: 3.0 would be transitional, and 4.0 - “the big drop” - would have full source map support, “in the near future”. 3.0 shipped in April 2015. In June 2015 Josh made his last commit to Sprockets. Richard Schneeman picked up the pieces, largely alone and largely on his own time, and gave a talk with the telling title Saving Sprockets. Sprockets 4.0, with the source maps, was released in October 2019.
Seven and a half years after “Absolutely.”
I was one of the people who needed those maps badly, and in 2013 I gave up waiting and wrote Vitrine, a little development server, just to have them. When the 4.x branch got merged in 2015 I filed an issue because the maps got served but the browser was never told they existed; the answer was that this only worked in the debug pipeline, and “who knows.. maybe in the future there will be a more convenient way to do this..”. We are in the future. It never happened. By 2016 the mood had soured enough for Giles Bowkett to write Let The Asset Pipeline Die, which was unfair to Richard, who was doing the heavy lifting at that very moment - but hard to argue with from the receiving end. I wrote my own response at the time, too.
Note that nobody in this story was rude, and nobody said “go away”. What happened is subtler, and I think much more common. A maintainer had a clear picture of how the thing should be done properly - a new processor API, a pure functional pipeline, the beautiful stuff - and wanted it done that way, by himself, before anything else could land. Offers of help did not fit the picture, so they got a reasoned “not yet”, or silence, or got parked behind the redesign. Every individual decision was defensible. The sum of them was a feature the entire Rails world wanted taking seven and a half years, and a maintainer who burned through his own energy carrying it all - and then, understandably, left. It did not do Rails’ standing in the frontend world any favours either. Anyone who found their brownfield Rails.application.assets to be nil in production knows exactly what I am talking about.
Thanks you, but I prefer it my way.
Rails is omakase, and omakase lives by the chef’s aspirations. But omakase is not supposed to be huge, and Rails - after more than twenty years on the scene, with all its modules, libraries, dependencies and test cases - is frigging huge. That is not a bad thing; software that lives long and evolves grows functionality and guardrails, that’s just evolution. The situation gets dire, however, for the dishes whose commis has left the kitchen. Imagine this:
— But this nigiri is with a fish that is on the endangered species list, and the recipe calls for oil which has been found highly carcinogenic. Also, the consumption requires obtaining a beaker made from true ivory. Why do I have to consume it this way?
— This is what Naru has designed for this omakase. Naru left the kitchen a few years ago, nobody else knows the recipe, and the chef doesn’t have time to redo that dish.
— Ok, but can I get something that is at least not poisonous?
— Sorry, not at this time.
— I know how to cook this, and I know it well. May I recommend a good replacement at least for carcinogenic oils?
— Sorry, not at this time.
— Ok, can I at least propose an alternative for the ivory beaker? Because I have used this menu in my establishment, and a few patrons have left threatening to report me to the WWF!
— Sorry, not at this time. You will use the beaker.
— Do you have any alternatives? I have some great imitation ivory here, fresh off my 3D printer.
— Alright, you may use your 3D printed plastic junk. But don’t ever hope we would be replacing our ivory beaker, nor would we ever be telling patrons that your imitation could be preferable.
37signals built the Rails culture on ruthless curation of the components, and the inverse of that paradigm is that when a steward leaves, the dish stays on the menu as it was - until, years later, the chef’s table replaces it wholesale.
It may still be a big tent, but one in an advanced state of disrepair.
Sprockets kept its holes until Propshaft took its place; none of the patrons’ proposals in between made it onto the menu. The same reflex shows up in smaller ways, too: when Rails needed resumable jobs, job-iteration had been in production for years and covered most of the need, and Rails got ActiveJob::Continuation, written from scratch, instead. You look at the issue tracker for importmap-rails and you can already see how the new season of “Rails asset pipelines” is playing out.
Up until very recently that was the chef’s whole aspiration. After this year’s Rails World keynote we know the next one: burn the ships. Rails was great at transferring knowledge between people, and between people and machines - it was a way to talk to a computer in a language that does not make one throw up. Once you can do that in English, the whole point evaporates, and the transfer between people stops being interesting.
There is a Danish children’s book, Palle alene i verden, which every Danish child seems to have read. Palle wakes up to find everyone gone. He helps himself in the sweet shop, drives a tram, takes all the money from the bank, flies an aeroplane - and it is lonely, then boring, then frightening, because there is nobody to share the sweets with and money is worthless when there is nobody to buy from. Then he wakes up for real, and is very relieved. We are all turning into little Palle, on our own time, with our (subsidized) tokens.
Nobody is owed my begging
Back to HexaPDF, and to the question of who is saying the line now. HexaPDF is a commercial product: its license (AGPL plus commercial) is meant to let Thomas make a living from software you legitimately want to pay for. Now that chewing through the horrid contraptions of the PDF format can be offloaded onto an untiring robobrain, one prompt away, are we withholding income from him by typing that prompt instead of emailing him? We are. It is us, the consumers, telling a willing maintainer “no, thanks, I prefer it my way” - and the small verticals where one author’s laser focus used to be the only way to get anything done at all are going to get decimated by exactly that sentence.
And here is the part we all shy away from admitting: I no longer have time to try to convince anyone. Josh agreed that Sprockets needed source maps on the day the issue was opened in 2012, and it still took seven and a half years, because the how mattered more than the what. I don’t have time to convince DHH that any filename extension can be a JS module - that issue has been open since 2022, four years and counting. (This one, I will grant, DHH would call a rectangle: conventions are allowed to be conventions. I still think it is a square.) Just as the authors don’t owe me anything, I don’t owe them years of diplomacy. When “that’s just, like, your opinion, man” is the answer, and I can end the conversation and solve the problem my way - I now will. My time is finite, I see the solution from A to Z, and if I see it wrong, nearly all decisions are reversible.
For Rails assets that is how I ended up with assiette - because enough. I filed the source map issue in 2015 and the module extension issue in 2022, I waited, I watched the asset pipeline saga on slow replay for three or four seasons, and then I wrote the thing I wanted: import {Foo} from "./foo.mjs" just works, and I never think about it again.
“Nobody owes you a reply” has an inverse: “you do not owe to beg.” Same for Mikael Henriksson, who got tired of waiting on importmap-rails and shipped importmap-plus - a drop-in redo of the whole thing.
The collateral is that the default is now making your own thing from scratch for the 3% of the use case you care about. We end up with dozens of solutions covering somewhat-overlapping, somewhat-disjoint slivers of the same problem, and we are so happy about not having to beg that we never check what we are destroying in the process. We are entering the bazaar of a thousand cathedrals, and most of them will stand empty.
I know this because I am one of the cathedrals. geneva_drive, my durable-workflow engine for Rails, came out at the end of 2025. It is built around a declared graph of steps persisted as records, and that is a position, not a shortcut: I think a workflow you can draw is a workflow you can operate. When I started, the shelf was nearly bare - acidic_job had been sitting in release candidates for a year, gush is a Redis job runner with no notion of waiting for a week, and Temporal is a cluster. So I built mine. Then everybody else did too. I keep a list of the alternatives, because I sell the thing and need to know what I am up against, and the list is sobering: a dozen durable-workflow engines for Rails have appeared within two years of one another - Vladimir Dementyev’s ajdc, chrono_forge, ductwork, stepped, durable_flow, a handful more - and about as many that were created and abandoned within a week, a couple of them visibly generated by an LLM in one sitting. Hardly anyone joined anyone else’s. Several of the differences are honest disagreements, not taste: chrono_forge’s README argues at length, and in good faith, against exactly the declarative model I chose, and the replay-based crowd thinks persisting a graph at all is the wrong idea. They may be right. But a dozen simultaneous, mutually incompatible answers to one question is not an ecosystem making up its mind. It is a dozen people who each found it cheaper to build than to ask. It is a fully connected graph of mutual FUs. In an already shrinking ecosystem.
I am not going to pretend my feelings about this are purely principled; of course I would like people to pick my thing over the other person’s thing, and of course I would like to lose fewer sales to the eleven others. But I respect the people behind them, I have borrowed ideas from several, and I cannot claim I did anything they did not. Rails now has twelve answers to one question and a user base that cannot sustain them. In two years most of these repos will look like the abandoned ones already do - quite possibly including mine.
And that is the real cost of a one-off thanks, but I prefer it my way - not that it exists, but that it gets dropped. Abdelkader Boudih put it precisely: “the mediocre leave projects the way kids leave toys: something newer came out”. His exhibit is a repository whose issue #1 has hundreds of comments and not one reply from anyone with commit access. That is where the rewrite you did in an afternoon ends up: a repository with one user, you, until you too find a newer toy. We have watched the Rails version of this for years - the component that gets its keynote slot, its logo and its ten minutes of applause at Rails World, and twelve months later is a repository nobody can remember the name of, let alone the point of. The LLMs made the toy nearly free to produce - and, in the same stroke, made it nearly hopeless for any one of us to ever get onto that stage with something that has a real user base. When everyone can build, nobody draws a crowd. The toy gets built faster, gets dropped on the floor faster, and the people who picked it up in the meantime are, in Abdelkader’s words, the Cyclops shouting a name into the thread while the neighbours walk away.
I would posit that both the authors and the consumers of software fucked this up. The consumers - by demanding, by feeling entitled to everything, to the point where Rich Hickey had to write Open Source is Not About You in 2018 to explain to his own users that a free language does not come with a claim on its author’s time. The authors - by stalling and deferring every meaningful contribution: “this 70% approach is not 100%, let me spend another year mulling over how to do it right, never mind that I have been burnt out for the past one” - and then never delivering. By bikeshedding if versus unless, by insisting things get done just like so. This explosion of rewrites is happening not only because it became mechanically possible, but because elitism - and neediness - have both stopped paying off.
The precious thing in software
For me, the main value of the software I make is seeing people rejoice when they use it. That it makes them smile, that it gets them home earlier, and - yes - that they use it and think “whoa, this is well done, this guy must be good”. The joy is in the using, and most of all in seeing others use it. Now that everyone can conjure software with a flick of the wand and it comes out “mostly decent” - which is perfectly fine for most people - the likelihood of your software lighting up somebody else’s day gets smaller every month. Making software with others is a great joy as well, but not when the point of debate is rspec versus minitest, or “we won’t make this useful today because it is not academically perfect yet”.
The hubris on one end of the bargain and the ignorance on the other have rebounded on us. Now the main actor of software - the user - is the one who is leaving, while we just keep arguing whether “using AI to write code is the original sin” or “using Ruby for your applications is ruining the planet”. Spoiler: if you are hell-bent on twisting all the arguments towards grievances of that order, a vibed Rust blob will be better for the planet than a carefully curated, hand-painted Ruby app.
Ruthless incrementalism, but inclusive
How do we turn the tide? By not being the stupid petty dictators, of course. The same machine that makes it cheap to route around a maintainer makes it cheap for the maintainer to say yes. A pull request that is 70% there used to cost you a weekend of cleanup, which is why it sat for a year; now it costs a prompt and a review. The forcing function is gone - but so is the excuse.
Previously a pull request could be stalled for a year over an insignificant detail, and the contributor would wait, because what else were they going to do?
They don’t wait anymore. Please do investigate is a cop-out now. You no longer have the luxury of having people spin their wheels in a desperate attempt to please your aesthetic judgement, having at it again and again. The only workable strategy for keeping software shared is lowering the barriers, and that requires an admission maintainers find harder than it sounds: I could have designed this wrong. Presume you can be wrong. Yes, maintainers are swamped with issues and PRs (such PRs even get called “drive-by PRs”, a term we should strike out). It all seems overwhelming, but there are tools, and you may use every one of them:
- You can tell people that issues are more welcome than PRs.
- You can art-direct others’ PRs. Not “hint and teach and endlessly chip at” - no. Someone brings you an idea, you see it is 70% there - send it through your agent to drive it to 100%, unapologetically.
- You can state openly what you want and what you do not want.
- You can use automatic triage when stuff gets too much.
- You can demand that requests be formulated tersely, in a language of your choice.
What you cannot be anymore is the wizard who only accepts input once it is 100% correct and adheres to every whim of yours, including the ones you never uttered out loud.
And that’s not all! For example, with herringbone I knew that hand-rolled structures could be exploitable, so I went to Greg Molnar to poke a few holes in it. He knows his stuff. Of course he found a few things to patch, and quickly. You can proactively invite people to collaborate.
The other end has homework too. When an itch comes up - before it turns into a wound - go and look whether something out there covers 70% of your use case. Does the author look like someone you can reason with, are the barriers low? Then don’t prefer it your way: file the issue, bring the 70%, let it get art-directed. Give it one issue, one PR and a couple of weeks. If that gets you silence - fork, say so publicly, link back, and do it without rancor. That is the difference between frustration and spite: frustration asks once, spite never asks again.
Spite only makes you more bitter. Yes, that includes rage-removing all of your software from the Internet because “AI is ruining the planet and the community has submitted itself to it”.
If we want to keep making software that anyone cares about besides the person who made it - don’t be the Thanks you, I prefer it my way guy/gal. Being Baptiste Senior is not much fun in an empty room.
And, lest you have hope: remember that the Soviets landed first. One of my earliest libraries for Rails was called active_search.
Recommended
- Reverse Engineer Anything Is the New Wild West Of AI - anything can be copied now, at near-zero cost, with zero respect but also with zero theater
- rain, Anthropic’s Scion is heavily inspired by Mononoke - instead of adapting something, Anthropic just went and did their own thing. Obviously.
- Jamie Zawinski, Groupware Bad - “how will this software get my users laid?”
- Rich Hickey, Open Source is Not About You
- CELPHASE, He who does not code, neither shall he eat - on the broken social contract between open source contributors and users
- Andy Croll, Look at all the things he is not doing
- Barton Friedland, The Devastating Mistake Employers Are Making When It Comes To AI - on confusing the absence of friction with the presence of thought
- Polly Mackenzie, In praise of friction
- Graham Lee, Fully Automated Luxury Communistical Software Factories
- Simon Willison, My answers to the questions I posed about porting open source code with LLMs
- Jared Norman, What About Rails? - on the Rails World 2026 keynote that had shockingly little to say about Rails
- Paul Battley, End of an era