A lot of content around here boils down to links to someplace else, for which all I have to add is a brief call-out or commentary. As such, the headlines for each of these posts link to the original source article. (If you want a permalink to my commentary, try clicking the salt shaker.)

I was excited to be hosted by the Changelog folks for a recap discussion following Apple's WWDC keynote on Monday. If you listened to my WWDC spoiler cast, you might like this after-action report.

Changelog & Friends 48: Putting the Apple in AI – Listen on Changelog.com

A few errata and missed pitches:

  • I didn't mention this on the podcast, but I was deeply disappointed to see Apple didn't expose any system-level model that can just be invoked as a general purpose LLM API. This would have been a game-changer for small developers who are currently hobbled by figuring out how to roll out meaningful LLM features without risking that the cost of calling through to the OpenAI API will eclipse the revenue generated by app sales and subscriptions
  • When discussing why Apple Intelligence requires an iPhone 15 Pro, I whiffed on the reason (which became clear later that day) that the root cause is memory. Devices with less than 8GB of RAM probably can't run Apple Intelligence without the base operating system falling over
  • I referenced Mac Virtual Display working with the "Wireless NIC turned off". That's not quite right. The network interface needs to be on, but if neither device is connected to a wifi network, screen sharing will work over a peer-to-peer wifi connection

I hope you'll listen! I like Jerod and Adam a lot. The whole Changelog family of podcasts is fantastic. You can tell they're smart because they have yet to invite Breaking Change to join the network.

Yesterday in the Platform State of the Union, Apple celebrated 10 years of Swift and then in the very next slide announced they finally built a testing framework for it.

Digging in more, I found this forum post that a "vision document" (I'm not familiar with Swift people's vernacular for this stuff) for testing direction had been accepted.

Anyway, this is all interesting in its own right and something I'll be following generally, but I have to say that for such a broad and important topic, that forum thread had an absolutely incredible time-to-mocking-derail metric. The very first reply is about mocking and then seemingly half of the subsequent posts in the thread are people spouting off their personal opinions on mocking instead of any of the more important stuff.

I don't claim to be an expert in very much, but having built several mocking frameworks, spoken about mocking practices (including a stealth mocking keynote at RailsConf and a not-so-stealth mocking closing talk at JSConf), and even named the company I co-founded after a mocking term, I feel like I have some authority to say the following: boy howdy is it a bummer that most developers only understand mocking as a utility and lack any comprehension of how to deploy mocks in a well-defined, consistently-executed software development workflow.

I'm very happy to have nailed an approach that works really well for me, but I'll probably always view it as a major failure of my career that I was never able to market that approach effectively. Even now, I don't have a single authoritative URL to point you to for my "Discovery Testing" approach that would provide a clear explanation of what it is, why it's good, and how to use it. And for me personally, the moment has probably passed and I just need to live with that failure, I imagine.

One of the things I think about a lot with respect to testing practices (including test-driven development) and their failure to really "stick" or spread more broadly is that they transcend any one testing framework or programming language. As a result, consultants like me were so absorbed just porting slightly-different versions of the various tools we needed to every new language that there's no such thing as a single README or book that could explain to a normal developer how to be successful. I tried to write a book once that would serve as a tabula rasa across languages and I almost immediately became trapped in a web of complexity. Other programming idioms and methods that apply across languages are similarly fraught, but mocking's relative unimportance to the primary task of shipping working software probably doomed it from the start.

Anyway, it's cool that mocking will be possible in Swift Testing.

Humane, of overpriced AI tchotchke and parting-foolish-VCs-with-their-money fame:

Our investigation determined that the battery supplier was no longer meeting our quality standards and that there is a potential that certain battery cells supplied by this vendor may pose a fire safety risk.

I would normally be worried about people's safety here, but I'm pretty confident that all of Humane's customers had already stopped using it months ago.

I'm happy to share that I'll be speaking at Rails World 2024. Everything I heard about last year's event was overwhelmingly positive, and my interactions with Amanda give me every confidence that this year's event in Toronto will be great, as well. Japan's RubyKaigi—which has grown to 1400 attendees and attracted dozens of corporate sponsors—has set a high bar for any conference that aims to blend community-building, professional development, and in-person collaboration to push a technology forward, but every indication suggests Rails World is on the right track.

My topic? Glad you asked.

