As greatly explained by Ruffin White in this talk, streamlining robotics development workflows can be extremely challenging due to the exponential number of target environments, resulting from a combination of many dimensions like sensor models, OSs, platforms, ROS versions, supporting libraries, etc.
In this article, we will argue for the use (and reuse) of development containers, or dev containers, across many steps of the workflow. This low-hanging fruit can make a huge difference in the flow of your development and deployment cycles. Keep reading to learn why.
We all know the hassle of starting anew in a team and a codebase. Install dependencies, get it to compile, and get the ecosystem of tooling to work. And when problems arise, try to reproduce the environment in which they happened (e.g. production or a CI).
Dev containers aim to make this process easier, providing developers with a standardized environment to use as a development platform, as very well explained in the official VS Code documentation. Dev containers make use of docker containers under the hood to provide a reproducible environment that is described by code that can be versioned.
One could think of them as an actual shipping container equipped with all the tools one can need for a given task. Those of us that do our own bricolage work know, one can spend as much time looking for or sourcing the right tool as much as doing the job itself.
You may be thinking, "you just said it, it's for developers". However, we'll argue that developers are just one of the clients for containerized development. We believe they can also serve:
Yes. Dev containers make developers' lives easier. They decrease learning curves and setup times. They allow a team to pay the cost of setting up a tool once and reuse it many times, across different platforms.
For this to happen, though, dev containers must provide a good developer experience. This is something usually overlooked. This includes:
Depending on the domain and the base container used, these are sometimes not provided out of the box. If they are missing, developers tend to prefer their own setup.
As we discussed in a previous article, providing AI agents (think of Cursor Background) with an environment that empowers them to test and iterate is key.
Using a dev container means the chat provided by tools like GitHub Copilot and Cursor have access to the same tooling developers do. Relatedly, features like Cursor Background allow reusing dev containers for the environment AI agents will run on.
In short, as we move to more AI-powered development workflows, the AI experience (AI-dev-exp) becomes as important as the developer's. Namely, AI experience design for development.
Implementing CI systems can be challenging due to the same challenges we have discussed so far. An environment that allows for compiling and running tests needs to be set up in an external system, often in the cloud. We at Focus specialize in the development of HITL (Hardware in the Loop) CI testing environments, a case in which things get dramatically more complex as the testing framework needs to interact with multiple devices using specific drivers that might also require versioning.
Additionally, when things go wrong, developers need to be able to quickly reproduce what happened to be able to fix it efficiently. Those who have experienced the "push, fix, and pray for the CI to pass" cycle know the pain I'm talking about.
By reusing the dev container for running tests on our CI, reproducing the conditions is straightforward: just use your everyday development environment.
Docker containers are great for deployment, greatly reducing the probability of deployments breaking due to differences in the target and testing environments.
Reusing the dev container, or an optimized version of it, for actual deployment in the robot can be a way to easily dockerize your deployment. Your robot can then just have a vanilla Linux OS with docker and a few other tools, whereas most configuration and setup happens inside the Docker container.
If you want to or need to optimize your deployment container, multi-stage builds are your friend.
Yes, as it is usually the case. Firstly, it takes effort to set up and maintain dev containers, customizing them to the needs of developers and assisting them when problems arise. A server failing to give out a given Debian package can break your Docker builds.
The abstraction promised by Docker is powerful, but not one hundred percent effective. If your developers operate in diverse platforms, including Windows machines, you might find yourself hassling to make the image compile, or giving it access to a specific USB device.
In addition to that, containers use more memory than just executing things in the host machine. This is due to many reasons, including maintaining a separate copy of shared libraries and extra management data structures at the kernel and Docker layers. This might mean having to upgrade some of your oldest computers before you can implement this workflow.
In summary, we argue that following these steps will streamline your robotics development:
There are of course some caveats, but in our experience, the benefits of a replicable development environment that can be leveraged in other stages of the development process greatly outweigh the costs.