At least for those of us who used floppy disks, software feels overcomplicated today.

I think most of what we’ve built is made of too many parts. I think it’s partly because we no longer make the same thing a thousand times, refining it as we go. We don’t reduce software to its essence like we did with common tools like a hammer or a dish sponge. We take software parts off the shelf and use them wholesale for 10% or 1% of their total capability. Trimming the unused remainder and integrating it all to suit the problem better would require we stop and learn what those dependencies are made of and how they work. In our rush to ship, we rely on abstraction for speed, but abstraction has a cost too, and it has been compounding for decades.

The advice to “write one to throw away” was always a good practice, but we usually don’t follow it, probably because of time. LLMs write code quickly, but it’s unreliable, right? So an LLM can write the version you plan to throw away, while you focus on critical thinking about right and wrong ways to do it.

The folks behind the Oil shell have suggested some vocabulary around reducing the number of integrations among M kinds of inputs and N kinds of outputs from M times N to M plus N: You create an intermediate conventional interface called a narrow waist. The Perlis-Thompson Principle expresses the scaling benefits of minimizing distinct concpts. Read more. Having done this narrow waist maneuver a few times and spotted it in the wild here and there, I’m glad to have some language for it.

Every single person seems to have a distinct opinion about the AI field since ChatGPT’s launch. So far I think this: Statistical methods just have wildly different pros and cons than traditional programming. It’s an exciting and increasingly necessary space to explore because the pro column offers so much brand new capability. But the con column is also loaded with fundamental problems. When do you use one method versus the other? For which pieces of a system, for which levels of control? Depends on what they’re each good at. I think both will continue to matter, but I can’t make any more assertions.

After spending a few years with TypeScript, I remain impressed with all it accomplishes, especially with structural typing. But its central goal is still to add safety around JavaScript, and it can’t escape certain design limitations that imposes. For instance, it can’t avoid having both undefined and null. Working in TypeScript, I feel somewhat held back by JavaScript. It’s much better, but not like I think a language created two decades later ought to be.

One of GitHub Copilot’s well known strengths is writing boilerplate code. But in the past, what, 10 years? There has been less boilerplate to write as languages have improved. I’ll take Objective-C and Swift for example: Language features have removed specific known boilerplate, like replacing retain/release with ARC and autosynthesizing protocol conformances. And as of Swift 5.9, macros can practically address cases of boilerplate that would have been a burden to build into the base language.

For instance, the stored properties of a type decorated with the @Observable macro will track incoming access during a view update, in order to re-invoke that view update when their values change.

That’s a whole lot like a pattern my projects used to use around 2015: We’d have a UIViewController with some stored properties that could change, and we’d note certain parts of the view that needed updating based on which properties they accessed. It came down to a didSet on each stored property to run each of the update___ functions that depended on them. Of course wiring that up was the programmer’s job, but the pattern was very straightforward. And here it is done for us, just tracked dynamically, and having the tracking installed by a macro.

It is said, many things we know as “design patterns” from the object oriented world are, from the perspective of macro-heavy languages like Lisp, an opportunity to DRY.

Boilerplate elimination and limited token context windows are two reasons I would rather use AI tools with languages that are nice and terse in the first place.

In any case, I think Copilot will continue to shine in one-shot, “how can I write this code” scenarios, and with languages that aren’t getting any shorter.

I had expected the Rumored Apple Headset’s user interface to be made primarily of volumes, intricate objects, and texture, without so many flat windows floating in space. But having seen the pitch for Vision Pro, I think their choices make a lot of sense.

Flat windows seem necessary to launch visionOS with a significant number of existing apps plus the Mac desktop. VR and AR devices have generally been bottlenecked by limited software catalogs. I really liked my PSVR, but the catalog was its downfall. So this most compatible approach may bring in users first and then compel businesses to follow their customers to the platform.

Flat windows also work nicely with using your gaze as a pointing device. Even though you can drag objects in full 3D, it’s a less clumsy 2D interaction (pitch and yaw) that casts the ray to the pinch starting point. It also helps that windows generally aren’t positioned to occlude one another.

So are we bringing back VRML or what?

A few years ago, after Mac OS X’s 20th anniversary, I got it in my head to read all of John Siracusa’s long form reviews for Ars Technica. I forget how I got the impulse. But it’s a pattern that I like to back up and study the basics, see what I didn’t understand before, and improve my foundations.

Recently I published some technical highlight links from that study path as a gist: macOS Internals

Lately I tried Mastodon client Mona. This is just a first impression, but I think Mona shows how special an app can be when you put your time into how it works first and what it looks like second.