In keeping with the "one-person framework" motif, I'm calling it "The Empowered Programmer", as a sort of sequel to my 2019 presentation, The Selfish Programmer. I'll be talking about the Rails 7 app I've been building this year, in support of my wife's eponymous Better with Becky business.

A few themes that might emerge:

  • The value of proving out the app's basic plumbing with a lower stakes proof-of-concept, so as to avoid packing one's most naive, unconventional code into its most important "MVP" features (in this case, by building Beckygram before breaking ground on the more important strength-training system)

  • Why to adopt and how to get the most out of relatively recent Rails-ecosystem tools like Hotwire, Active Storage, and Solid Queue—many teams skip omakase stack stuff out of habit or because they're upgrading an older app, but staying on the rails has greatly accelerated my productivity as a solo developer

  • The various (mis-)adventures I've had with GPT-4 as my only pairing partner

  • How nice it is to not have React or Webpack anywhere in my codebase. Seriously, Stimulus and Turbo really feel like the "JavaScript sprinkles" we should have had all along, and the amount of pain they can spare you from trying to balance a single-page JavaScript app with a Rails backend is profound

  • Plenty of other takes, served hot

XOXO is back for one last festival this August. Having always wanted to attend, I was about to buy a ticket when I thought to click through to the COVID policy mentioned in their announcement e-mail:

All XOXO participants are required to wear a high-quality mask at all times while inside Washington High (including Revolution Hall, Show Bar, and all common areas inside the venue), the reserved area for Park Pass holders in the tent, as well as at any festival event on our schedule where masks are required.

I knew XOXO was frequented by hipsters, so I'll grant that an all-N95-all-the-time policy in late-2024 is decidedly vintage.

Park Pass holders will have access to reserved seating in the tent at Washington High Park, a large, shaded, well-ventilated space for viewing the simulcast of our main stage programming. Park Pass holders are required to wear a high-quality mask at all times while in the reserved seating area.

Even outdoors, too? Pass.

John Hawthorn, a brilliant Rubyist and contributor of quite a lot of important open source in the Ruby ecosystem references a benchmark for a gem that lets you invoke the Crystal programming language from Ruby.

I'll spoil the post here by giving you the before and after.


The “crystalized” version runs about 4x faster than the pure Ruby version.


Now it’s Ruby that’s 5 times faster than Crystal!!! And 20x faster than our original version.

Writing a program that behaves the way you want is hard, but that's not the end of the journey. Without an understanding of how the computer will execute your instructions, you're left at the mercy of a bunch of arbitrary performance implications that can lead to misguided beliefs emerging within teams and organizations.

A common refrain in recent years is, "we're rewriting all our critical sections of Ruby into Rust." Closer inspection by an expert almost always finds flaws in the assumptions (or, if there are any, analyses) that lead to these whole-cloth "optimizations", however.

I've seen organizations try to pull off pretty much every approach you can think of to escape performance problems and technical debt. The more dramatic the maneuver—like, say, bifurcating one's codebase into two languages and then bridging them—the more likely it is to fail to accomplish the stated goal or to fail outright.

Trust me when I say it's almost always better to dance with the one who brought you. Embracing the problem usually leads to better solutions than running from it.

In a post on X (formerly Twitter), Kosutami explained that Apple has stopped production of FineWoven accessories due to its poor durability

Sorry cows, your skin is simply too good to not wrap our metal devices in.

The company may move to another non-leather material for its premium accessories in the future.

On second thought, it's very unlikely Apple would ever go back to leather. My guess is FineWoven quietly disappears and isn't immediately replaced with another "premium" material in 2024.

The co-founder of a nascent game studio learned of a significant internal leak when a reporter called him for comment. He notified his publishing partner who responded to the leak by cutting funding. Then, after breathing what I'm sure were a series of very deep, very audible sighs he decided to say, "fuck it," and close the whole damn studio:

As a result of the cancellation of the publishing relationship and after careful consideration, I am closing Possibility Space. Today is your last day of employment at Possibility Space and Prytania Media. Your final paycheck including pay for work through the end of today will be deposited to your account, along with any other required payments, as dictated by your work location.

And exit the industry while he's at it:

As of today I am stepping away from the game industry to focus on my family and care for Annie [Delisi Strain]. I wish all of you the best as you navigate this complex industry and the challenges and opportunities ahead.

