I’m fascinated by virtual machines. Along with compilers, Bach’s canons, or the Russian nesting dolls, they induce this unique kind of awe one feels when faced with infinity. A VMWare on Windows running macOS running VirtualBox running Ubuntu running Chrome running jslinux running… have I mentioned my Intel 8085 simulator? Okay then, what if told you that for an entire year I did all my work in a virtual machine? In the age of Vagrant and Docker, this might not come as a surprise, but this story has little to do with these tools.
It started with me pondering a question about the difference between an app and a virtual machine from the user standpoint. We’ll return to it later, but let me digress for a moment. The first time I ran Windows XP in VirtualBox on Linux in 2008 I was exhilarated: no longer I’d have to dual-boot to run some obscure Windows-only program required by the university. Before I could blink, my “lightweight” VM turned into a full-blown system with a web browser, Dropbox, and (maybe) an antivirus. Instead of a contained app, I got a leviathan threatening to swallow my primary OS. And it was slow. I dreaded touching this thing, and pretty soon I’d rather reboot into Windows than start it ever again.
For a while, I didn’t have to use any virtualization software. The projects I worked on ran natively on both Linux and Mac. At some point, one new service demanded the latest (backwards-incompatible) version of the database server. That was when I decided to give Vagrant a try. Vagrant promulgates a belief that a development environment must mimic production as closely as possible. Its approach encourages automation, unifies development environments, and helps surface environment-specific bugs early. However, it’s drastically different from just developing locally.
With Vagrant, the editor runs on a host OS, while the developed program lives inside a headless guest VM. This indirection makes it difficult to use some common IDE features relying on direct access to build tools or a running program. Instead, any interaction must happen over the network. For example, RubyMine (a Ruby IDE by JetBrains) had to build a complex integration that connects to a Vagrant box over SSH and downloads any installed dependencies for local indexing to provide smart code completion and navigation. Only recently this functionality managed to become usable, but I still run into weird issues with it on a regular basis.
Many of the problems with Vagrant can be alleviated by intentionally designing your app to accommodate for this workflow. Unfortunately, this isn’t always achievable with existing software, or when using older technologies that evolved before automation became ubiquitous. That said, Vagrant’s dependency on automation also comes at a cost. In my experience, it takes 2-10 times longer to build an automated environment than to configure it manually once. I also noticed that any obstacle to starting a project significantly reduces the chance of me ever completing it. Building on these observations, I looked for a way to employ virtualization for spinning up isolated environments using the simplest tools possible.
“UNIX is the ultimate IDE” — often repeat some Linux devotees. I wondered what if such “UNIX IDE” was an actual graphical IDE running on my Mac. Indeed, using a tiling window manager, it’d be possible to make application windows feel more like panels. It should be easy enough to configure Linux to mimic Mac shortcuts. With bi-directional copy and paste between the guest OS and the host, a browser stub that opens web pages on the host when clicking on a link in the guest, and a bunch of shared directories, the illusion of working with a local IDE would be almost impeccable. At the same time, such IDE would have direct access to the developed program and contain any necessary tools without bloating my primary OS.
I built this environment and used it for over a year working on different Wildbit products and personal projects. I kept dedicated VMs for DeployBot, Postmark, and Beanstalk; as well as a labs VM for running random experiments. Despite my initial concerns, VirtualBox and the computing powers at our discretion have come a long way since my first Windows VM experience. With 4-8 gigabytes of RAM and 2-4 CPUs allocated per VM, performance wasn’t a serious problem, even when programming for JVM or running multiple microservices. Through this whole time, I had practically zero development tools (e.g., XCode, JDK, Git) installed on my Mac.
I still remember when macOS Sierra came out, and I did a clean setup on the release day, just to prove that I can be back to work in a matter of minutes after install. Worked without a hitch: soon I’d be watching Picture in Picture videos on top of my virtual IDE running in full-screen mode. Say what you want, but 2016 was the year of Linux desktop for me.
It was in no way perfect though. I have a 15″ Retina MacBook Pro which I connect to a 27″ Thunderbolt Display at work. Most Linux apps don’t handle runtime changes in screen DPI gracefully, so I had to choose between restarting the VM each time I change displays and not having HiDPI support at all. I picked the former, which was ultimately good for my eyes but not so kind on my nerves.
Mimicking macOS shortcuts isn’t easy either. One can naively assume that just re-mapping
Cmd would take you at least half way to the goal. However, this ignores some important details, such as using
Ctrl-C to terminate a running program and
Cmd-C to copy. Unfortunately, the subtleties don’t stop at this, so in the end tweaking shortcuts turned into a never-ending endeavor, with some issues being practically impossible to fix. For one, I never found a good way to use the same shortcut for switching keyboard layout between English and Russian both in macOS and in VM.
There are many more problems, of course. A full-blown graphical Linux VM takes at least 8GB of disk space on the host (though, 15-20GB+ is more realistic). Thus, keeping more than a few VMs at once can be tough when you’re on a laptop with limited disk space. If that wasn’t enough, 3D graphics programming is almost impossible because of driver limitations and some apps don’t play nicely with tiling window managers. VirtualBox upgrades also proved to be problematic. I still haven’t updated to 5.1 because of some outstanding regressions.
Anyone who has developed software for their personal use knows how it goes. With time, maintenance becomes a drag. When you’re your only user, there’s little motivation to improve things that aren’t obvious roadblocks. You get used to minor bugs, but annoyance slowly builds up until at some point you’re not as excited about your precious invention anymore. When I reached that milestone, I went back to RubyMine and Vagrant for work (Docker on newer projects). Once again, my Mac exploded with various development tools: Homebrew, Git, OpenJDK, MySQL running in the background, you name it. It’s still a mess. But it’s our mutual mess.
There are several observations that I’d like to share before wrapping up this post. First, the malleability of the GNU/Linux desktop remains astounding. Testing how far it would bend to my will was one of the primary motivational forces behind this project. I feel like we don’t appreciate this enough.
Second, I was reminded of a disconnect between the pompous slogans of emerging developer tools and the prosaic reality of legacy software. One can dream up an intricate system that auto-scales to thousands of machines, but chances are, it’s still executing sed under the hood to modify a text NGINX config. Think about it next time you SSH to a Docker container.
Finally, developers tend to misjudge productivity gains and hits imposed by tools. My unorthodox virtualized IDE didn’t make me less productive (if not the opposite). Neither I noticed any significant changes in my productivity when I switched back to a more traditional development setup. “No Silver Bullet,” indeed.
I still use my virtualized IDEs for little experiments and running Linux-native software (FFmpeg, ImageMagick, OpenVPN, etc.). They work well for that. If you’d like to try this configuration or you’re curious to glance over the configs, check out the project on my GitHub. I want to conclude this article by encouraging the reader to experiment with their tools. Even if Brooks is right and the tools can’t make us much more productive, curiosity will.