Visit a user profile screen and check out the “…” menu in the corner. Actions are included here even if they can be done from the body of the screen. It’s so complete that it reminds me of a Mac app that does the Menu Bar right, which I’ve never seen attempted in a phone app.

On macOS, you might treat Mona like a single-window, single-tasking app, but you can also create as many windows as you want and group them as tabs. (Looking at you, Slack.)

Drag and drop works, even on phones.

There are all manner of options related to accessibility and usability.

Actions like “Add Divide View” may be a bit far afield, but progressive disclosure keeps them out of the way until you want them.

Mona also seems to have revived Tweetie for iPad’s sliding column stack. It’s a bit unstructured for my taste, but I enjoyed finding the pattern in the wild again, and it’s time well spent on behavior.

One place they could spend more time on appearance is the app icon. I don’t understand its meaning. It does suggest familiar UI, but bits of UI chrome are the edges of an app, not the center.

RSS legend NetNewsWire is my other go-to example of “plain” iOS user interface done well.

As a brutalist web design fan I was a little embarrassed to have dropped large images right in the bodies of blog posts which then aggregate into an unpaginated blog page. The hanging side task of updating my blogging script to make a thumbnail and show that in the article instead of the original image formed a minor ugh field around the blog in general. Well, the answer was much easier once I gave it some attention: Image alt text can be link text instead. Page size is then as tiny as I could wish for.

The greatest single orders-of-magnitude leap in capability I ever witnessed was when I moved from 56kbps at home to 100BaseT in a college dorm room. Web pages of the time were sized to fit in the patience budget of a dial-up user. Overnight, everything loaded like lightning. The web felt faster than my hard disk. And as broadband became common and page sizes grew, it has just never seemed that good since.

There are other good points on this topic: In developing countries, a user’s network speed may make your comparatively bloated site or service inaccessible. I admit that’s not top of mind for me and I’m mostly motivated by my own experience. I just want it to be as good as I remember, and I hope my memory is accurate.

When it’s up to me, let apps be apps and let web pages be printable rectangles again. My browser already lives in a multi-window environment; it doesn’t need to be layers all the way down. A little on-page interactivity can be OK—I’ll praise the hamburgers on the mobile views of swift.org and signed-in GitHub for disclosing their menus by pushing page content down rather than covering it.

Hill climbing won’t reach a new hill. With local optimizations you can only solve local problems.

It’s good to explore around your immediate space to understand how much a local adjustment could improve things. How high is this hill and how can I climb it? Some of those changes are worth making; others would yield diminishing returns.

But you just can’t locally address a complaint about your situation if it’s pervasive in that local space. To recognize that you need to zoom out, and to change it you need to get out. So you have to experiment beyond your comfort zone, or you may remain quite stuck with your most longstanding problems.

Everybody understands “Out of sight, out of mind.” If we put something away when it’s not in use, it preserves our attention. If we keep the object in a drawer, it’ll be more available than in the attic, but it still won’t cost any attention. If availability matters more than the cost, we keep it visible. And we organize more for visitors because the messes we’ve adjusted to would repulse them.

Progressive disclosure is software jargon for the same idea. We display just the most necessary things. That makes new users comfortable because their attention can handle that much. We keep everything else out of sight but available, so users can get more involved when they’re good and ready.

Spite is a natural reaction to being judged instead of accepted. Though an old chip on your shoulder remains your own responsibility to solve.

A todo is an impending, growing regret. You made the list in order to remember, but soon enough you can’t stand to remember.

Try this on: “As soon as I start, I’ll be done.”

Only starting takes effort. Sit up in bed and you’re up. Dial the phone number and the doctor will see you now.

So what if it’s bullshit and a trick? Anxiety was a bullshit trick first. When you’re derailed, getting onto the rails is the entire task.

Clams are the original mimic chests.

Significant whitespace puts the “y tho” in Python.

With only talent you can do sorcery, but to become a wizard you must RTFM.

IPv4 is the Dewey Decimal System of the internet.

I moved from Objective-C to Swift right about five years ago, so this NSHipster article about method dispatch was a nice nostaglia trip. If you’ve written code but you haven’t spent a lot of time with Objective-C, it’s an excellent illustration of message-passing, which is a dynamic dispatch pattern that differs from the vtables you’d find in C++ or in pure Swift classes.

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging”…

—Alan Kay

Yesterday I played Death Stranding, and last night I dreamed about pressing balloons together to make bigger balloons, except on the inside they were huge eggs.

There should be a Pac-Man edition of the Roomba.

Another downhill path covered in yellow fallen leaves

Looking downhill along a narrow sidewalk, with a carved rock wall to the right and the town of Jozankei to the left

A statue of a Kappa at the base of a mountain path

Jozankei is a cute hot spring town. Kappa like this one are everywhere.