In a recent episode of Breaking Change I talked at length about why the "AAA" gaming industry is falling apart before our eyes, so reactions like this don't seem so unreasonable to me. I've been wondering aloud for my entire career why the hell anyone gets into the gaming business when virtually everyone involved could make an order of magnitude more money working half as many hours doing literally anything else involving software. Now that the free money spigot has been shut off, investors are starting to realize the same thing and are looking for any plausible pretense to pull the plug.

I'm sure the journalist named in the piece (incidentally, by his own employer, via this article) doesn't feel awesome that his reporting indirectly led to his sources losing their jobs. And the fact he apparently never bothered to contact Annie Delisi Strain—the rare-for-the-industry woman CEO currently on medical leave—for comment is certainly a bad look. But he's probably got enough to worry about, considering that one of the few industries closer to the brink of collapse than games is games journalism.

Note that this App Store policy change is global, not only to comply with the EU's Digital Markets Act.

You can read the updated policy directly, but The Verge breaks it down:

The move should allow the retro console emulators already on Android — at least those that are left — to bring their apps to the iPhone. Game emulators have long been banned from iOS, leaving iPhone owners in search of workarounds via jailbreaking or other workarounds. They’re also one of the key reasons, so far, that iPhone owners in the European Union might check out third-party app stores now that they’re allowed in the region. Apple’s change today could head that off.

There goes the only reason I'd ever be interested in a third-party marketplace. I suspect I'm not alone in that.

The Wall Street Journal (News+ link) with a pretty wild profile of one of Apple's most interesting and influential leaders:

People close to Schiller describe his three main hobbies as cars, Boston sports teams and Apple, where he is still known to work nearly 80 hours a week, respond to emails almost immediately and answer phone calls at any time. He is also heavily involved in philanthropic endeavors, including an institute at Boston College, his alma mater, that carries his name, the Schiller Institute for Integrated Science and Society.

If you've been a senior executive someplace for decades and your company still relies on you working 80-hour weeks, I don't know how else to say it: you fucked up. You should have figured out how to develop other leaders and delegate responsibility to them by now. And unless your unique contributions are going to make the difference in curing cancer, ending global hunger, or bringing peace to the Middle East, you're only exacerbating the problem by continuing to work around-the-clock into your mid-60s.

This is a very good list. A few things I hadn't seen before but will instantly add to my project.

Hirb is great when inspecting elements in the console. It’s a mini view framework for IRB/Console. It can handle displaying information in tables and pages. It’s not quite powerful enough to build a full fledge TUI application, but it’s really useful for quickly inspecting data in the console. Say you want to print the attributes of the last 10 signed in users. Hirb would let you display them as a table instead of a bunch of long lines, It makes it a lot easier to visually parse information. It’s not Rails-specific but comes with Active Record support out of the box.

Looks like a worthy successor to one of my favorite gems, table_print.

Apple has internally tested a new Apple Pencil with visionOS support, according to a source familiar with the matter. This would allow the Apple Pencil to be used with drawing apps on the Vision Pro, such as Freeform and Pixelmator.

One hopes you're supposed to wave it around in the air like a conductor might.

The Weibo user explained that the ‌iPad Pro‌'s new matte display option will be offered in addition to the standard, glossy glass finish. It apparently features -4° to +29° of haze and may tout some kind of blue-light blocking technology to help protect the eyes. Matte screen protectors for the iPad have become popular, so it is possible that Apple is trying to offer such an option at the point of purchase for those who want it.

I wonder what this means for display performance in direct Florida sunshine, as the current iPad lineup is worthless outdoors here.

Apple is in discussions with Google to integrate its Gemini AI engine into the iPhone as part of iOS 18, according to Bloomberg's Mark Gurman.

Through iOS 5, Maps and YouTube were native apps that Apple built and which were backed by Google services. This was advantageous for both parties at first. Apple wasn't nearly ready to roll out its own mapping service and Google was more focused on growing YouTube's reach than monetizing it. Eventually, it stopped making sense for either party, and they went their separate ways.

The primary media narratives about this focused on Steve Jobs' "thermonuclear" threat over Android's copying of the iOS UI and the degree to which the two companies had begun to compete on services. But one thing that was lost in the discussion—which never really squared with the fact Google has continued to pay Apple tens of billions a year to be Safari's default search engine—was that both companies maintain relatively-tenuous moats to lock in customers.

