VSC vs. PyCharm: Developing inside Docker Containers
Using containers in development seems to be the go-to these days. It aligns all the developers to the same development environment as well as sets a unified backdrop that resembles production for your code to run in.
In the vast majority of organizations, developers install their development environment locally. That would mean environment variables, Debian or Mac packages, global tools and so forth. Language specific dependencies are also installed. For python, that means virtual envs and pip packages. Things are similar in node, although as far as I know, there are no node envs yet.
When something changes, then a development setup document needs to be upgraded. For more advanced teams, maybe there is a github scripts style directory housing setup.sh, update.sh, build.sh, etc.
This is where docker has entered the mix. Dockers allow us to create a virtual environment of sorts with ease, and commit the recipe of creating these environments to our source code management together with the code changes that rely upon them. Unfortunately, there are still problems with this approach.
Some developers have Macs, some Windows and some linux. Production is usually run on one of these (mostly linux). Docker works well on linux, but is lacking on Mac and Windows. Dockers also add complication when trying to debug and usually relies on debugging protocols as if the code is running on a remote machine. That is why docker alone is not enough to enable developers to enjoy a complete setup with ease.
VSC and PyCharm both offer container integration in their IDEs, both are nice and both are lacking. Before comparing, below is a brief list that summarizes my expectations from a development environment.
What do I need
- I’d like to share my environment with my team, and have this environment resemble production.
- I’d like to have different environments for different branches of code.
- I’d like to have services that use different environments to be able to co-run on my machine.
- I’d like my code to access the web, my database or file system, message exchanges, etc.
- I’d like to debug my code with breakpoints.
Basic Requirements
For both pycharm and vsc docker integrations, a development environment requires docker installed and access to whatever image repositories that code in the organization runs in. I prefer also working with docker-compose and both solutions have support for docker-compose.yml files as well.
VSC
Visual Studio Code has an excellent plugin for working with containers. The approach in VSC is to have the development experience be identical to that of running on your local host. Here are a few implications of that approach:
- Integrated Terminals open inside the container, and not on your host.
- Installed Plugins are installed inside the container, and not on your host.
- VSC handles the volume mapping that allows changes in your code to actually persist on your hard drive, where you cloned your repo.
- Debugging happens as if you are debugging your host, through hiding the remote protocols that are actually occurring between your IDE and the code in the container
- There is a container running in the background as long as the IDE is open.
Configuration
The entire environment is saved in a devcontainer.json file which specifies:
- What container is being used. This file supports several options like Dockerfiles, Docker images, running containers, or the best option in my opinion: docker-compose.
- Additional Volume Mappings
- Environment Variables inside the container
- Which plugins to install (like python for vsc which includes a cool testing workbench)
- Which ports to expose
- … and many more options.
Debugging
VSC has a launch.json with every workspace. When opening a container with a .devcontainer file, then a workspace can be opened as if running as usual with a local host, and the launch.json actually looks the same, so no difference there. Whatever you can do with a local python installation is still possible within a container.
Sharing Your Environment
Once a devcontainer has been created, it can be committed and loaded by anyone else who has vsc installed with the remote container plugin.
For a new hire for example, given that their host has access to the images or docker-compose.ymls specified in the file, the experience will be like this: They will clone the repo, and then select the repo as a remote container. The environment will be brought up. They will see the container being created for the first time by building the images (if specified to) after which the container will be spun up with the relevant arguments, and then vsc plugins installed inside the container. At the end of the process, the developer will look at an IDE that has all the requirements and packages needed to start developing.
Switching Branches
If a branch has a different docker image, then you need to rebuild that image and then reload the IDE. Some convenience commands like “rebuild” exist, and you need to wait for the container to reload each time to continue working. The build steps themselves take less time each run since both branches, and therefore both images may be in your docker cache.
Multiple Services
If you have different services with different environments, you will create a different devcontainer file for each and have several IDEs open.
PyCharm
The approach in pycharm is a tad different. In pycharm, your IDE is the same IDE with the same plugins. What you change is the interpreter and the run configurations. Some implications of this approach:
- The integrated terminal is your local host
- No containers are running until you hit F5 (or equivalent run button)
- The REPL that opens is inside the running container
Configuration
There are two things that need to be configured: interpreters and run configurations.
To begin, a developer must create an interpreter that is inside a container. Interpreter configuration can be based on a dockerfile, docker image or docker-compose file. After selecting an interpreter, pycharm starts analyzing your code and does what it usually does. PyCharm behind the scenes create a container to run the intellisense properly to resolve dependencies that are installed inside your container.
Run configurations use specific interpreters to run, as well as specify other elements like environment variables, command line run options, open ports, etc.
Debugging
When hitting the F5, then a container is spun up according to the interpreter selected for the run configuration. If things haven’t been built yet, they will be now.
Pycharm then executes the code inside the container, and activates the remote debugging automatically so the experience is similar to running locally. The main difference is the amount of time to stop/start each run which adds some overhead for recreating the container each time.
Multiple Services
You can have several interpreters, include node interpreters, inside pycharm and several run configurations that rely on different interpreters. You can also leverage the compound run configurations which run several run configurations at the same time.
Sharing Your Environment
While run configurations can be shared easily, interpreter configurations are not.
This means that each developer will have a few steps to configure their interpreters before they can use the shared run configurations.
Unit Tests
Once a global interpreter is selected for your project, running unit tests in the test bench behaves pretty much like they are run locally. You can run specific tests as well as the entire test suite.
Summary
All in all, VSC has the better alternative.
First, the PyCharm option costs money while the VSC experience is free.
Second, the integrated terminal in VSC is so convenient. It allows you to run whatever you want inside your env like additional tools that have the same requirements as your code, open a REPL and simple tests things out.
Finally, I’d rather load the container once when I start working and have the processes run inside an already running container over waiting for new containers to spin up each run.
On the other hand, for developers who are used to PyCharm’s rich feature-set, like the myriad of refactoring functions, they may be put off using VSC in general.
Other Issues
There are other issues that you may encounter that I haven’t discuss here like having several services in one repo and what that will do to the source control plugins in VSC inside a container. At the end of the day, I find myself having several VSC instances open, for each service as well as one for simply navigating files and running a terminal in my own host.