Build tool requirements

Some thoughts about the perfect build world.

Any tool has some purpose to use and requirements.

The build tool’s purpose is to provide a simplified and straightforward way to build a complex code base and produce several completely different artifacts, such as libraries, binaries, Docker images, etc.

The purpose does not include deploying these artifacts, though these artifacts should be ready for deployment. Build tool has to help be focused on making new features rather than on fixing broken build process, i.e., it should be integrated into the development process and save the developer’s time providing a fast and correct output.

As for requirements, I would list next and explain each of them below:

Correctness

Each build run must be correct:

  • If something during build went wrong, the build must be stopped, and the intermediate result must be discarded.
  • The same input must produce the same output each run.
  • If anything like source code, build parameters, input files, dependencies (and/or its versions) have been changed, the build tool must recognize such changes and produce another output.

What’s wrong with make?

Reproducibility

Each time build must produce the same output from the same input regardless of the external environment and/or tools and compilers (and/or its versions) installed in the environment. The build could be run anywhere: on developers' machine, on a build server, in CI/CD pipeline, on stage environment - the output must be the same each time.

Versions of compilers, interpreters, sources code, external dependencies, external tools, input data files must be fixed with tags, commit hashes, or checksums.

Isolation

The build must be run inside an isolated environment with a minimal set of files and tools to prevent any impact from unlisted dependencies. Minimal set of files are the only files that are necessary for producing required output and the only files explicitly listed by the build step (see Explicitness section below)

Reliability

Build tool should be reliable. It should work without failure everywhere, but if it’s broken, it’s broken everywhere.

Speed

The build must be cached; the first one can take relatively a lot of time, not hours, but up to tens of minutes; the subsequent ones must be no longer than several seconds. Any following build should not touch unchanged code or input parameters. It’s essential, especially during debugging when developers have to repeatedly rebuild different parts of the application to check it. Slow builds lead to unnecessary context switching and increasing development time.

Cross-platform builds

Build tool must allow building artifacts for different platforms without changing a lot of code. Sometimes it requires changing compiler parameters, sometimes code change, but the build tool should support cross-platform build with changing tool parameters.

Multi-language support

Some applications’ codebases consist of several languages, each of which has compilers, interpreters, input parameters, and build process.

The tool should provide a unified way to build any library in any language with the same commands and different parameters.

Simplicity

Build tool should be relatively straightforward. Developers have to understand how it works, what it actually does, and why it does what it does.

Developers have to be able to modify build steps and their details during development along with development requirements.

Explicitness

  • There should be no magic behind the build.
  • All build dependencies should be explicitly listed for each build step.
  • Implicit dependencies should not be used.
  • Each build step or target should clearly define what it needs as an input, which dependencies the source code has, which intermediate data files it requires, and the output.