Why macOS Will Lose Developers

It won't be flakey keyboards, or restictive security policies or app store monopolies. It will come down to containers.

Containers are eating software development writ large

Containers consumed operations long ago. Packaging and deploying apps was a use case that was obvious and readily supported by the Docker ecosystem. Prior to deployment someone, or some thing, would take the code for an app and build a docker image out of it. This would happen maybe a couple times a day if you were doing continuous deployment, and usually not on your machine if you were doing continuous deployment at scale. Your app was running in a container, but the rest of your stack probably looked the same as it did before.

As for the average engineer their workflow probably didn't change that much. Maybe they would run the app as a container to test some things out, but they were building their binaries and running their unit tests the same as they always did. Maybe they got their hands on a convenient dev database container their colleague built that came with the schema and test data applied. That was about it.

Enter cluster management tools: Docker swarm, Mesos and Kubernetes. With the benefits that these tools offer containers quickly subsumed the rest of production operations. Now everything is a container, from machine learning to cronjobs. The benefits to operations were well understood, but Kuberentes specifically has begun to make its way in to development environments.

Why? Two reasons.

Kubernetes is really a runtime, like Linux or Windows. It provides APIs that developers want to use in their apps. For example, at Replay (TODO pitch more here), when a user asks for a recording to be replayed our app can decide which container to launch, what kind of hardware it should run on, and just tells Kubernetes to make it so. This is exactly the level that developers want to be thinking at. Developers also want to be able to test this code in dev just like any other code, therefore they want to run Kubernetes in dev.

The other, more common reason is that usually the person setting up the dev environment is the same person responsible for production. They probably set up production first, and it's easier for them to ask everyone to run Kubernetes on their laptops than it is to orchestrate the app in a different way. Guilty.

One way or the other, more and more people are running Kubernetes loally.

Containers are continuing to trickle down further and further in to the deepest parts of our dev workflows. Maybe it starts at your company with an engineer getting fed up with the differences between BSD and GNU sed(1), and adding a docker run command to their Makefile so their coworkers stop running in to platform specific issues. Or maybe someone creates a Docker image for running your app locally, with all of the microservices bundled together. It could even be that engineers see a tool like Earthly, which uses containers to provide fast, reproducible builds and start to adopt it.

Containers are eating all aspects of software development.

When this happens engineers using macOS will become second class citizens, because containers on macOS suck.

Why containers suck on macOS

From Julia Evans:

The word “container” doesn’t mean anything super precise. Basically there are a few new Linux kernel features (“namespaces” and “cgroups”) that let you isolate processes from each other. When you use those features, you call it “containers”.

Docker is just a piece of software that uses these new Linux kernel features to run processes in particular ways. It's another runtime, another set of APIs, just like Kubernetes or Linux.

Unfortunately Docker doesn't try to be agnostic of the platform it runs on, it directly ties itself to Linux. This is where the problems start for macOS. In order to be able to run Docker on macOS you need to have a Linux virtual machine running in the background. When you run a docker container it actually runs in the VM.

This has worked OK for macOS users for most of Docker's life. It provides you a high fidelity development environment to test out your code befores it hits production. But as we use containers for more and more things in development, this high fidelity environment becomes a liability for one reason: disk I/O.

Say you wanted to write a program that updates the copyright comment header on all of the source code. Program is a strong word, it might just be a sed one-liner, but because of the differences betwen BSD sed and GNU sed you want to make it a container. You might invoke it like this:

TODO: fill out this example, show how much slower it is to run on macOS

Every time you want to run a container on macOS that accesses a lot of files you run in to this problem. Precious seconds and minutes waste away. Your laptop fans spin up and your battery depletes.

What can we do?

One thing we could do is just not develop software on our laptops and always do development on a Linux box in the cloud. There are lots of tools for helping to do this, from doing everything in a SSH session to GitHub workspaces. This has a lot of upsides, and when I have a reliable internet connection it's the way I prefer to work. However, we can't always depend on having a reliable internet connection. Not yet, anyways.

You could also just not use macOS. I don't want to do that, macOS is so close to exactly what I want in an operating system.

Could there be a version of Docker that runs natively on macOS, and offers many of the same features, including API compatibility, but runs macOS processes instead? I think there could be. macOS provides the ability to sandbox apps, effectively restricting what a process can do at runtime. This is essentially what namespaces and cgroups do on Linux.

Can you restrict the same things with macOS sandboxing as you can with Linux containers? No. Could we get close? I think so, especially for what we care about for local development. I like containers in local development because they allow me to make things more reproducible. I don't need to restrict access to the system clock, mostly just the filesystem. I'd love to try this (maybe next time I'm at Recurse Center Journal: Week 1). It would be a fun project, if a little frustrating given the lack of documentation around libsandbox.

Help us Apple: you're our only hope

Which brings me to Apple. Every year I look at the WWDC word cloud slide hoping to see a mention of containers. Every year I am disappointed.

Engineers at Apple use Kubernetes. Don't they run in to some of these problems? Aren't they frustrated when they start up a container to build their code and it takes forever, slowing their system to a call, as it copies all of the files in to the container?

Even if Apple is unwilling to implement facsimilies of cgroups and namespaces on macoS, they could at least document how sandboxing an application works, and let the millions of engineers who use their platform come up with the solution.

If the pain outlined in this blog post isn't convincing, I would point Apple to the work that Microsoft is doing to get developers back on Windows. WSL(2), with GUI apps. VSCode, VSCode remote workspaces. The same forces that drove developers to macOS in the 2000s and 2010s and now driving developers to Windows. macOS is a real Unix, and you can use your Unix knowledge to be more productive on macOS. Developers are technology taste makers, I'd argue they really helped Apple during this early macOS period. But now Windows has a real Linux. Everything you can do on Linux, you can do on Windows. Plus printers work.

Apple: make it easy for us to do our jobs on your platforms again, as easy as it was to run a LAMP stack in 2010. As of right now, modern software development on macOS just doesn't work.