Diced coffee jelly, deep brown in color with liquid residue, in a white bowl on a spring green tablecloth

There is such a thing as coffee jelly. It’s good, but hard to eat with chopsticks.

We made a new friend today and probably won’t see him again. I want to remember him here.

Yesterday we traveled for a solid 24 hours. This morning we woke in Narita, flew to Sapporo, and took the train into the city. At every step we’ve had uncertainty, new things to figure out, a language barrier, and stress. Any step that doesn’t succeed could derail the rest.

To reach the hot spring hotel, our first real destination, we had reserved a shuttle bus from Sapporo. We hiked through the city to it, rolling suitcases behind us. But at what we thought was the appointed place and time, there was no such shuttle. So of course we’re on high alert.

We asked two parked strangers if their vehicles happened to be the shuttle bus, and in each case they went out of their way to figure out our problem. The first stranger was driving an unrelated shuttle bus and ceded when the second got involved. The second stranger was taking a break from work, and he believed we needed to be several blocks in the other direction, back at the train station where we started. He took us there in his car. He said he designed structures, waving a broad paper chart. I that took to mean he was an architect or structural engineer.

When he dropped us off, the bus had already gone. We were ready to just figure out another way to get there. But our mystery man showed up again, having parked around the corner. He called the hotel. He called the bus service. He drove us around the block to the main city bus terminal. He quizzed a driver about which bus we needed and where it was parked. A few minutes later we were on the right bus going to the right place.

Sometimes you don’t know you’re among friends until you need one.

Foamy coffee with a royal blue ceramic mug and gold wisk

I ordered a coffee in a little restaurant in the Sapporo TV tower, and I loved the colors they chose for this cup and its wisk.

Yesterday was a relatively grueling 24-hour travel day, ending at an overnight hotel near the Narita airport. It’s one of those hotels that feels like what I imagine the 1970s were like for a traveling businessman, except we scored a non-smoking room.

The crown jewel of the room is this multi-function hardware built into the bed’s headboard.

Electronic bedroom control device reminiscent of classic stereo equipment

It controls the room’s lights and air. Its radio has two preset FM stations and one for BGM – a term for background music which I just learned is used outside of video games. These are the original radio buttons, the kind where if you push one in the others pop back out. The centerpiece is what appears to be an all-analog alarm clock from before the invention of the snooze button.

So mod.

The last post was a test of publishing photos, which means this blog is ready to travel. Watch for Japan photos soon!

My feet, oddly far away

This “ultra wide” camera makes me look tall.

I’m going to travel for a few weeks soon. This has been planned for a long time, and I’ve been lightly anxious whenever I’ve thought about it.

I’m always anxious before travel. I think it’s because for every decision you make when planning and packing, you raise the stakes, and then it all has to go off without a hitch. When you finally leave your house, you can’t have forgotten anything, you can’t be late, your plane can’t be late, and so on. What kind of vacation is that? So I only tend to relax once I arrive, when the highest-stakes plans have played out.

This time, I’m starting to get a little excited, and we haven’t even left yet. It might be because I did very little of the planning.

What’s funny about languages with optionals is that seeing ? gives you certainty and seeing ! raises questions.

I don’t know why I was complaining the other day about how Docker is unintuitive. Maybe it is, but I just needed to read 100% of the documentation and try out half the sample code, and now it fits in my head like it ought to.

I’m only half kidding. You don’t get to learn everything top-down. Like when you begin Swift with print("hello world"), you benefit from progressive disclosure in the language and its documentation. After a short tutorial you can do useful things, and then you come back for more information when you need to know the next thing. Stack Overflow not only gets you unstuck, it improves your intuition.

Some things you have to learn bottom-up, like the shell. man cat, man ps, man 3 intro, apropos regex, ad infinitum. Only when you’ve been exposed to all these details do their combinations begin to make sense, and nobody is going to tell you which information you’re missing. You have to just be willing to absorb every tiny thing and then synthesize the intuition yourself. In theory, if you learned it all, you could solve every problem. So in practice, you read everything available and look up anything you don’t recognize. That learning curve can be a lot when you’re just stuck on one thing, but it’s a fact of life.

Everything you can learn is somewhere on this spectrum. If you’re stuck, you may need to back up and adapt your learning style.

iOS 13 beta was nice to use all summer, but next week I’ll need my new phone to restore from a backup. That means I needed to make a backup on the public release version, the GM. This year it’s complicated because the beta channel is currently on 13.1, not 13.0, so it’s a downgrade. And because that’s something you’re never supposed to have to do, I had problems.

