More from David Heinemeier Hansson
We just opened a search for a new junior programmer at 37signals. It's been years since we last hired a junior, but the real reason the listing is turning heads is because we're open about the yearly salary: $145,849*. That's high enough that programmers with lots of experience are asking whether they could apply, even if they aren't technically "junior". The answer is no. The reason we're willing to pay a junior more than most is because we're looking for a junior who's better than most. Not better in "what do they already know", but in "how far could they go". We're hiring for peak promise — and such promise only remains until it's revealed. Maybe it sounds a little harsh, but a programmer who's been working professionally for five years has likely already revealed their potential. What you're going to get is roughly what you see. That doesn't mean that people can't get better after that, but it means that the trajectory by which they improve has already been plotted. Whereas a programmer who's either straight out of school or fresh off their first internship or short-stint job is essentially all potential. So you draw their line on the basis of just a few early dots, but the line can be steep. It's not that different from something like the NFL scouting combine. Teams fight to find the promise of The Next All-Star. These rookies won't have the experience that someone who's already played in the league for years would have, but they have the potential to be the best. Someone who's already played for several seasons will have shown what they have and be weighed accordingly. This is not easy to do! Plenty of rookies, in sports and programming, may show some early potential, then fail to elevate their game to where the buyer is betting it could be. But that's the chance you take to land someone extraordinary. So if you know a junior programmer with less than three years of industry experience who is sparkling with potential, do let them know of our listing. And if you know someone awesome who's already a senior programmer, we also have an opening for them. *It's a funnily precise number because it's pulled directly from the Radford salary database, which we query for the top 10% of San Francisco salaries for junior programmers.
While the world frets about the future of AI, the universal basic income advocates have an answer ready for the big question of "what are we all going to do when the jobs are gone": Just pay everyone enough to loaf around as they see fit! Problem solved, right? Wrong. The purpose of work is not just about earning your keep, but also about earning a purpose and a place in the world. This concept is too easily dismissed by intellectuals who imagines a world of liberated artists and community collaborators, if only unshackled by the burdens of capitalism. Because that's the utopia that appeals to them. But we already know what happens to most people who lose their job. It's typically not a song-and-dance of liberation, but whimper with increasing despair. Even if they're able to draw benefits for a while. Some of that is probably gendered. I think men have a harder time finding a purpose without a clear and externally validated station of usefulness. As a corollary to the quip that "women want to be heard, men want to be useful" from psychology. Long-term unemployment, even cushioned by state benefits, often leads men to isolation and a rotting well-being. I've seen this play out time and again with men who've lost their jobs, men who've voluntarily retired from their jobs, and men who've sold their companies. As the days add up after the centering purpose in their life disappeared, so does the discontent with "the problem of being". Sure, these are just anecdotes. Some men are thrilled to do whatever, whenever, without financial worries. And some women mourn a lost job as deeply as most men do. But I doubt it's evenly split. Either way, I doubt we'll be delighted to discover what societal pillars wither away when nobody is needed for anything. If all labor market participation rests on intrinsic motivation. That strikes me as an obvious dead end. We may not have a say in the manner, of course. The AI revolution, should it materialize like its proponents predict, has the potential to be every bit as unstoppable as the agricultural, industrial, and IT revolutions before it. Where the Luddites and the Amish, who reject these revolutions, end up as curiosities on the fringe of modern civilization. The rest of us are transformed, whether we like it or not. But generally speaking, I think we have liked it! I'm sure it was hard to imagine what we'd all be doing after the hoe and the horse gave way to the tractor and combine back when 97% of the population worked the land. Same when robots and outsourcing claimed the most brutish assembly lines in the West. Yet we found our way through both to a broadly better place. The IT revolution feels trickier. I've personally worked my life in its service, but I'm less convinced it's been as universal good as those earlier shifts. Is that just nostalgia? Because I remember a time before EVERYTHING IS COMPUTER? Possibly, but I think there's a reason the 80s in particular occupy such a beloved place in the memory of many who weren't even born then. What's more certain to me is that we all need a why, as Viktor Frankl told us in Man's Search for Meaning. And while some of us are able to produce that artisanal, bespoke why imagined by some intellectuals and academics, I think most people need something prepackaged. And a why from work offers just that. Especially in a world bereft of a why from God. It's a great irony that the more comfortable and frictionless our existence becomes, the harder we struggle with the "the problem of being". We just aren't built for a life of easy leisure. Not in mass numbers, anyway. But while the masses can easily identify the pathology of that when it comes to the idle rich, and especially their stereotyped trust-fund offspring, they still crave it for themselves. Orwell's thesis is that heaven is merely that fuzzily-defined place that provides relief from the present hardships we wish to escape. But Dostoevsky remarks that should man ever find this relief, he'd be able to rest there for just a moment, before he'd inevitably sabotage it — just to feel something again. I think of that often while watching The Elon Show. Musk's craving for the constant chaos of grand gestures is Dostoevsky's prediction underwritten by the wealth of the world's richest man. Heaven is not a fortune of $200 billion to be quietly enjoyed in the shade of a sombrero. It's in the arena. I’ve also pondered this after writing about why Apple needs a new asshole in charge, and reflecting on our book, It Doesn't Have To Be Crazy At Work. Yes, work doesn’t have to be crazy, but for many, occasional craziness is part of the adventure they crave. They’ll tolerate an asshole if they take them along for one such adventure — accepting struggle and chaos as a small price to feel alive. It's a bit like that bit from The Babylon Bee: Study Finds 100% Of Men Would Immediately Leave Their Desk Job If Asked To Embark Upon A Trans-Antarctic Expedition On A Big Wooden Ship. A comical incarnation of David Graeber's Bullshit Jobs thesis that derives its punchline from how often work lacks a Big Why. So when a megalomanic like Musk — or even just a run-of-the-mill asshole with a grand vision — offers one, the call of the wild beckons. Like that big wooden ship and the open sea. But even in the absence of such adventure, a stupid email job offers something. Maybe it isn't much, maybe it doesn't truly nourish the soul, but it's something. In the Universal Basic Income scenario of having to design your own adventure entirely from scratch, there is nothing. Just a completely blank page with no deadline to motivate writing the first line. If we kill the old 9-5 "why", we better find a new one. That might be tougher than making silicon distill all our human wisdom into vectors and parameters, but we have to pull it off.
Picasso got it right: Great artists steal. Even if he didn’t actually say it, and we all just repeat the quote because Steve Jobs used it. Because it strikes at the heart of creativity: None of it happens in a vacuum. Everything is inspired by something. The best ideas, angles, techniques, and tones are stolen to build everything that comes after the original. Furthermore, the way to learn originality is to set it aside while you learn to perfect a copy. You learn to draw by imitating the masters. I learned photography by attempting to recreate great compositions. I learned to program by aping the Ruby standard library. Stealing good ideas isn’t a detour on the way to becoming a master — it’s the straight route. And it’s nothing to be ashamed of. This, by the way, doesn’t just apply to art but to the economy as well. Japan became an economic superpower in the 80s by first poorly copying Western electronics in the decades prior. China is now following exactly the same playbook to even greater effect. You start with a cheap copy, then you learn how to make a good copy, and then you don’t need to copy at all. AI has sped through the phase of cheap copies. It’s now firmly established in the realm of good copies. You’re a fool if you don’t believe originality is a likely next step. In all likelihood, it’s a matter of when, not if. (And we already have plenty of early indications that it’s actually already here, on the edges.) Now, whether that’s good is a different question. Whether we want AI to become truly creative is a fair question — albeit a theoretical or, at best, moral one. Because it’s going to happen if it can happen, and it almost certainly can (or even has). Ironically, I think the peanut gallery disparaging recent advances — like the Ghibli fever — over minor details in the copying effort will only accelerate the quest toward true creativity. AI builders, like the Japanese and Chinese economies before them, eager to demonstrate an ability to exceed. All that is to say that AI is in the "Good Copy" phase of its creative evolution. Expect "The Great Artist" to emerge at any moment.
I've been running Linux, Neovim, and Framework for a year now, but it easily feels like a decade or more. That's the funny thing about habits: They can be so hard to break, but once you do, they're also easily forgotten. That's how it feels having left the Apple realm after two decades inside the walled garden. It was hard for the first couple of weeks, but since then, it’s rarely crossed my mind. Humans are rigid in the short term, but flexible in the long term. Blessed are the few who can retain the grit to push through that early mental resistance and reach new maxima. That is something that gets harder with age. I can feel it. It takes more of me now to wipe a mental slate clean and start over. To go back to being a beginner. But the reward for learning something new is as satisfying as ever. But it's also why I've tried to be modest with the advocacy. I don't know if most developers are better off on Linux. I mean, I believe they are, at some utopian level, especially if they work for the web, using open source tooling. But I don't know if they are as humans with limited will or capacity for change. Of course, it's fair to say that one doesn't want to. Either because one remain a fan of Apple, in dire need of the remaining edge MacBooks retain on efficiency/battery, or simply content inside the ecosystem. There are plenty of reasons why someone might not want to change. It's not just about rigidity. Besides, it's a dead end trying to convince anyone of an alternative with the sharp end of a religious argument. That kind of crusading just seeds resentment and stubbornness. I know that all too well. What I've found to work much better is planting seeds and showing off your plowshare. Let whatever curiosity that blooms find its own way towards your blue sky. The mimetic engine of persuasion runs much cleaner anyway. And for me, it's primarily about my personal computing workbench regardless of what the world does or doesn't. It was the same with finding Ruby. It's great when others come along for the ride, but I'd also be happy taking the trip solo too. So consider this a postcard from a year into the Linux, Neovim, and Framework journey. The sun is still shining, the wind is in my hair, and the smile on my lips hasn't been this big since the earliest days of OS X.
More in programming
How do cryptocurrencies actually work, though? Join Alice and Bob as they embark on designing a new digital ledger for secure “Bobcoin” transactions.
Understand how computers represent numbers and perform operations at the bit level before diving into assembly
(async () => { const colors = ['fb6b1d','e83b3b','831c5d','c32454','f04f78','f68181','fca790','e3c896','ab947a','966c6c','625565','3e3546','0b5e65','0b8a8f','1ebc73','91db69','fbff86','fbb954','cd683d','9e4539','7a3045','6b3e75','905ea9','a884f3','eaaded', '8fd3ff', '4d9be6', '4d65b4', '484a77', '30e1b9', '8ff8e2'].map(c => `#${c}`); const mask = document.querySelector('#mask'); const replacement = await fetch('/images/2025-04-12-tidbyt-second-life-tidbyt-mask.svg').then(r => r.text()); mask.style = ''; mask.innerHTML = replacement; let i = 0; let delay = 10; const svg = mask.querySelector('svg'); svg.removeAttribute('width'); svg.removeAttribute('height'); svg.setAttribute('style', 'width:auto;height:auto;position:absolute;top:0;right:0;bottom:0;left:0;opacity:0.4;'); for (const path of svg.querySelectorAll('path')) { delay += 20; delay *= 1.02; setTimeout(() => { path.setAttribute('fill', colors[i++ % colors.length]); }, delay) path.addEventListener('mouseover', () => { path.setAttribute('fill', colors[i++ % colors.length]); }); } })() Remember the Tidbyt? It’s a super low-resolution, internet-connected, wood-paneled display that I wrote a review of it back in 2022. It’s been on my shelf for years now, showing the time, weather, warning me when the UV is going to be high. In 2023 I used it as an excuse to learn some Rust, to render custom graphics. It’s a toy, a distraction, a worry stone for me to work on when I need something open-ended and low-stakes. Anyway, the company that made the Tidbyt is no more. They got acquihired by Modal, a company that makes serverless AI compute hosting. So, they aren’t making devices right now, and the blog post promises that their cloud services will keep working. I don’t hold anything against the Tidbyt team: in fact, our Val Town office was coincidentally right next to theirs in a WeWork, and we met in real life! They’re very nice folks, and were doing so much with a small team. Lots of respect to them. Modal made a smart choice acquiring Tidbyt. But realistically, it’s time to make sure my device doesn’t become e-waste. The Tidbyt is ready for this One of the biggest critiques of the Tidbyt was that it was just an LED matrix and an ESP chip. You could buy an LED matrix on Sparkfun, the ESP, a power supply, some wood for the enclosure, and you’d have your own DIY Tidbyt. Maybe you could do it for half the price! But that’s also a strength. The Tidbyt is not some custom SoC with an exotic custom software stack and boutique hardware. It is what it looks like: a neat combination of commonplace parts. That makes it kind of future-proof and flexible. The first step is to replace the firmware. Tidbyt’s stock firmware routes all of its requests through the Tidbyt company’s servers. I want to eliminate that hop. Replacing the firmware Thankfully, Tidbyt published their ‘HDK’, which is an open source version of their stock firmware. It’s remarkably simple: It connects to Wifi It downloads a WebP image from a URL It displays that WebP image The HDK contains the code to do this stuff. There’s very little code required, but it does drag in a WebP decoder, Wifi library, and a library for running the LED matrix. But, setting up the HDK I ran into issues both small and large: it had issues with HTTPS URLs and Wifi passwords that contain spaces. Plus nobody has been added as a contributor to the HDK repository, so Pull Requests aren’t being accepted and it hasn’t had a change in 7 months. But the community came to the rescue with tronbyt’s firmware-http, a fork of the HDK that fixes every issue I experienced. Open source works! So back in 2022 I included this chart of the Tidbyt network: With an updated HDK, this workflow is a lot simpler. Instead of sending images to the Tidbyt servers and those Tidbyt servers delivering them to my device, the device makes requests directly of the server that generates the images. Replacing pixlet The Tidbyt team wrote pixlet, a little framework for generating pixel graphics that the Tidbyt displays. It lets you define a React-like tree of components - some text in a stack, a rectangle, images, and so on - and does all of the layout and rendering. The tronbyt community also forked pixlet and are actively developing it, which is fantastic. But this part of the stack I really never liked. That’s why I spent so much time reimplementing it in Rust and JavaScript. Partly it’s the language - pixlet apps are written in starlark, which is kind of an outgrowth of the Bazel build system from Google. Starlark is sort of like Python, but isn’t actually compatible with anything in the Python ecosystem. It’s very niche, limited, and overall just weird. I think I understand why Tidbyt would choose Starlark - it’s fast and has hermetic execution - making it safe to run untrusted Starlark programs because they can’t access the filesystem, network, or even the system clock without being given explicit controlled APIs to do those things. If you’re building a cloud service that runs a lot of untrusted user code, dictating that code is all Starlark is a really good cheat code - I know firsthand how hard it is to run untrusted JavaScript. But I’m not building a cloud service full of untrusted code. People who are self-hosting their Tidbyt devices (dozens of us!) don’t benefit from the tradeoffs of the Starlark language. They’d be better off with something normal. I rewrote pixlet again It’s called indiepixel and it’s a Python reimplementation of pixlet. It supports almost the entire pixlet API, and comes with the added benefit of being Python. You can use Python modules! You can read from the filesystem, parse CSVs, do all of your usual Python stuff. You can embed it in a Python application to render some graphics. What does indiepixel do currently? Renders text in the glorious retro BDF pixel font format. Renders pixelated pie charts, rectangles, and boxes. Supports animation for its WebP outputs. Provides a nice UI for browsing your selection of screens. It’ll probably never be finished, but it works well enough to power my Tidbyt. I’m running indiepixel on a free Render server instance, but it should run pretty much the same on any Python-compatible hosting: the only tricky dependency is Pillow, which it uses for image parsing and rendering. My free time for computer-oriented side projects has been limited, due to other commitments and an intention to get offline on the weekends. I’ve been sewing, biking, and running more. So I really want a side project I can enjoy, and indiepixel has fit the bill. It’s really satisfying to implement a new widget and see it rendered in blocky 64x32 pixels. The Pillow image rendering library for Python is mostly wonderful and very powerful. Why Python? Why is indiepixel written in Python? Well - I learned from tidbyt-rs that Rust would be an awkward fit as a scripting language for rendering graphics. The well-known Rust complexities around memory management made simple things difficult for me, which would make them totally unacceptable for others. Besides the attraction of being able to compile a small binary that might be able to run on the Tidbyt itself, Rust didn’t have many other advantages. The Pillow module really is such an advantage for Python. JavaScript doesn’t have a real alternative: there’s sharp, a great module for image conversion, but nothing that has such a great canvas interface. node-canvas is fine, but it doesn’t support WebP or animation, which are critical features for this project. I also wanted a test out the amazing new Python tooling that Astral is cooking up, like uv. I now have a better grasp of the Python ecosystem than I did a few months ago, and it’s optimistic but mixed. uv is amazing, but Python has a lot of legacy cruft around packaging. People are critical of NPM, but I think it did benefit from being established after PyPI and learning from its lessons. Thank you Steven Loria for a PR that fixed everything and made it all work and saved me months of tweaking settings. The graphic I watercolored that Tidbyt a while while ago and have been seriously dragging my feet on finishing this blog post. Sometimes the watercolor-illustration wags the technical-blog-post dog’s tail? Anyway, it’s a callback to that little world, with some small tweaks: this time I thought it’d be nice to have it be both watercolored and interactive. That ‘cybernetic’ feel. The secret recipe: a nice palette from lospec, creating a black & white mask of areas in Affinity Photo and vectorizing it with potrace, and then just some JavaScript that recolors based on hover handling. If you’re using the Tidbyt or some similar pixel-displaying device, try out indiepixel! It’s niche and has required a silly amount of effort to generate a glorified weather clock in my apartment, but it was a fun time chasing another interest.
I learned a new word: ductile. Do you know it? I’m particularly interested in its usage in a physics/engineering setting when talking about materials. Here’s an answer on Quora to: “What is ductile?” Ductility is the ability of a material to be permanently deformed without cracking. In engineering we talk about elastic deformation as deformation which is reversed once the load is removed for example a spring, conversely plastic deformation isn’t reversed. Ductility is the amount (usually expressed as a ratio) of plastic deformation that a material can undergo before it cracks or tears. I read that and started thinking about the “ductility” of languages like HTML, CSS, and JS. Specifically: how much deformation can they undergo before breaking? HTML, for example, is famously forgiving. It can be stretched, drawn out, or deformed in a variety of ways without breaking. Take this short snippet of HTML: <!doctype html> <title>My site</title> <p>Hello world! <p>Nice to meet you That is valid HTML. But it can also be “drawn out” for readability without losing any of its meaning. It’ll still render the same in the browser: <!doctype html> <html> <head> <title>My site</title> </head> <body> <p>Hello world!</p> <p>Nice to meet you.</p> </body> </html> This capacity for the language to undergo a change in form without breaking is its “ductility”. HTML has some pull before it breaks. JS, on the other hand, doesn’t have the same kind of ductility. Forget a quotation mark and boom! Stretch it a little and it breaks. console.log('works!'); // -> works! console.log('works!); // Uncaught SyntaxError: Invalid or unexpected token I suppose some would say “this isn’t ductility, this is merely forgiving error-parsing”. Ok, sure. Nevertheless, I’m writing here because I learned this new word that has very practical meaning in another discipline to talk about the ability of materials to be stretched and deformed without breaking. I think we need more of that in software. More resiliency. More malleability. More ductility — prioritized in our materials (tools, languages, paradigms) so we can talk more about avoiding sudden failure. Email · Mastodon · Bluesky