Develop inside a container for easy developer onboarding
Ep #7: Use containers to create developer environments.
In this article, I'll first introduce what it means to develop inside a container, what benefits it provides compared to other solutions and why it is my favourite way to onboard developers to any project I work on.
But before we dive deep into dev containers, let me explain what problem it solves and what other alternatives I tried in the past.
Problem statement
It's your first day at a new job. You got a brand new laptop and now you can spend your first week setting it up like you want. You need to install one or more language runtimes, your favourite IDE, and an infinite list of tiny command line tools. Then finally you need to configure all these bits and pieces according to your taste. There is so much to do. It might also be fun to choose your tools and configure them the way that you want. The first week is meant for this kind of task together with any mandatory training. Great, no pressure.
What if after six months you have to move to another team? What if you are working on a personal project on your weekend? Most of your tools will probably stay the same, assuming that you are still coding in the same programming language. What if your next team or your next project has a very specific set of tools that you never used before?
A similar issue might happen if you are the main contributor to an open-source project and you want to make it easier to onboard other people.
All of these last scenarios are way more frequent than changing jobs but they share the same problems, so you would expect them to have a similar solution. In order to make our discussion easier, from now on I'll call this set of problems "developer onboarding".
Need for automation
You have two alternatives here, documentation or automation.
Either you write a long readme file with the installation instructions for each and every component that you need or alternatively, you provide some sort of automation that do the job for you.
Like most Engineers, I am a very lazy person, so if there is anything that I think I'll have to do in the future, I'll try my best to automate it so that I don't have to do it again. Automation over documentation is a very easy choice for me.
How can you automate developer onboarding to a new project or a new job? Can we use a generic automation solution or is there anything specific for this task?
Some alternatives
As I made clear in the past in a previous article called Taskfile: a modern alternative to Makefile, I am not a very huge fan of Makefile. While I think Taskfile is a better option, I still believe that there are better solutions out there for onboarding developers to a new project.
A while ago I discovered a tool called Homebrew Bundle for macOS. If you, like me, have a MacBook as your main workstation, you can list all your tools in a Brewfile and have Homebrew install them all at once. The problem with the Homebrew though is that it installs those tools globally on your machine. While this might be great for easy onboarding to a new job, it doesn't fully cover the issue of different projects using a different set of tools or even worse different versions of the same tool.
In a previous article, I discussed how I use Devenv to install my project dependencies. I believe that's a better alternative to Homebrew Bundle since you can provide a different devenv.nix file for each of your projects to have project-specific reproducible environments.
While I believe that Devenv definitively solves this problem the way I wanted it, it comes with the complexity of installing Nix. You might be thinking, that's something that you do once and forget about it. That's what I thought until I had to update my laptop for the first time. For some reason, I have to reinstall Nix from scratch every time I update the operating system. That has been happening more and more frequently. So I have started putting off those updates because I didn't want to waste time reinstalling Nix. I'm not sure why that's happening, I don't exclude that is due to my lack of knowledge about how Nix works internally. While I could spend some time learning why that's happening, I am not really interested in that at the moment.
Is there something else that doesn't require learning Nix or Devenv but still provides the same level of flexibility in creating a per-project development environment?
I think I found the solution in Dev Containers
.
Spoiler alert, in a future article I'll explain how to configure Dev Containers and Devenv together to reap the benefits of both solutions and none of the downsides of installing Nix. For now, I'll consider dev containers to be an alternative to Devenv only because explaining my final setup would have made the article too long to read.
Containers to the rescue
In the past couple of years, with Docker and containers in general, we already learned how important it is to reproduce your production environment on your laptop when running applications. You can't say anymore "It works on my laptop" as an excuse for something broken in production. Local development and production have never been so close.
What if we could use containers for onboarding developers as well? One developer could set up a development environment with a container and some other settings and everyone else will be able to replicate the same environment with no effort at all.
Wouldn't it be great if the only thing you needed to get started on a new project would be Docker or a Docker alternative like Colima?
Dev container
Discover Dev container, an open-source standard created by Microsoft that allows to develop inside a container. No more endless Readme with the installation instructions for every tool you need. Just spin up your dev container and you automatically get everything you need to get started.
The concept of dev containers is a specific implementation of a more generic concept called development environments (or dev environments). There are multiple solutions that provide remote dev environments on the cloud like GitPod but only a few of them like GitHub Codespaces provide full compatibility with the dev container
specification from Microsoft.
Supporting the dev container
standard means that the same dev environment definition can be used either when running on your laptop via Docker (or any other Docker alternatives like Colima) or when running on the cloud. The freedom of saving money by running everything on your laptop I think is something to keep in mind. Especially if you, like me, have a powerful laptop that allows you to run most of your setups locally.
So in my opinion, if I even need to develop on the cloud, GitHub Codespaces is a better option than GitPod. It hasn't happened often so far, but I am happy that I have the option to move the cloud without rewriting my setup.
Also if you only need to develop on the cloud sporadically and you are happy with a 2 cores instance, GitHub Codespaces offers 60 hours of free usage per month. Free usage decreases the bigger the instance you choose.
Getting started with dev containers
Getting started with dev containers is quite easy. You only need to provide a file called .devcontainer.json
in the root folder of your project or at .devcontainer/devcontainer.json
and then use your IDE (like VSCode) to "open your project inside a dev container".
A very basic devcontainer.json for a Golang project is provided below.
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:1-1.20-bullseye"
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "go version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
This file was automatically generated by VSCode using the extension Dev containers and choosing Go
from a list Dev Container Templates. There are a lot more templates available that make it easy to get started.
I kept here all the comments in this file because it makes for a great way to explain all the possible features provided by dev containers and also it provides links to more documentation.
From what you can see above, you can customise many parts of this config file:
you can choose which base container image to use between the one already provided by Microsoft or you can build your own container image with a custom Dockerfile. You can put anything you want in this container, from tools to runtime frameworks, and libraries.
or you can also add "Features" to your dev environment. A Feature is a software runtime or a tool that can be installed automatically without packaging it inside the container image. The list of Dev container features is quite extensive and includes the likes of Nix, Python, and Vim, as well as many other tools. This config saves us from packaging everything into the container but at the cost of installing those dependencies when the container starts.
or you can forward ports from the container to your local machine
or you can run a custom command after the container is created by using the setting
postCreateCommand
or add some "customizations". These settings are specific to the tool used to start your dev container. If using VSCode, you can provide the id of extensions to install when the container starts. Alternatively if using GitHub Codespaces you can specify extra actions like opening files or permissions. There is a lot more on GitHub customizations in the GitHub Codespaces documentation.
or change the remote user inside the container
I won't go more into detail about how to use dev containers in this article since Microsoft created a great tutorial at Developing inside a Container.
Conclusion
I hope that I convinced you into looking into dev containers for improving the onboarding of developers into a project or a new job.
I'm planning to write more about dev containers in the coming months. As you can see there was already a lot to unpack here and there is definitively more I can provide you from my own experience.
Stay tuned to learn more.