Midjourney’s rendering of Octocat + Moby
Midjourney rendering of GitHub’s Octocat + Docker’s Moby

Overview

Alternate Title: The Death of “But It Works on My Machine!”

This is an opinion piece based on my recent experience with GitHub Codespaces. I’ve written about several tools in the past, but few (besides ChatGPT) have had me raving this much in recent memory.

TL;DR: Codespaces is the breath of fresh air that Docker promised to bring to development ~10 years ago. Container-based development, with all of the tedium that comes with it— fully-automated. Everything from the building and storing of images, management of container engines, cloning of repos, connection to IDEs, redirection of OAuth requests, and probably dozens of other features I haven’t scratched the surface of, are no longer a burden. All you need is a browser.

The Local Dev Environment

Starting a new job, contributing to FOSS projects, building personal tools – each case carries with it the drudging feeling of “ugh, how do I get started.” Contribution guides are often lacking, and it can be tough to blend them with your development setup and workflow. I’ve been attempting to standardize my setup process for 10+ years now, and until recently I felt that I’d made “meh” progress at best. I’ve struggled to put it into words, but this diagram from containers.dev lays it out perfectly:

dev vs production

The so-called Outer Loop and Production have been my primary focus as a DevOps Engineer, as business revenue and reputation tie directly to them. No production = no money. As a result, the Inner Loop has often been neglected. It’s one thing to build a CI/CD pipeline in Jenkins, Bitbucket, Bamboo, Azure DevOps, or GitHub, (I’ve been doing this for years), but another thing entirely to have a local setup that is easily replicable and stable. On top of that, even if my job were to solely revolve around developer experience, I’d have one hell of a time trying to build a gold-standard setup that fits everyone’s needs.

Working on the Outer Loop is like building an assembly line; the process is generalizable, repeatable, and there is usually a single output that determines success. You monitor the process and check the outputs for quality, but your primary function is to keep it moving.

Modern Times
Under the hood of every Jenkins server
Modern Times
Chaplain, the spiritual predecessor to Hudson and Jenkins

Working on the Inner Loop is akin to building a fallout shelter; it is an environment in which you will reside for an indefinite amount of time. You can try your hardest to plan for what you will need in the future, but you can’t fit every tool inside without cluttering the whole place. Occasionally, you will need to venture out into the world to gather new resources, hoping that you don’t bring something tainted or bug-ridden into your domicile. You do your best to keep the place clean, but tasks of urgency and botched experiments lead the place to fall into a state of disrepair (or worse). Years pass, and you give up maintaining your mess and burn the whole thing down, only to rebuild it back in place.

Except your blueprints were last updated years ago.

And you’re missing half of the parts.

And you forget which wire is hot/neutral.

And your glasses are broken.

And you wonder how you made it this long at all.

Time Enough
“My poor 32-bit machine at 03:14:07 UTC on 19 January 2038”

Oh wait, we’re still talking about software, right?

Hopefully, I am not alone in this struggle, and my journey resonates with some. The point is, building and maintaining local development environments can be a burden, even with the modern niceties of containers and package managers. If you care to compare, here is an off-the-cuff listing of how I’ve maintained my dev setup over the years:

  • Jotted on a piece of notebook paper how to set up Windows 7 Ultimate (2010)f
  • Found Ninite, which scripted 80% of the process
  • Toyed with virtualization, spent countless hours building a VirtualBox golden image
  • Realized working in vBox sucks (or did in 2013), and wrote a Powershell setup script for my laptop
  • Built an auto-recovery partition with an autounattend.xml to refresh the laptop regularly
  • Realized that the previous setup was a clunky and fragile mess
  • Switched back to vBox with the autounattend and setup script to build dev environments from scratch
  • Found Chocolatey, which replaced Ninite for me
  • Discovered the power of containers, but struggled to integrate them into my workflow
  • Experimented with Docker Desktop, but hated the fact that it used Hyper-V
  • Learned that Vagrant exists, switched to prebuilt images
  • Found /r/homelab, bought an r720, set up proxmox
  • Experimented with LXC, struggled to understand it
  • Somehow thought golden images were a good idea again, replicated on pmox with Windows
  • Switched to my shitty autounattend + script again on pmox
  • Moved to ubuntu + docker + portainer – my first time feeling productive with containers
  • Configured zfs wrong, nuked my proxmox setup
  • Tried unraid, bought unraid, and repeated my cycle of misery all over again
  • Moved through several AWS and Azure services (EC2, Workspaces, and VMs)
  • Frustrated myself trying to connect cloud resources to local resources
  • Learned k8s, had some fun with kubefwd connecting local resources to clusters
  • Learned that k8s kind of (actually, really) sucks to maintain
  • Tried Fedora Silverblue, frustrated myself again
  • Built setup scripts for WSL 1 & 2 with Ubuntu, Fedora
  • Started using Podman on WSL
  • Banged my head against a wall with a broken WSL setup
  • Discovered Windows sandbox
  • Looped all the way back to my powershell setup script
  • Actually enjoyed windows sandbox, but still struggled to keep the script maintainable (a previous guide I wrote shows me using this in January)

Typing out that list alone made me exhausted– figure that between each of those bullet points there are probably 20 hours of struggle. Through each of these, I’ve waffled with one “do it all” config, versus slim configs for each dev situation (one for python, one for node, etc). I’ve tried containerizing my work, throwing it in a VM, running it on bare metal, and everything in-between. That nagging feeling that there has to be a better way has never left my head. Until now, with Codespaces.

