Working with distributed teams in a polyglot engineering environment that encourages a wide range of technologies can often become overwhelming.
Focusing on fast time to market, over perceived engineering excellence is a key point raised in What is the best Tech Stack for my Startup in 2021? Software Architecture Series, Part II. Embarking on a distributed engineering effort does however come to a series of challenges you have to be aware of early on.
Each language, or platform, has its own way of handling basic tasks such as linting, formatting, compilation and more complicated tasks such as running the test suite or generating migrations.
While the technology and commands are different, the engineering and operational flow usually stay the same. Regardless if we write in Python, PHP, Go or Ruby, we want to quickly:
1. Check syntax & run a static analyser on our code to spot bugs
2. Check and enforce objective code quality (PEP8, gofmt)
3. Run the test suite and report results
4. Run test coverage to understand the reach of our test suite
5. Rebuild docker images
The list goes on and on, yet of course, there are no two systems, not even ones written in the same language, that share the exact same commands.
Switching context, from one project to another, is a natural responsibility of a Software Architect. Your job is to not only plan but also maintain all the individual pieces that make up the entire software stack.
Even in a small Polyglot environment, with three different platforms, each written in its own framework, executing the five basic tasks from above can become tricky and prone to errors.
Having to take even one minute to look up how to run the coverage tool on the current Rails app, vs a Django one you were working before, causes a snowball effect in productivity due to unnecessary context switches.
You might rely on a cheat sheet of commands, but even that can lead to issues if such a list is not being actively maintained by everyone involved. At best, you end up with each developer having notes on their computer with frequent commands. Hardly a productive, collaborative setup.
Makefiles represent the ultimate engineering cheatsheet. In one file at the root of your repo, literary called “Makefile”, you can define any number of commands.
These commands are executed based on a shorthand name, which gives the entire experience a seamless flow. You execute the tasks with the shorthand `make X`, where `X` is one individual task, or a group of tasks, defined in the Makefile.
For the list above, using a Makefile we now can invoke each task like this:
# 1. Check syntax & run a static analyser on our code to spot bugs
make lint# 2. Check and enforce objective code quality (PEP8, gofmt)
make fmt# 3. Run the test suite and report results
make test# 4. Run test coverage to understand the reach of our test suite
make coverage# 5. Rebuild docker images
Learn by example
In engineering, often the best way to master a new tool is by diving straight down to the code. Let’s take a very simple Makefile, built for one of our many little code challenges at CHT over at labx.
To make this easier to follow, I’ve gone ahead and commented on each interesting line to showcase how to use variables, commands and even group multiple tasks together.