How to Create a Local Development Environment with Docker Compose
As a developer, when working on a service, you face a problem with the working environment. And when I say working environment, I do not think about IDEs, stacks, OS, libraries, etc. I’m thinking about the environment where our services live.
These days, our services are usually packed inside some container and put in some distributed system. Most containers and other moving parts are controlled by Kubernetes, Nomad, and similar orchestration systems.
No matter how different they are, they all control containerized services. They do more than just control containers; at least, we care that they own containers.
The Problem
So, I’m developing a feature for some services. As soon I checkout the code, I realize that the service has external dependencies: other services, some storage(database), messaging system(Kafka).
In the ideal world, I would not care much. I would add an API endpoint and some DTOs and make some calls to external services. To be sure everything works, I add some unit tests.
Later, when I finish the code, I would push it to the working environment to test service integration with other components. And everything is fine.
In the real world, it isn’t like that. Some bugs, wrong assumptions, and freshly discovered constraints exist when you use newly made features.
The pain here is waiting for the code to be compiled and deployed. I make a series of small changes, one by one. After each change, I push the code to the working environment. Then you enter a cycle of commit, push, build, and test, which is very slow if done through CI.
Idea: Replicate the Required Dependencies
Simple. Replicate required dependencies.
But as your dependencies have dependencies, you have to replicate those dependencies, and then those can have dependencies, etc. Does that mean copying the whole system? No, that wouldn’t be smart. We need to figure out the minimal set of required dependencies. The idea is to replicate only a minimal set of dependencies.
The good part is that the replica doesn’t need to be precise as in the working environment. It can work with less memory and less CPU. For example, suppose the working environment has Postgres database version 14.1.1. In that case, you’ll use the same Postgres version but much lighter(with less memory and CPU). The data stored in the replica would be a fragment of the original.
If you are interested in how I do it, check it out on my blog.