Right now, Google needs people to reach for its AI and search stack before a generation of users learn to "GPT it", and Apple needs an AI stack for its platform that can compete with the dozens of devices set to launch that are little more than thin candy shells on top of OpenAI's API.

I really hate the idea of this deal, and I bet executives at both companies do, too. Which is why it's so unfortunate that it also makes sense.

Gripping story, overall, and worth a read. This bit stuck out to me as something I'd never considered before, but felt obvious as soon as I was exposed to it:

Political communicators are sticking to approaches developed for an era when ticket-splitters and swing voters composed a sizeable chunk of the electorate. But with a body politic that has sorted into two highly polarized parties—with just one-tenth of voters torn between them—the logic of persuading voters to support a candidate has grown obsolete. Ad campaigns should instead promote the Democratic Party itself, Malchow proposes, particularly at moments when news events might help it win new adherents, such as after a mass shooting, which thrusts gun-control policy back into the news and voters might be ready to reconsider their allegiances.

To wit: in an era of extreme party polarization, 90% of people in the US are voting based on party affiliation, but campaign advertising is still centered on candidate choice. This isn't just inefficient, it's counter-productive, since most candidates run away from their parties in general elections because both parties' brands are so toxic. Focusing money and messaging on bolstering a party's brand seems like a much smarter way to meet this moment of overwhelmingly party-line voting.

I can only hope I'll still have meaningful insights to offer others during my final week on earth.

My Twitter account had been peacefully lying dormant since November 18th, 2022, but so many people are still using X that I finally caved a few weeks ago and spent 45 minutes wiring up a syndication strategy, effectively adding it to this site's POSSE.