Containerization

*(skip if you don’t want a brief rant/history)*

To understand Codespaces, a basic understanding of containerization is helpful. Containerization promised to simplify development by enabling developers to package applications and their dependencies into a single distributable unit, called an image. These images all rely on a shared linux kernel, making them smaller and faster to spin up than conventional VMs. Docker was at the forefront of this movement, offering a set of tools to create, deploy, and run applications in containers. However, Docker fell short in implementing a truly seamless integration with IDEs. The setup process was often time-consuming, clunky, and prone to breaking, especially on non-*nix OSes. The learning curve was steep for developers without a background in virtualization. As I mentioned before, I struggled with this heavily (especially as a Windows user). Docker Desktop eventually made this process easier, but with the caveat that it was no longer free to use for large businesses. Of the places I have worked, not a single one has entertained the thought of paying, meaning devs would be restricted to the CLI, or another management tool such as Portainer. The overhead of configuring, troubleshooting, and documenting this was, and still is, a PITA.

The following is a dramatization of the onboarding process as a developer:

Manager: Okay here’s your ThinkPad. We use containers! Set up your machine so we can start building.

Developer: Alright, you know I’ll need local admin for that, right?

Manager: Sure, we’ll get you that!

— 3 weeks later —

Internal IT: Admin access granted

Developer: Attempts to install docker, fails due to missing WSL

Developer: Attempts to install WSL, fails due to Windows 1903 requirement

Developer: Attempts to update Windows, blocked by GPO

— 3 weeks and 3 support tickets later —

Internal IT: Due to the demands of a very persistent developer, the whole company has updated to Windows 10 1903. We will be swamped fielding support tickets related to this for the next year.

Developer: Whew, glad that’s not me! Downloads and installs Docker

Manager: No, wait, not Docker Desktop! We don’t want to pay for that and we don’t want to get sued

Developer: Uninstalls Docker Desktop

Developer: Attempts to install Docker CLI

Developer: Wait, where is the installer for Windows?

— The CLI / engine is only officially distributed for *nix —

Developer: Oh goddamnit, I have to set up Ubuntu on WSL just for this

Developer Goes to install Ubuntu from the Microsoft Store

Developer The Microsoft store is disabled by GPO

— 4 weeks and 4 support tickets later —

Internal IT: Due to the demands of one developer who claims the Microsoft Store is “mandatory” for his job, we will be enabling it organization wide. Please do not install Candy Crush. All microtransactions and jelly beans will be the property of ConglomerateCorp™

Developer Finally installs Ubuntu for WSL

Developer sudo apt-get update && sudo apt-get install -y docker.io

Developer Alrighty, finally time to work…

All of this, and we haven’t even gotten to the IDE linkage, VPN issues, or other dependency hell scenarios… 11 weeks have gone by, not a single line of code has been written, and the anxiety of having to repeat this process if your laptop shits the bed looms above your head.

Stains the Dog

I’ve gone through this hypothetical in some fashion at least 5 times now. Some parts get better, but the dread never fades.

Matthew McConaughey

Codespaces

If you’ve read the entirety of this article, please take a smoke break, or maybe just scroll on TikTok for a bit – you’ve earned it. If you’ve skipped to this section, just know that the world of local development on corporate machines has not been fun. And Codespaces will change that (or the open source spec, https://containers.dev). What exactly is it, though?

GitHub Codespaces has managed to bridge the gap left by Docker and other development tools by providing a smooth, browser-based (or desktop-based, if you prefer) ephemeral environment. With Codespaces, devs can quickly spin up a container on Azure and plop directly into it using VSCode. The setup requires only a browser, a GitHub account, and a few JSON/YAML files.

The entire scope of the development environment is contained within a folder at the root of the repo, .devcontainer. If the folder does not exist, the Microsoft Universal image is used, which has many modern tools and frameworks pre-installed. It’s as easy as clicking the big “Create Codespace on Main” button. 10 seconds later, VSCode opens with the repo already cloned, all dependencies installed, and… that’s it. You can just work. All app ports that you’d need to access are forwarded to your local machine. Debugging feels as natural as it does locally. GitHub keeps track of changes made in the repo so you can commit without even having VSCode open. The container pauses after 30 minutes of inactivity to minimize costs. When you’re ready to resume, it takes 10 seconds to pick back up where you left off. If you need additional custom dependencies, you can pre-build your own image, and GitHub handles versioning and pushing it to the registry behind the scenes. I’m struggling to convey how easy it is.

As an example, I’ve written a .devcontainer definition for a utility called “granted”, which handles AWS SSO credentials in a very seamless way. The result is that I can go from zero, a brand new machine with just a browser → deploying to an AWS dev account in the time it takes to make a cup of coffee.

codespaces-aws-granted https://github.com/robbycuenot/codespaces-aws-granted

It has never been so easy. Gone is the tedium of setting up Docker, configuring proxies, finding workarounds for Cisco VPN in WSL, and every other menial task that burns developer hours without accomplishing a single task. Call me a fanboy or a Microsoft shill all day; this is a gamechanger.

Conclusion

GitHub Codespaces has successfully realized Docker’s vision for efficient developer environments by offering a fast, easily distributed, and seamless solution. By addressing the pain points of previous dev tooling and leveraging containerization, Codespaces has changed the way developers can work. As the development landscape continues to evolve, we can expect Codespaces to play a significant role in shaping the future of software development.