I wrote a spreadsheet application.
There used to be a software consultancy based in NYC called Postlight that created a "tinysheet" web app as a sort of joke. This was for doing back-of-the-napkin math, the kinds of things where you have multiple stages and you don't remember how the memory buttons on your calculator work. Then Postlight's founders sold the company to a Japanese telecom company who left it to molder, and now most of its domains redirect to Bitcoin casinos. Tinysheet still exists, but it's not something I feel good about using, especially as the Postlight founders have subsequently gone into vibecoding-as-a-service with marketing via the New York Times opinion page.
In any case, while I was coming off the small project buzz from my Varvara implementation the week before, I figured: I can write something that hits the low bar of the original project fairly quickly. And then it turns out that this is the kind of project where it's easy to just keep adding new functions and UI features, which is how I ended up with a final result that was honestly far more powerful than it needed to be.
As with almost everything I've built over the last decade, it's implemented as a set of plain web components. In this case, I didn't even end up using a base class, and I leaned heavily on the handleEvent() pattern for UI interactions and triggering state updates between the cells and the sheet controller. The most frustrating part of the front-end was the table layout itself: using a literal <table> had reflow and positioning issues, but subgrid layout has a few gotchas still, and I ended up needing to set the grid template styles from JavaScript, which is unfortunate.
The formulas themselves are not structured like Excel or Google Sheets, because I find those painful to type on a phone. Instead, having just built a stack VM, I decided to use a similar approach for writing the interpreter. It's an RPN- or Forth-like system where most operands consume everything on the stack, cell references are immediately evaluated to their value, and a naive cycle-tracking system makes sure that you can't create any infinite loops. There's probably a lot that could be done to improve performance (such as value caching), but in a sheet of 40 or fewer cells, there's some value in prioritizing reliability over speed.
Like a classic spreadsheet, the stack interpreter (internally known as the "Different Engine") is extremely liberal about value types, although by design it doesn't try to translate dates. Any unrecognized values are typed as strings, which means that you can do basic templating and text formatting very easily: 3.1415 $0 text produces $3.14, for example. Using the parentheses operators to "stash" the stack makes it easy to bypass the default greedy behavior for most operators: Grand total: ( B:B + ) . pushes "Grand" and "total:" onto a background stack, sums the numbers in column B, then restores the stack and prints the combined text.
(Over the last decade, I've gotten pretty weary — and wary — of mobile application design. The reduction of all input to a touchscreen, and the limits of palm-sized UI real estate, have shrunken the possibility space for tools and creative expression to match. Working on this formula syntax, where there's almost no punctuation or mode switching required, is the first time in a while where I've actually felt like a phone could be an interesting place for code. I suspect further experiments are in my future.)
Finally, just to satisfy my muscle memory I added some minimal spreadsheet affordances: you can click the drag handle on a cell (or hit Ctrl-D) to fill a formula down, with cell references being updated automatically (unless you flag them as absolute references). You can also select rows or columns and delete all values in them. Handily, I didn't have to add row-wise tab behavior because the browser gives that to inputs for free, but I did add code so that return moves to the next cell vertically based on where you started typing, just as you'd expect. I'm missing arrow key support, mostly because I didn't distinguish between "selected" and "editing" states, but on a touch screen you don't miss them much.
If I were still teaching web development, this would be an ideal class assignment for intermediate students over a series of weeks. The basics of cell editing and calculation are straightforward, but from there it is easy to extend outward through a series of more advanced (but still bite-sized) tasks: writing a real parser, dereferencing cell IDs, tracking dependencies, and allowing for more complex interactions and styling.
As it is, Cell Culture was a perfectly-sized project to scratch the itch for self-expression. It's not challenging Google Sheets — not yet, at least — but it fits directly into the niche of personal tools that I love so much. And you never know! We often assume, both personally and professionally, that our needs are very large. That's why small businesses find themselves using enterprise management systems, why web developers are caught up in the same frameworks that Facebook pushes, and what drives the personal organization industry. But maybe for many problems, our needs are actually modest. Maybe a tiny sheet is all I need most of the time.
During the past week, as Madrid suffers through a historic heat wave, I've been working on a little side project: a web component that can be used to load and run software for the Varvara fantasy computer. At this point it's not complete — I'd like to add a windowing system and a way to run console-only applications, making the page close to a mini-OS — but it has crossed the line into being able to load and run pretty much any arbitrary ROM that I can find, which is an extremely cool feeling.
Varvara is a loose collection of "hardware" wrapping the Uxn virtual CPU, an 8-bit stack machine that is usually programmed in a mix of Forth and assembly called Uxntal. It's basically an art project by a duo called Hundred Rabbits. In their Uxn developer log, Hundred Rabbits says that Uxn should take about a week for a single person to rebuild on their own. My experience says that's about accurate for a first-time attempt.
In many places, Varvara's design feels like it's explicitly walking a line between "making tasks challenging in a fun way" and "eliminating the drudge work of actual low-level hardware." For example, it's an 8-bit machine in an age where it's almost impossible to find an 8-bit CPU (and the ones you could find would never be able to run a Uxn VM). So code tends to spend a lot of time juggling values around the stack, but then the places where that would get tedious (like sprite display) actually run at a relatively high level. Similarly, it's a big-endian architecture (meaning that multi-byte values are stored the same order as human numbers), which feels significantly less annoying to me.
Which is not to say that I found the entire process to be smooth sailing. The Uxn documentation is both weirdly over- and under-specified. For example, it will note that the CPU operates only on unsigned integers, which is true, except when it doesn't (relative jump instructions use signed integers for the destination, screen coordinates are signed 16-bit values). The sprite auto-tiling algorithm is poorly explained for something relatively simple, and the only place you'll find the actual correlation of the color nybble to the display palette is by visiting a tutorial that a random hobbyist put together. On the other hand, the official bytecode definition looks like BNF designed for interpretation by aliens who found the Voyager record, and the human-readable docs are beautifully illustrated and designed.
This contrast between missing detail and elaborate decoration is, in some ways, deeply annoying. It's also something that makes the implementation process a fun puzzle, an archeological challenge. I spent a lot of time while working through the test ROM byte by byte in another emulator, checking the stack values and comparing them to my own logging traces, until they matched.
The resulting Varvara system is uniquely mine. My audio sounds like the reference implementation to my ears, but I'm sending bytes through a WebAudio detune that I just kind of adjusted by hand. My screen rendering logic gets to the same place but is doing its own thing in places. I have a nice mapped memory implementation that's very JavaScript-ish. Everything passes tests and runs standard ROMs, but I can feel ownership of the system, which feels very true to my experience of 90s-era computing, when your particular machine was often a unique combination of components with its own personal set of hacks in CONFIG.SYS and AUTOEXEC.BAT.
At the end of the day, none of this is likely to become a daily driver for me. I don't really have any desire to write Uxntal myself, and the code — while clean and reasonably optimized — is not particularly unique or valuable. But I learned a few things, and brushed up on some skills I hadn't used in a while. And for all the times that it was frustrating to dig through and find a weird stack bug or operator precedence issue, it was also thrilling to solve those problems and see the program counter advance just a little further. At a time when a lot of people keep implying that exactly this kind of work should be left to language models, it's good to be reminded that the craft and process is rewarding in and of itself. The thought of just asking a chatbot "write a Varvara emulator" rings deeply hollow to me.
Designing this has also made me think more about what I want out of my tools. Hundred Rabbits designed Uxn and Varvara to address the particular projects that they work on, the values they hold, and (I suspect) the feel of the digital medium that they want to use during creation. That means some things fall by the wayside, like visual accessibility or networking — and that's fine, it's a personal platform for people who don't need those things. I don't yet know what the bedrock of my own personal platform would be (even assuming that there's just one), but I now have a better idea of the value in the question.
When I first starting taking dance classes, I remember asking my teachers if they had any tips on layering movement over different parts of the body without losing the beat or falling over. As someone who had musical experience, but not a lot of physical coordination, I figured there had to be a trick to it. They often gave me good advice, but nothing that would solve my problem instantly.
Eventually, and this was the most important lesson that I learned from my ten or so years dancing, I realized that they couldn't give me a simple trick to movement, because there are no simple tricks for complex skills. There's no silver bullet, no shortcut. All skills worth learning are like this. You improve at dancing by doing a lot of it, slowly, and with discipline, until you don't have to think about the mechanics. Becoming better at playing an instrument is going to mean spending a lot of time running scales or drills. Being a better coder requires you to write a lot of boring, repetitive code. Malcolm Gladwell is a con artist and a lazy thinker, but he was right about the 10,000 hour thing.
As a former teacher, this is why one of the most frustrating assertions around LLM coding is that it will "democratize" things by making it easier for people to program without worrying about pesky problems like "syntax" or "actual code." This is a bait and switch, like telling someone that buying a baguette makes them a baker. Swap the disciplines to realize how silly it sounds: we don't call someone with a Suno subscription a "musician," and when someone pastes from ChatGPT into the Kindle store, most people would not consider them an "author." If you "haven't written a line of code in a year," as Spotify's CEO said of many staffers, you're not a developer. At best, you're a manager, and at worst, a client. These are skills, maybe, just not the skill of coding.
Still, as people have forever pointed out, there are genuine speedbumps that make it harder to get started as a developer, and that's part of the reason that the LinkedIn crowd is so excited to finally be able to make their own applications. Education isn't to blame for the plague of LLM boosters, but the rise of "democratization" rhetoric is a good opportunity for us to think about what is holding students and junior team members back, and how to create more welcoming environments.
I can't really speak for the native side of things, which has its own elaborate set of problems. But on the web, there's plenty of speedbumps that make learning difficult, falling into three categories: platform cruft, visual layout tools, and a lack of locality in structure.
The cruft is straightforward and not actually hard, but it's a constant drag on teaching. I spent a lot of time in the classroom saying "don't worry about it" for various snippets that students needed to remember in order for pages to work correctly: the HTML doctype, the meta viewport tag, a usable box-sizing model. Some of these exist for good reasons, and some of them are junk that Safari forced on us, but they're all annoying. I don't even remember them most of the time — they're part of a standard set of includes that I basically bring to every new project.
For doing visual layout, tools do exist on the web, but primarily for pages, not apps. Lots of people build a site (granted, starting from a relatively complete template) in services like Squarespace. But while native toolkits at least pretend to still support visual form builders, there's really nothing similar for JavaScript. Even before we get to responsive design and styling (huge problems in their own right), just laying out a page is harder than I'd like it to be for students who just want to get started.
And this goes hand in hand with my final papercut, which is locality of code. In a system like Visual Basic, back when I first started coding, you would drag-and-drop controls into your window, but crucially you could then click on them and directly start adding code for common interactions. Style and behavior were directly and visually tied to the application's UI. The modern web not only doesn't have this kind of tooling, but it's also distinctly hard to build. For example, I can't ergonomically write a web component that's customized by inline scripts, because there's no way to export code from an inline module. I can hack something together with custom type attributes and function constructors, sure, but that sets up students for another fiction they'll have to unlearn later — another speedbump in their way.
It's tempting to drift comfortably in nostalgia for VB6 and Hypercard, but it's worth remembering that those toolkits aged poorly in a lot of ways that are difficult — or impossible — to solve. They worked best when everyone had roughly similar-sized screens and a standard interaction model. They had serious limitations that people had to hack around. They tended to produce spaghetti code in "natural" languages that didn't translate well to other runtimes. And of course, while we remember success stories like Myst, we tend to forget about how much those programs had to hack around the limitations of their environment.
I do think we can create better templates, and maybe some tooling. Low-hanging fruit include CSS-first components like "responsive flex row/column" or "simple grid." I'd like to see thought given to framework architectures that are specifically geared toward beginners: easy access to global/shared state, exposed and transparent local state, strong accessibility guarantees. But the more I have thought about this problem, the more I'm uneasy with the idea of coding our way out of what is, in the end, a pedagogical problem.
And what if we don't have to? When I think about two of the problems we're trying to address here — visual editing and locality of control with UI — this actually exists: it's the dev tools, combined with well-designed web components. It may need some additions (Chrome can't add an element directly without using "Edit HTML"), but it's shockingly powerful and puts style, markup, and code right next to each other. When working on Tarot and the synthesizer components, I never ended up building any editing tools, because everything I needed was available in the browser.
But I have almost never seen a JavaScript tutorial or class that started from — and focused on — the dev tools as a primary interaction, the way that Racket prioritizes DrRacket, or R tends to lead with RStudio, or Python instructors can lean on Jupyter. These environments are not uniformly helpful — Jupyter, especially, has a habit of training new Python programmers to think only in terms of notebook cells and not other control structures — but they provide interactivity and guidance early on in skill development.
If I were putting together a new version of my old JavaScript textbook for today, I wouldn't start with the console and writing entire scripts. Instead, I would hand students a bare-bones HTML file with the cruft already boilerplated in, and then we would start altering that page, using the dev tools to prototype and explore. Crucially, this would help students think about the page as a coherent whole, instead of thinking about CSS, HTML, and JavaScript as separate (and oppositional) technology. As we moved into scripting, we'd set breakpoints, define watch statements, and inspect elements both in markup and in the DOM.
Eventually, students using this approach would graduate to separate files, loaded via ES modules and external stylesheets. They'd learn about the accessibility tree, one panel over from their styles. They'd use the network tab to inspect requests, and understand how async code works. We'd open up other people's websites and see what they're putting in local storage, or snoop on their code, or find performance issues, or write little scrapers. Most importantly, they would be learning to think about the web as a coherent whole, not three separate technologies glued together or papered over by a framework.
At the end of the day, the LLM bet for new students is "you don't need to know the code," which is an astonishingly arrogant thing to propose for the purpose of training new developers. It's bad for students, bad for the junior developer pipeline, bad for the users stuck with decaying software, and bad for pretty much everyone who isn't a large language model vendor.
"Letting the machine do the drudgery" is bad for skill maintenance for senior developers, as we can see from the constant cognitive decline of the people who use them. But it's fatal for people just starting out. You might as well try to dance just by learning the magic words. From experience, I can tell you that you just end up looking silly.
The bet for "teach primarily from the dev tools" is the opposite. It is invested in the idea that you should know code, that you should be interacting with it thoughtfully, and that tooling can be powerful and deterministic and standardized. Indeed, from this we can derive one of the fundamental critiques of LLMs, which is that most of the uses people keep raving about — transpilation, refactoring, rapid iteration — already existed, they were simply ignored by a programming community that preferred to use techniques that were old in the age of punch cards.
And of course, teaching students to debug will be valuable when they enter a job market decimated by vibe-coded disasters and "shadow IT" coded by execs high on chatbot services. Like it or not, a lot of us are going to be on cleanup duty until this bubble pops — might as well get the juniors better prepared.
"Resonant computing" has strong Ezra Klein energy.
"Resonant Computing" / "Abundance" => "I don't really understand what this is about, but I'm pretty sure it hates trans people" — Andi McClure
I've been trying for a month or two to figure out why the "Resonant Computing Manifesto" bugs me so much, until McClure's post broke through the cloud cover. If you are a normal person who is not deeply saturated in Online Political Reporting Discourse, it probably doesn't clarify anything for you, but (un)luckily, I can provide that missing context.
The "abundance agenda" is a pitch by liberal political pundits Ezra Klein and Derek Thompson that (they claim) will reinvigorate the US left by focusing the party on infrastructure and housing — specifically by slashing environmental, safety, and economic regulation — and leaning heavily into free-market ideology. It's hard to separate this from Klein's columnist persona, polished over twenty years of being the kind of center-left wonk who thinks the party is making too big a deal out of the whole "abortion rights" or "union support" planks in its platform.
In fact, the problem with "abundance" in general is less its message and more the company that it keeps, which keeps raising uncomfortable questions about which specific regulations and government interventions should be paved under for the sake of progress: fans include people like sweatshop enthusiast Matt Yglesias, centrist political writer Josh Barro, or "no, it's the kids who are wrong" pundit Jonathan Haidt. It's technocratic populism, which in a lot of these cases is code for "we will only support constituencies that poll well," thus managing to be both uninspiring and (in a shocking number of cases) weirdly transphobic.
On the other hand, I cannot stress enough how much whiplash you will suffer while reading through the "Resonant Computing Manifesto" web site. It opens by talking about the problems of modern software, which is badly designed, exploitative, and alienating. All of this is true! And then it immediately pivots into madness (emphasis in the original):
This is where AI provides a missing puzzle piece. Software can now respond fluidly to the context and particularity of each human—at scale. One-size-fits-all is no longer a technological or economic necessity. Where once our digital environments inevitably shaped us against our will, we can now build technology that adaptively shapes itself in service of our individual and collective aspirations. We can build resonant environments that bring out the best in every human who inhabits them.
(record scratch) Sorry, what? The problem of modern software is that it's too centralized and isolating, and so the solution is that we should instead outsource all our computing to one or two tech companies to build individualized, ad-hoc, non-standard solutions? Also, who are these people in 2026 who are screaming for their software to change more often instead of simply praying that the next update to their operating system or productivity software doesn't break everything again?
AI as automation is facially absurd, of course, and it becomes more so when (as with abundance ideology) you start to investigate the contributors for the manifesto, which includes a bunch of professional AI boosters, venture capitalists linked to the the arms industry, and GitHub employees working on the Copilot team. I think we should be inherently suspicious whenever these people get together, especially when they start shopping around inspirational content with folksy illustration work. We certainly should be skeptical when they start saying "we" and "us" in a paragraph about the motivations of the people who built the panopticon.
"Regardless of which path we choose, the future of computing will be hyper-personalized," say the resonant computing advocates, without providing a shred of evidence (or admitting the possibility that it... won't be). But even if we take them at their word: personalized for who? We know that generative text systems encode and recreate bias, and we know that they have "alignment" directives that exclude certain content. To be fair, this is often for good reason, as the Grok CSAM generation debacle reminds us, but we should also keep in mind that all these companies are currently cozying up to same federal government waging war on gender, germ theory, and anti-racism. "Hyper-personalized" only works if the company running the service thinks you're a person.
(Not to mention the most obvious concern with "adaptively shaped" software, of course, which is that we've seen what chatbots can do when they reinforce user desires, ranging from legal malpractice to so-called psychosis. Are we sure we want to swaddle ourselves in this stuff completely?)
Absent from both the abundance and the resonant computing pitches is the idea of community resilience. The former is not explicitly anti-union, but it has a lot of suspiciously anti-union supporters. The latter believes that more humane software will be achieved by largely removing the humans from it and replacing them with stolen labor. But you cannot solve problems with isolation by introducing more ways to avoid human interaction.
I haven't actually read Abundance and don't intend to, so it may be that I'm being unfair or overly broad. That said, my experience with software is very different from what the resonant computing advocates describe. Over the last ten years of my career, I've often developed custom bits of software for the people around me: I built an audio sequencer and a book locator quiz for my father, who worked as an elementary school librarian, and little data processing scripts in Python/Tk for my wife to help at her non-profit job. I worked on the web site for a dance company that I joined in DC. Nobody paid me for any of this, and I don't think I'm particularly unusual in that.
Now, you can argue that the point of the manifesto is that lots of people could have a virtual programmer friend at their fingertips. I would say if that's the end goal, you don't need to tell people to pay a monthly fee to a billion-dollar tech company. You could build ways to connect developers with volunteer opportunities, or write up how to easily set up a mutual aid network within a neighborhood or profession. You could prototype visual scripting systems that integrate into existing tools. There's plenty of options. But maybe that doesn't fit easily into the worldview of people who are late-stage investors in (checks notes) racist anti-immigration drone company Anduril.
If you are not a ghoul, it's not hard to imagine tech as part of the low-key mutual aid that happens quietly, every day, in cities and towns around the world. Librarians provide free tech support for patrons. Community gardens loan out tools and provide green spaces. Local social media groups provide both comic relief and "buy-nothing" trade. It turns out that if you give people a chance, they will stick together and help each other out, using appropriately simple tools. We don't have to look far for the evidence of that.
As I write this, the Trump administration is currently regrouping after the murders of Alex Pretti and Renee Good threw a wrench into its ongoing plans to invade American cities with military force and terrorize communities (immigrant and otherwise) there. They'll be back, of course. But while there were certainly pitches for technological solutions or custom anti-ICE apps, at the end of the day what has actually worked to protect communities is simple, grassroots mutual aid, what Ryan Broderick describes as "pragmatic protest": 3-D printed whistles, watch groups over encrypted channels, and organized monitoring/publicity of ICE vehicles.
It may seem unfair to ask for any ideology to prove itself under those kinds of conditions. On the other hand, it's a tremendous heuristic. What good is a political philosophy that tells us to give ground on immigrants or women in order to win power, when we see what those in power will do to the people we abandoned? What good would it do to have infinitely customized software when coordinating between untrained activists? Why insist that we need "adaptively shaped" tools to strengthen community, when the obvious truth is that the community strengthens itself using simple — but powerful — tools?
So yes, if you're catching a whiff of the same neo-neoliberal promises from the authors of this manifesto that you get from center-left pundits like Klein and Thompson, you're not imagining it. It's not an actual plan, it's a way for them to justify the selfish actions they're already taking (and the sins they previously committed). They want you to ignore their backgrounds, the slop and slop-ware that actually constitutes the "tooling and infrastructure" they're building, the externalities of the process, and the ethics and constituencies that they're happy to jettison, and instead focus on the hazy, idealized future that they've sketched out.
You know what else resonates? An echo chamber. No, between the AI weirdos, the long-termists, and the effective altruists, I've lost patience with visions of a shining city on a hill. Let's build for resilience and stability against the overwhelming challenges we face today, instead of trusting a gamble proposed by the people who already failed us.
Every time I open up a new spreadsheet in Google Sheets, a task that I perform for my job two or three times a day, it shoves a toolbar into the right side of the screen that cannot be disabled with a large blue button. "Help me create a table," the button says, and underneath it lists some of the incredible "AI enhanced" tables that I can insert, such as:
This persists even after I've pasted data into the workbook, except then it will apparently replace my actual numbers. So I get to close this panel every single time. It is deeply infuriating.
In the blank tab, meanwhile, a prompt lets me know that I can "type =AI to insert a Gemini prompt into any cell." I've literally never done this, but the docs for the function let me know that I can use this to search the web for information, generate a slogan, or categorize addresses. They also warn me that I shouldn't rely on these features for professional advice, and that they may be inaccurate, which strikes me as a fundamental misunderstanding of the two most important goals for a spreadsheet (i.e., that it is a professional tool for achieving accurate results).
A lot of ink has been spilled on how wasteful LLMs are, how they're based on training data taken without consent and remixed without attribution, their cost to labor, their biases and harmful effects on mental health, and of course the grotesque and grating smoothness of their prose. But it also cannot be stressed enough how stupid their integration into tools like Sheets or Docs has been.
In so many of these cases, the LLM integration is what JĂĽrgen "tante" Geuter refers to as "tool-like" or "makeshifts at best": the =AI formula is a kind of paste being applied to the interface in lieu of having actual purpose-built mechanisms for a given task. For example, sentiment analysis is a well-known problem that can be solved with natural language processing relatively cheaply, but instead of offering =SENTIMENT as a well-scoped and deterministic formula, Google has chosen just to pipe cells into a chatbot.
Similarly, when the docs describe categorizing a list of pizza places by NYC borough using their address and neighborhood, there are tools that would be useful for that — and which are already available, in some form, in Sheets! Google Apps Script provides programmatic access to the Maps geocoder to turn address strings into lists of locations at decreasing levels of granularity, which could include sub-city geography. Adding =GEOCODE could have both a high level of accuracy and other information that could be used to verify the result. There is no reason to get a chatbot involved in this! Just expose the functionality that already exists!
The reason I find the intrusion of Gemini and LLM assistance into Sheets so particularly frustrating is not just that I'm a giant tabular data nerd. It's also that prior to this year, Sheets and Excel had started quietly expanding the underpinnings of their formula language in exciting new ways. =LET, =LAMBDA, =MAP, and =REDUCE build on the work that had started with =FILTER to create a more consistent model for how formulas handle array values, with exciting results. As the formula language became more accomodating to functional programming, it wasn't hard to imagine how other services could be exposed directly in the sheet, without requiring Apps Script or Visual Basic.
Instead, what Google has done is smear new features into a "prompt" formula, a completely opaque tool that takes in and returns arbitrary text in unpredictable ways. This fits well with the pseudo-mystical belief system of AI proponents, who treat LLMs as a "miracle machine" that can be applied to any problem if you just let it churn long enough. But it does nothing to make spreadsheets a better tool, an object that has been shaped intentionally for accomplishing discrete and useful tasks, and with which a person can gain expertise. It is as though someone wished that Excel's notoriously fickle date handling behavior could be moved into a function, and the monkey's paw curled.
For the first half of the year, I didn't have a ton of time to play or watch much of anything — Spanish lessons for our student visas kept me busy, and between that and work my concentration was shot. So that meant I spent a lot of time in stuff that could be played in bursts without continuity: Game Boy pinball and fighting games.
Street Fighter 6 had really grabbed me in our last year in Chicago. But when I switched over to Linux at the start of 2025, SF6 didn't always jive with my older Nvidia card, and I was a little tired of its metagame anyway (there's only so many times you can watch the same Ken combo), so I started learning Guilty Gear Strive in March.
Strive is a very different game, with its own quirks and frustrations: where SF6 has strong system mechanics that tend to flatten out variations in play style, Strive has thirty-odd characters who each break the rules in some way. It also has a lot more complicated options for interrupting offense and spending meter. The result is a much more dynamic game, with the caveat that some matchups (Happy Chaos, Faust, I-no) resemble trolling more than actual competition. I like it, and the changes for the upcoming 2.0 version sound promising, but I'd love something (Marvel Tokon?) that's somewhat of a midpoint between the two philosophies.
I did fit in a few smaller single-player titles between classes and work. Blade Chimera is a pretty good metroidvania from Team Ladybug, although if you haven't played Deedlit in Wonder Labrynth, start with that instead. UFO 50 was a great value for the money: fifty NES-style games from a fictional developer, so even if they're not all good, you're guaranteed a few hits that match your taste (for me, that's Party House, Elfazar's Hat, and Overbold).
I also surprised myself by completing New Game++ in Armored Core 6 after bouncing off it pretty hard in 2024 — I think it does an astonishingly bad job of providing guidance on its own mechanics, which I know is FromSoft's whole deal but there's still a reason that the only game of theirs that I really connect with is Sekiro. AC6 is fine: I like the parts that remind me of Virtual On, mostly.
Three indie games stand out on PC once I had time and energy for them. In May, Blendo Games finally released Skin Deep, their immersive sim in which you rescue cats from space pirates in a series of Die Hard-inspired slapstick scenarios. I love pretty much everything Brendon Chung has ever made, and this is no exception: it's funny, surpisingly touching, and just a little bit janky in a way that adds texture, not frustration. Well worth the eight year wait since Quadrilateral Cowboy.
Metro Gravity is two Rush games in one: mixing the "any direction can be down" mechanic from Gravity Rush with the beat-matching combat of Hi-Fi Rush, all coated with a strong PS2 aesthetic. I enjoyed this quite a bit, although not quite enough to do all the fiddly challenges for extra costumes, and I do think the story gets a little over its skis at the end. Still a pretty incredible first game from a solo developer, and I'm excited to see what he does next.
Third, I tried Wanderstop toward the end of the year. although I don't know if I'll finish. People love this for its funny dialog and exploration of burnout, and if I'd been playing it in March, that probably would have spoken to me more deeply. But in December, now mostly recovered from chronic sleep deprivation and still happily employed at the best job I've ever had, I just wasn't in that psychological space anymore.
In August I bought a Switch 2, which was its own little minigame since Amazon Spain was convinced I was committing credit card fraud and kept cancelling my order. This was partly reward for graduating classes, and partly a way to get out of my office chair. The vast majority of my time on it was spent in Silksong, which is a masterpiece that I never want to look at again. I also played through Donkey Kong Bananza (fun, but disposable), Mario Kart World (same), and Star Wars: Outlaws (genuinely far better than it has any right to be).
However, two lesser-known Switch titles probably deserve special notice. One of these is Absolum, a roguelike brawler from the team that made Streets of Rage 4 a few years back. It's not particularly well-balanced, but it feels phenomenal to play, and it's beautiful to look at. People don't make titles in this genre much any more, and there are good reasons for that, but I'm glad Guard Crush is keeping it alive.
The other is Demonschool, a tactical puzzler with a self-explanatory name. This reminds me of Into the Breach: it's a little more forgiving with the rewind, but each turn asks you to set up a chain of actions with deterministic results, and then you're graded on how quickly you dispatched the required number of enemies and how many people on your team survived. It's good, and while the writing at the macro level is not particularly great (and the game itself is just a little bit too long), on a line-by-line basis it's one of the funnier things I played this year.
Finally, thirty-two years after it was initially released, I've finally beaten Final Fantasy VI, at which point Belle immediately confiscated the Analogue Pocket so she could play it again herself. I can see why a lot of people love this game, but it's not going to supplant FFXIII as the one I'm irrationally attached to. Still, happy to take that one off the bucket list.
My hope for 2026 is that it'll be a chance to catch up similarly in the PC space, having now updated my GPU to something a little more modern, if not quite cutting-edge. As someone who is trying very hard to keep LLM slop out of my life, a strong rule of thumb is going to be to stick to games that were in development prior to 2023 — the equivalent of low-radiation steel construction — or were developed by teams with clear disclosure policies around AI usage. I don't like that this is something I have to think about, but unfortunately we live in a nightmare run by oligarchs obsessed with turning fossil fuels into LinkedIn posts. On the other hand, that gives me roughly one-and-a-half console generations of entertainment to keep me busy until the bubble finally pops.
Twelve (!) years ago, when I started writing what would turn into the interactive template for the Seattle Times, NPR, and Civic News (not to mention a few others), I needed a way to cue up its various functions from the command line. As this was pre-Webpack, this was an interesting space with a lot of innovation going on. I went with Grunt, instead of Gulp, Broccoli, or Brunch.
Since at this point there are probably readers who have never used (or even heard of) these tools, it's worth talking about the design of Grunt a bit. The basic concept is that your build process can be organized into tasks, and those tasks can be composed into larger pipelines. So I can transpile client-side scripts with grunt bundle, or run the CSS preprocessor with grunt less, but I can also run both with grunt bundle less. More importantly, I can define a new meta-task that lumps these together with other build steps into a single pass: grunt static in the rig loads data, applies it to HTML templating, builds JavaScript, assembles CSS, and copies assets to the build folder for publishing.
Grunt was originally introduced and pitched to many developers based on its plugin ecosystem, but I never used those very much. Instead, as the interactive template grew and adapted to new projects (e.g., scraping title data for the NPR Book Concierge, baking out election results, connecting to Google office apps), the real value became the way that it imagined the build process as a vocabulary. Grunt ended up being almost Forth-like: a system in which you create very small "words" of functionality (which are easy to create and maintain, due to their limited scope) and then use the tool to sequence and combine them toward more complex goals.
In the wider dev culture, Grunt was eclipsed by Webpack, which was A) boosted by the popularity of React and B) provided an all-in-one solution for front-end build tooling (as long as you didn't mind debugging a truly incomprehensible configuration file). Once out of fashion, Grunt lost a lot of energy. The last significant update in the source repo was more than three years ago, and the last real feature release was about a decade back. It shipped a copy of Coffeescript all the way up through 2020! I am personally very content to use tools that are tried and true, and Grunt has continued to work well without complaints for all this time, but there's a looming sense that at some point either Apple or Node (or both) will ship an update that breaks it, and that'll be an awkward week for me.
Unfortunately, none of the tools that replaced it — Webpack, Vite, Parcel, npm scripts, etc. — really do what Grunt was doing. They're very good if you want to build out a single-page application on a few static routes, especially if you really only care about the JS code path. But they're not designed to be a general-purpose task runner for a static site generator, and they certainly don't offer the same kind of composability. So I've started writing Heist, a kind of minimal subset of Grunt that takes advantage of all the advances in the Node runtime over the intervening decade.
The JavaScript community loves to come up with new approaches to problem solving — new philosophies and theories of architecture — which it deploys via increasingly complex runtimes and compilation processes. These tendencies are fractal: they show up at the broad level, as with JSX or "component models," and at the micro level, as with the endless parade of state management solutions. I don't think this innovation is necessarily bad, but it perpetuates the pattern of what happened with Grunt: not only are old tools abandoned when a replacement is introduced, but their approaches are discarded as well.
But what if, as with Heist, we retained the design and just modernized the code? Today's built-in JavaScript environment is monumentally more capable both on the browser and the server than it was a decade ago. Instead of building around an entirely new fundamental conceit, forcing developers to abandon trusted paradigms in favor of blowing bong hits in the cat's face, we start from a familiar concept with a radically smaller and more maintainable codebase. This turns out to be surprisingly compact: the core of Grunt task management, plus file system searches, ends up being about 160 lines of code when we take advantage of modern Node.
I catch myself doing this fairly often. Heist is Grunt built on a modern foundation. Skelethon is Backbone-style MVC and Conspiracy is HTML-based templating, again on a modern foundation. The interactive template itself (as well as my textbook on interactive graphics) builds out a piecemeal jQuery from browser primitives. I've also explored building new patterns (like signals) using classic OO techniques. Some of this is no doubt nostalgia — the same way the best music was always released your senior year of high school, the best code patterns are the ones you learned at your first professional development gig — but I also have a strong feeling that those paradigms weren't broken, and don't deserve to be tossed aside.
The challenge is distinguishing between traditional patterns that were a "best worst choice" option for the time of their design (think of Hyperscript-style h("div") DOM construction) and which ones are still useful. I wish I had hard and fast rules for this, but the one I come back to most often is "good boilerplate." Isn't all boilerplate bad? I would argue no, it's actually deeply important.
Take my favorite web development punching bag: React hooks, which are violently anti-boilerplate. Lots of developers love hooks. They provide a way to manage persistent state in a UI made up only of nested functions (if you're thinking about explaining this to a student, alarm bells should already be going off). And they seem kind of magical when you use them, because somehow they're managing to track a value consistently across function invocations using only a local variable, which (if you know anything about JavaScript scope) seems like it should be impossible.
The reason for that, under the hood, is that hooks are tracked using a linear list of value slots that is populated and accessed whenever React renders. That means that the order and frequency of these must stay constant for it to work, and they can only be called from inside a function chain initiated by the render process. You can't use hooks in regular code, and you cannot put them inside any dynamic code path, such as loops or conditionals. There's a whole set of rules for this, plus dev plugins to help flag misuse. All of which seems self-evidently insane to me, but it does eliminate the need to type out the class definition.
Now, I don't like writing boilerplate code more than anyone else, and I generally think it's a sign that you need to think about your abstractions more clearly. But I would argue that a little boilerplate is good for you. Similar to the "framework vs. library" distinction, boilerplate often implies that you remain in control of execution, and it provides a strong example for structure if it's well-designed. Arguably, the biggest problem with React's class components wasn't the classes themselves, it's that the lifecycle methods were garbage — everything they've done since has been downstream of those early bad decisions.
Not to mention, when we think about younger developers, it's good to not only have that structural guidance, but to also have more opportunities for them to engage with the language on a practical basis. This is, incidentally, another problem with learning from AI (another "boilerplate avoidance" tool): if you argue, as many have, that LLMs can handle the boring things like "writing a loop" or "defining a function," it means that junion developers stop engaging with the syntax routinely, which means they likely fail to develop an intuitive sense of how the language actually works, in much the same way that Google Translate does not help you develop actual fluency in Spanish.
There's a rich seam of developer experience that's available to us here, locked behind that little bit of boilerplate. If we think of friction not as a thing to be absolutely avoided, but as an interesting part of the rhetorical and design space — something that shapes the directions that users will take when they use the code in anger — then choosing where to deploy it becomes more interesting. In reality, all code has this friction, but older code patterns tend to make it explicit (subclassing, events and other engagement with the runtime, multiple syntax constructs) and a lot of newer code is implicit (rules for when constructs can be used, use only of syntax that can fit into a single expression or function).
Notably, JavaScript as a language offers us much greater tools for managing productive boilerplate. We have classes with access control modifiers now, proxies (albeit with performance caveats), native modules, Maps and Sets, custom element lifecycles... If you want to implement patterns that were originally designed in other (more full-featured) languages, like MVC UI, we are in much better shape to do that now than we were in 2014. We're not inventing things from first principles anymore (Alex Russell is fond of noting that React is a legacy technology built for a much older, less reliable browser environment, which explains why it ships so much redundant code to this day).
So by evaluating the boilerplate of older code patterns, we can start to distinguish between the ones that are using it to offer guidance or direction, and those that simply considered it a cost you paid for (at the time) higher performance or integration with their framework. We can let the latter die off without regret. But in the case of the latter, whether the original was web-related or from native development, it's not just nostalgia at work. There's value that's worth reconsidering, now that our tools and platform give us greater capabilities for their implementation.
It's intern season, which means I spend a lot of time explaining "this is how a shell works" and "this is how you save time by piping a CSV through grep." That also turns into lessons about how zsh and Bash are different, and how to install newer versions of all the tools that Apple leaves stranded in 2005 because they don't like the GPL. It's frustrating, but educational, for all concerned.
One thing that we don't cover, in these pairing sessions, is any kind of shell customization. This is partly because it's already hard enough getting young journalists on board with the command line, without introducing variations into the experience. But it's also a long-standing philosophy of mine, which is: assume the system could experience catastrophic failure at any time, so optimize for garbage tools you'll always have, instead of adapting to a curated ecological niche. Be a trash panda, not a panda-panda.
Among other things, this means I try to use unmodified configurations for:
But over the course of my career, I've spent a lot of time logged into new computers: fresh virtual machines, debugging for coworkers and interns, replacements for broken laptops, shells on devices that are secretly running Linux under the hood, and so on. Some of these are the result of disaster and some are just how infrastructure works now. Either way, it makes sense to learn to be immediately productive in an unpredictable environment.
And for all its frustrations, there's also something to be said for learning a set of tools with a lifespan measured in decades. These tools are often not as good as you'd like them to be, but they're also often very efficient, versatile, and so deeply ingrained in the culture that they're unlikely to ever go away. Sed is always going to be there, a thought that is equally comforting and depressing. And if they are broken, it will be in reliable ways that persist over time — you do not have to worry about someone pushing an update that suddenly alters how grep searches, which is a relief in an age of auto-updated everything else.
Of course, this is a lot to unload on an intern who's still trying to figure out why there are at least four different "quit" commands that they need to learn. Trust me, I tell them. Either it'll all make sense eventually or you'll realize that your manager cannot be trusted—either way, it's good preparation for a data journalism career ahead.
From The Verge:
Windows 11 is also getting a variety of new AI features, including an AI agent baked into the Windows settings menu; more Click to Do text and image actions; AI editing features for Paint, Photos, and the Snipping Tool; Copilot Vision visual search; improved Windows Search; rich image descriptions for Narrator; AI writing functions in Notepad; and AI actions from within File Explorer. In its detailed blog post Microsoft says the AI features are designed to “make our experiences more intuitive, more accessible, and ultimately more useful.”I have been using Windows (or MS-DOS, even) for my entire life. It hasn't always been a pleasant experience, but it has generally worked and (as someone who does a fair amount of gaming) ran the specific software that I wanted to run, with a high level of backward compatibility. But over the last few years, it has become clear that Microsoft and I are no longer seeing eye to eye on what my computer should be doing.
I think it should be doing the tasks that I ask for predictably and reliably, and Microsoft thinks that it should be inserting semi-randomized chatbots into every nook and cranny of the system, when it's not taking screenshots of everything I do and running them through OCR. This is in addition to a series of UI tweaks that have made Windows increasingly unusable, like the weirdly-centered taskbar or jamming ads into the start menu.
So during my holiday break this January, I started taking steps to make 2025 my own personal Year of Linux on the Desktop. I'd already been using Xubuntu to keep an old 2009-era Thinkpad viable, so I knew it could work for my professional tools and workflows, but I'd resisted making the shift on my tower PC until the end of Windows 10 support gave me a deadline to meet.
To make the switch easier, I bought a second hard drive solely for a Fedora installation, keeping the original drive in the machine unchanged. With this setup, I could switch between the two operating systems at boot, gradually moving over to Linux for longer and longer periods, and pulling files off the old drive as necessary. As of this week, I haven't reverted back into Windows for a couple of months, and I thought it might be useful to write about what the experience has been like, for anyone considering the same migration.
I picked Fedora since it was often recommended as the "no-nonsense" distribution. I actually tried a few distros, and went through a few reinstalls, before everything was functional at the basic hardware level. In particular, the Nvidia drivers for my GPU (a well-loved 1070 GTX) were obnoxious to install and upgrade reliably. Also, partway through the process, my motherboard blew out (I'm assuming for unrelated reasons, probably due to being carted across the Atlantic in a badly-padded suitcase) and had to be replaced, including a new CPU (AMD this time around).
Finally, I needed to manually disable the USB wake functionality for my mouse, which is apparently chattier than Linux likes when it's trying to sleep. This fits my general expectations from prior experience, which was that 95% of my hardware would be fine and 5% would have some screwy but generally surmountable problems (it was certainly miles easier than debugging sleep issues on Windows has been for me).
At the software level, Fedora generally does feel more cohesive, in ways both big and small, compared to the Ubuntu systems that I've used in the past. For example, the logo and progress graphics shown during initial boot or upgrade are more polished, which seems minor but contributes to confidence that corners are not being cut on larger issues either. I prefer Flatpak to Snap, which was another factor in its favor. And of course, they're not trying to sell me a "Pro" service subscription, which I appreciate.
It does have some quirks, mostly around its software sources: by default Fedora only comes with "free" (read: non-patent encumbered) repositories enabled. You need to turn on "non-free" in order to install Steam or good video drivers, and in some cases you'll want to reinstall applications like FFmpeg to use the non-free version, unless you really like having choppy, broken video playback. You also need non-free repos for Blu-ray support, which is important to me.
With the system in a solid working state, I disabled automatic updates. I'll still run upgrades, of course, but I can do it on my own schedule. This is part of my general philosophy with computing going forward, which is that I'm through with software that doesn't respect the user's right to informed consent. Almost everything I do is either on the web platform (which can handle a little lag) or offline, I don't need to be updating every time a UI gets revamped so that a product manager can get a raise.
My personal opinion is that user interface design pretty much peaked with Windows 7 and it's all been downhill from there. I want to be able to snap windows to the screen edge and tile them, search and run applications from the OS menu, and see the names of the programs I'm running in the task bar. I do not want to have big media popups whenever I change the volume. I do not want a "notification center" that serves as the junk drawer for old chat messages. I do not want recommendations or ads anywhere on a computer that I paid for with my own money.
I do not want "AI" anywhere on the machine, at all.
Keeping all that in mind, I went with KDE for the default window manager, since it seemed like the best modern "Windows-ish" option (I like XFCE, but it's always felt clunky in terms of keyboard shortcuts and settings, and Gnome has a real case of MacOS envy that I've never cared for). A few tweaks have put everything pretty much the way I like it — mostly.
The primary catch, which will be unsurprising to any Linux user, has been multi-monitor support. KDE handles rendering to my second screen just fine, especially once I got the Nvidia driver running to support Displayport daisy-chaining. But it seems clear that testing on multi-monitor setups is not something that Linux devs do very much. For example, the taskbar on each monitor is a separate "panel" with its own configuration and application order — if I drag Firefox to the leftmost position on the first screen, I have to repeat this on the second if I want them to be consistent. The result has been that I've largely stopped re-ordering items in the task bar so that I don't obsess over it, which is not ideal but ultimately doesn't actually have any impact on my workflow.
Window positioning also sometimes requires intervention. For example, I typically keep the picture-in-picture video window for Firefox on my second monitor, so that it's basically a "watch in background" button. But KDE initially insisted on automatically placing the pop-up player directly over the Firefox tab, until I specifically told it to remember the last position and size of a browser window with a specific title. I don't know why that's not the default. Of course, some applications do remember where they were last located, which I think they're doing for themselves instead of letting the OS handle it, because Linux UI has a legendary "no gods, no masters" approach to window management that I think only got worse with Wayland.
My favorite thing about the GUI is actually not graphical at all — it's the ability to run an SSH daemon on our local network. Every now and then something (usually a game running full-screen) crashes in a way that captures all input and prevents closing the misbehaving application. I used to fix these crashes by using some weird kernel-level keyboard shortcuts that bypass KDE entirely, causing their own oddities along the way. But then I realized I can just open a terminal on my phone and kill the process from there. This is funny, and stupid, and incredibly useful, all at the same time.
Multi-monitor gripes aside, window management has pretty much been a non-issue. It stays out of my way and most of my GUI muscle memory still works. I suspect that in part this is because I've always been a person who didn't really customize the defaults very much, whether on Windows, Linux, or Chrome OS (and on MacOS I mostly only installed tweaks to get it to the standards of the others). So I've never developed any really esoteric habits that I needed to unlearn.
At the end of the day, software is what actually matters. As long as I can actually run the programs I need — and I am not, in this regard, a person with particularly esoteric tastes — my experience will probably be fine.
I spend roughly 90% of my time in Firefox. Unsurprisingly, it works exactly as I would expect, with the exception of an annoying keyboard shortcut change that I wrote an add-on to fix. Both Firefox and Chrome have been able to see the camera and microphone for video chats without any issues, although there were issues with WebUSB, so I've been running Via from its older AppImage package.
Sublime also worked out of the box. For backups, I'm using Deja Dup instead of Acronis. Bitwarden came from Flatpak. Mozilla VPN is only officially supported on Ubuntu, but you can compile it or (and this is what I did) you can grab the RPM file from the releases on the GitHub repo. I will have to update this manually, but it hasn't been an issue so far.
For e-mail, I had been using a copy of Outlook 2007 for the last two decades. Obviously, it wouldn't be directly compatible and this was a good time to upgrade anyway. It took a little while to figure out the tools needed to convert my old .PST files into something that Thunderbird could import, but I only needed to do that once, and then it's been pretty smooth sailing. For the rest of my office suite, LibreOffice works, but at this point I'm much more comfortable in Google Sheets, so there wasn't much migration cost there.
The truly impressive thing has been running Steam. Of course I knew that Wine had existed for doing Windows emulation, and that Valve had put in a lot of effort to make applications run on their Linux-based handheld. But it's one thing to know that in theory, and another to see pretty much everything in my library run pretty much flawlessly under Proton. The one exception — literally the one I've found so far — is Street Fighter 6, which starts out in good shape and then at some point the shaders lose coherence and turn the screen into one giant chaotic polygon soup. As a result, I've been playing less SF6, which is probably not a bad thing for my sleep habits, and does mean that I'm finally getting around to games I've neglected, like UFO 50 and the just-released Skin Deep.
Sadly, my ancient copy of Photoshop 6.0 has issues under the current versions of Wine. Since I refuse to use either a newer Adobe product or the badly-named open-source image editor, this may become a longer-term project.
Of course, as a web developer, the truly nice thing has been getting access to Linux's tooling support without having to run WSL or see what would function under Git's MSYS shell. Being able to run Poppler, or FFmpeg, or Python, without jumping through any of those hoops is not a revolution, since working on Windows for such a long time has made me pretty good at hoop-jumping. But it's very much appreciated.
Would I recommend this to an ordinary person, like my dad? Probably not. Once the system is running, it's been largely stable, but getting it there was still not frictionless. If you have closed-source devices that you're plugging in, or you need a specific proprietary application, I wouldn't want to take it on faith that those things will work (e.g., my much-loved Zune HD can be viewed in the file explorer but I can't add music to it). And when things break I'm still sometimes digging into a text file from the terminal to fix them.
On the other hand, that kind of transparency — being able to deeply configure the system from a text file — is exactly what I want from my computer these days. Linux has gotten good enough that day-to-day I'm not spending a lot of time recompiling or manually tweaking (i.e., I'm not doing sysadmin work as a hobby), but if I need to change something, I have that option.
Meanwhile, nothing is being installed without my permission. Copilot is not lurking on the horizon, and I don't have to cringe whenever Windows Update pops up a notification or pesters me to update to Windows 11. People complain about systemd or Wayland, but they feel like things I can conceptualize by comparison, and that I can access on my own terms. It's not a perfect system, but for the first time in a long time, it feels like mine, and that's well worth the occasional inconvenience.
We need to talk, friends. Things have gotten weird out there, and you're not dealing with it well at all.
I'm in a lot of data journalist social spaces, and a couple of years ago I started to notice a lot of people starting to use large language models for things that, bluntly, didn't make any sense. For example, in response to a question about converting between JSON and CSV, someone would inevitably pipe up and say "I always just ask ChatGPT to do this," meaning that instead of performing an actual transfer between two fully machine-readable and well-supported formats, they would just paste the whole thing into a prompt window and hope that the statistics were on their side.
I thought this was a joke the first time I saw it. But it happened again and again, and gradually I realized that there's an entire group of people — particularly younger reporters — who seem to genuinely think this is a normal thing to do, not to mention all the people relying on LLM-powered code completion. Amid the hype, there's been a gradual abdication of responsibility to "ChatGPT said" as an answer.
The prototypical example of this tendency is Simon Willison, a long-time wanderer across the line between tech and journalism. Willison has produced a significant amount of public output since 2020 "just asking questions" about LLMs, and wrote a post in the context of data journalism earlier this year that epitomizes both the trend of adoption and the dangers that it holds:
I really started to think I was losing my mind near the end of the post, when he uploads a dataset and asks it to tell him "something interesting about this data." If you're not caught up in the AI bubble, the idea that any of these models are going to say "something interesting" is laughable. They're basically the warm, beige gunk that you have to eat when you get out of the Matrix.
More importantly, LLMs can't reason. They don't actually have opinions, or even a mental model of anything, because they're just random word generators. How is it supposed to know what is "interesting?" I know that Willison knows this, but our tendency to anthropomorphize these interactions is so strong that I think he can't help it. The ELIZA effect is a hell of a drug.
I don't really want to pick on Willison here — I think he's a much more moderate voice than this makes him sound. But the post is emblematic of countless pitch emails and conversations that I have in which these tools are presumed to be useful or interesting in a journalism context. And as someone who prides themself on producing work that is accurate, reliable, and accountable, the idea of adding a black box containing a bunch of randomized matrix operations in my process is ridiculous. That's to say nothing of the ecological impact that they have in aggregate, or the fact that they're trained on stolen data (including the work of fellow journalists).
I know what the responses to this will be, particularly for people who are using Copilot and other coding assistants, because I've heard from them when I push back on the hype: what's wrong with using the LLM to get things done? Do I really think that the answer to these kinds of problems should be "write code yourself" if a chatbot can do it for us? Does everyone really need to learn to scrape a website, or understand a file format, or use a programming language at a reasonable level of competency?
And I say: well, yes. That's the job.
But also, I think we need to be reframing the entire question. If the problem is that the pace and management of your newsroom do not give you the time to explore your options, build new skills, and produce data analysis on a reasonable schedule, the answer is not to offload your work to OpenAI and shortchange the quality of journalism in the process. The answer is to fix the broken system that is forcing you to cut corners. Comrades, you don't need a code assistant — you need a union and a better manager.
Of course your boss is thrilled that you're using an LLM to solve problems: that's easier than fixing the mismanagement that plagues newsrooms and data journalism teams, keeping us overworked and undertrained. Solving problems and learning new things is the actual fun part of this job, and it's mind-boggling to me that colleagues would rather give that up to the robots than to push back on their leadership.
Of course many managers are fine with output that's average at best (and dangerous at worst)! But why are people so eager to reduce themselves to that level? The most depressing tic that LLM users have is answering a question with "well, here's what the chatbot said in response to that" (followed closely by "I couldn't think of how to end this, so I asked the chatbot"). Have some self-respect! Speak (and code) for yourself!
Of course CEOs and CEO-wannabes are excited about LLMs being able to take over work. Their jobs are answering e-mails and trying not to make statements that will freak anyone out. Most of them could be replaced by a chatbot and nobody would even notice, and they think that's true of everyone else as well. But what we do is not so simple (Google search and Facebook content initiatives notwithstanding).
If you are a data journalist, your job is to be as correct and as precise as possible, and no more, in a world where human society is rarely correct or precise. We have spent forty years, as an industry niche, developing what Philip Meyer referred to as "precision journalism," in which we adapt the techniques of science and math to the process of reporting. I am begging you, my fellow practitioners, not to throw it away for a random token selection process. Organize, advocate for yourself, and be better than the warm oatmeal machine. Because if you act like you can be replaced by the chatbot, in this industry, I can almost guarantee that you will be.