If you want to do this, here's what you'll need:

  • The azu/rss-to-twitter GitHub Action and a modest amount of a free account's budget
  • A Twitter developer account and app
  • An Atom feed to read from (here's mine, specially crafted to cram the full content of each tweet in the <title> of each <entry>)
  • The handle you're posting as must be marked as an automated account and registered as managed by some other account

Once you have all that, you can define a YAML file defining the action in your GitHub repo like the one for this site:

# .github/workflows/rss_to_twitter.yml
name: rss-to-twitter
    - cron: "*/30 * * * *"
    runs-on: ubuntu-latest
      - uses: azu/rss-to-twitter@v1
          RSS_URL: "https://justin.searls.co/shorter-form.xml"
          TWEET_TEMPLATE: '%title%'
          TWITTER_APIKEY: ${{ secrets.TWITTER_APIKEY }}

The above cron schedule translates to checking the Atom feed "every 30 minutes", which is more frequent than it needs to be in my case. And as for all those ${{ secrets… }} directives, here's how to configure your repository's GitHub Action secrets.

And that's it. Easy!

Apart from being one of my favorite people—and someone whose wisdom had a big impact on my own professional development—Joel Helbing shares a bit about his experience giving himself just enough of a crash-course on whatever skill a new job needs to be able to show up to work on Monday:

Then I ended the call, got in my car, and drove an hour to the nearest Borders bookstore. I purchased two promising books on Microsoft SQL Server, went to the bookstore’s in-house Starbucks, purchased a venti iced coffee, sat down with those two books and a legal pad, and mapped out my weekend in fine detail. It came down to 15 minutes for this chapter, 10 for that chapter, skip this other chapter, etc. Then I drove home and followed my script meticulously for the whole weekend. This was not easy for me; I’m a curiosity-driven learner who loves to follow a thread and go deeper. Not this weekend, though. I stuck to the plan, and on Sunday night I got back in my car and started the long drive to my new gig.

(Imagine I spent the thirty seconds to insert a "this is the way" GIF here.)

But seriously, Joel's not kidding. He's indeed one of the most deliberate, curious people I've ever met, and I bet rushing through a bunch of content in order to get his arms around a topic was acutely painful for him. But when you're a consultant, your clients need you to be conversant in whatever they're focused on, and frequently that means brushing up on topics and technologies your previous ten clients weren't focused on.

Doing this was always painful for me, too. In part, because I'm a perfectionist who really struggles to move onto chapter two until I've absorbed, critiqued, and improved on everything the author posited in chapter one. For me, the act of learning is an exhausting war of attrition. But when my job depended on me showing up knowing something, I had no choice but to swallow my pride and immerse myself in a topic uncritically in order to learn enough to be dangerous.

This is an intensely uncomfortable activity. That immersion feels like drowning at first. I'm even feeling it this morning, as I'm upgrading a database I haven't touched in 3 years and which I presently can't remember how to back up—my gut is churning in worry as a result. As a consultant, though, I always understood that the stakes were always higher for my client than for me, and every ounce of discomfort I shouldered almost always translated to an ounce of burden I could remove from their plate. Sometimes that meant using their preferred technology over mine. Or assigning myself to their legacy systems so their full-timers could be the ones to break ground on the next green-field app. Or, and this was always the hardest for me, relenting and using their janky issue trackers and time-keeping systems.

Some consultancies blunt this reality by deploying large teams where there are levels of indirection between clients and practitioners—placing an engagement manager in front of the client who can run interference while the team of consultants behind him ramp up at a more leisurely and comfortable pace (as they bill every hour at a full rate). Other consultancies prioritize their convenience over their clients' needs by narrowing their services and prescribing a single monolithic "Way" to work with them—often requiring clients to build systems in the agency's favorite tech stack—firmly ensconcing each consultant in a cocoon of comfort. But at Test Double, it never occurred to do anything other than lean in and rise to the challenge of actually meeting our clients where they are. We spent years demonstrating to our clients that no matter how arcane their technology or byzantine their org chart, our people will get up to speed so fast it'll make their heads spin. You get good at what you do, and if the thing you do is stomach discomfort as you learn hard things in service of others, then there's almost no limit to what you can accomplish.

One last note: showing up to a client following a weekend-long crash course in a particular technology doesn't make you a fraud. Nearly twenty years in consulting has taught me that the people most worried about misrepresenting themselves and their abilities are the people who have the least reason to worry. The fact they care so much almost always means they'll put in the work when they need to. The real frauds, meanwhile, don't worry at all. And while Joel was holed up in a Starbucks for 72 hours, I'm sure they were having a delightful and relaxing weekend. And Joel's much richer for it, as he's gotten four careers' worth of experience by repeatedly diving into new industries, organizations, and technologies, whereas the real imposters only learned how to talk a good game as they skated through life without ever stretching themselves.

Nice set of reminders on how to validate e-mail addresses in Rails models and was glad to find his second example to be almost identical to what I found in my newest app:

class User < ApplicationRecord
  validates :email,
    format: { with: URI::MailTo::EMAIL_REGEXP },
    presence: true,
    uniqueness: { case_insensitive: true }

One thing I'd be sure to add, though, is the new normalizes class method to ensure all email addresses saved by your app are easily compared by stripping whitespace and lower-casing them. From my User class:

normalizes :email, with: ->(email) { email.strip.downcase }

Never hurts to revisit the stuff you wrote on the first day of an app's life.

"It is currently estimated that new models with significant changes to the Vision Pro specification may not be in mass production until 2027," Kuo said today.

This makes me glad I bought at launch as opposed to waiting, and even more glad I opted for monthly AppleCare payments as opposed to buying the fixed two-year contract.

I haven't been happy with the size or weight of any of Apple's notebook computers since 12" MacBook. And since moving to 14" and 16" for the MacBook Pro, I've been praying for a 12" MacBook Pro to be released. But now, with the Vision Pro among us, my desire for an ultralight Mac has only increased. In fact, about a month ago, I tried to get DALL•E and Midjourney to generate images of a headless MacBook Pro device (like an Apple II but in a modern industrial design language), but they weren't up to the task.

I had never even considered beheading a MacBook Air, but this guy sure did:

The fact this mod is as straightforward as it is is a real testament to Apple's improvements in repairability over the last few years. The only shame of it is that there's no way to reliably log into the device after a reboot (short of guessing at the state of the password entry screen). If it weren't for that I seriously might consider doing this to my own M2 MacBook Air after its warranty is up.

UPDATE: Rob Carlson inspires some hope that a headless MacBook isn't as unusuable at boot as I might have worried:

I log into a headless MacBook Pro all the time. Just hit the "up volume" key a bunch of times until VoiceOver turns on, then it'll prompt you for username, then password, and give three beeps if you're right.

Accessibility truly is for everyone!