After downgrading iOS from 13.1 beta to 13.0 GM, my Mail database seemed to fail to load. The app would freeze for about six seconds after tapping most any mailbox, and then the mailbox list would be empty. It was as if loading from the message cache caused a failure, like a crash and restart in whatever daemon owns the data. The only exception was an inbox I had zeroed before the upgrade, which worked fine.

To fix that I wanted to reset my Mail cache, for which there are no explicit controls. It wasn’t enough to remove my Mail accounts and delete the app and add them back. Thinking a cache wouldn’t be included in backups (and it should be only a cache because these are IMAP accounts), I backed up my phone on 13.0, erased all content and settings, and restored. That did the trick for Mail.

An hour later I found my music library was suffering from a similar problem, and no amount of tricks would solve it. Although I could add and remove files through means like iTunes sync, the library index appeared to be missing a lot of them. The index was immutable, for instance, I couldn’t add a song to a playlist or add from Apple Music. This was also true for Podcasts and TV. I imagine they are still one big database under the hood.

I think the reason having restored from a backup made no difference to the media library is that it is not simply a cache, it’s a source of truth. Syncing through iCloud Music Library is optional, so the backup mechanism probably needs to include the index even though it omits the media files.

I had to preserve some data, do a fresh install, and then set everything up again from scratch. Ultimately the amount of time that took was about the same as the amount of time I spent trying to repair things. It would have been simpler to just reset to a state I could be confident in.

If any of that made your stomach turn, don’t tempt fate by putting a beta on your carry phone.

In the timeline of multiplayer games there was a point where you could play on the internet with strangers, but there wasn’t yet voice chat. Games let you type a line of text and broadcast it to the top of everyone’s screen, and it would mix in with server messages about what else happened, like who killed who by what means, or who joined and left.

Quake 1 exemplified this when its online multiplayer community blew up. Id released the Quakeworld client, which compensated for the lag we all had on dial-up – Under 300 ping was great; under 100 would get you accused of being a low ping bastard / LPB. The community built GameSpy, then called QuakeSpy, the global server browser Windows 95 app, which we all used as a launcher. The DOS-era convention of having a boss key to quit to the shell was still in effect, though.

It was all so new, that year everyone was both a noob and a troll. I for one was in middle school. So we’d bind a key to say “F10 for extra armor” and see who quit. But it was not usually the player ahead of you on the scoreboard.

I think it’s cool the way identity terminology changes. If terms reflect how we identify, they reflect how we identify in the context of other identities. When you state your identity, you alter the landscape of what people can identify with. Now we have so much to say about it that just about anyone who spends the time to read about identity on Wikipedia learns something about themself.

Getting release and CI and debug behavior all working in Docker is rough. It’s a very opaque experience. You can’t see, so you have to do everything in baby steps, lest you have a problem you can’t track down at all. This is worth it, right? It’s satisfying when it works, but I feel like we’re solving the problem of configuration drift or environment mismatch by merely shrinking the part you write once and then don’t touch again.

In your Azure DevOps Pipeline, if your variable group’s values are not showing up where you reference them, check the syntax. There are two ways to satisfy the variables key, and they mean different things.

First, if you supply a dictionary, you are creating literal variable names and values:

  name1: value1
  name2: value2

Second, if you supply an array of dictionaries, each item’s keys determine whether it’s a single variable or a group:

- group: group-name
- name: name1
  value: value1
- name: name2
  value: value2

(YAML can be a little confusing here. Each hyphenated line begins an array item, and since there is at least one of them, the outer value is an array. Each line with a colon is a name-value pair in a dictionary, and since there is at least one of them after each hyphen, each array item is a dictionary.)

If, like me, you saw the first syntax, then learned about variable groups, then glossed over the fact there were two syntaxes, you might end up with this:

  group: group-name
  name1: value1
  name2: value2

That syntax doesn’t reference a variable group, it creates a variable whose name is “group”. That’s a nasty silent failure for a newcomer.

Assuming this doesn’t appear malformed, I was able to write and publish a blog post from a one-line shell command. Maybe when I’ve used it for a while, I’ll put up a tutorial post.

That’s a nice piece of work for a Sunday afternoon. I always feel like I do better work when I’m just fooling around, compared to when I’ve dreamed up a big project.

I thought I’d put together a blog that’s as easy for me to post to as Twitter and Facebook make it, so I will have a place to publish things without resorting to Twitter and Facebook.

The recent release of NetNewsWire 5 got me thinking about this. I’ve also been using doing, whose shell interface takes the drudgery out of tracking what I work on.

I figure what I need is a blog I can post to from the shell. Having moved my site to GitHub Pages recently, I find I have Jekyll available, which handles the Markdown and page generation bits. Scripting up a new post tool should be doable.

I’ll bet Heimdall is really good at Where’s Waldo.