Brutalist Builds
A Preview
This trailer kicks off a set of posts about my approach to implementing software builds; my design approach, how I choose tools, and what tricks I learned along the way to pass on to others. Included with all of this will be some examples of what I believe to be patterns and anti-patterns.
More of an Art than Science
Brutalism is a visual style, originating in architecture and more recently referring to an approach to web design. I think of it as bold, rugged, lasting, minimalistic but not overly so, but functional. The main goals are utilitarian, no leeway given to superfluous ornamentation.
I start this series with Brutalism as my visual metaphor for builds. Starting out as an architectural style mainly involving concrete, later adapted to describe a web UI style including the newer approaches of Neo Brutalism, I am now taking the mental picture to builds for an allegory. I am striving for ruggedly simple builds that can be chained together and run independently of CI tools locally, yet still be easily run in CI/CD build servers.
Basic Tenets
These are my main style points for approaching builds. Might not be everything, but these 4 tenets capture the big picture.
Remain in your Milieu
Milieu is more of a flashy term for your project environment. For example, Java projects have a different milieu than .NET projects. Stick with and learn your current framework tools. Use Gradle or Maven and learn how they work for your Java project. Don't use Ant, its old. .NET developers need to learn how to use MSBuild and be being ready to leverage tools like Bullseye and Nuke for more complex pre and post build processing. Don't use Gradle in your .NET build as you need a Java install for that.
Adhere to industry standard and any manufacturer's standards for the products and tools you use. If your process is weird, good chance you are doing it wrong.
Part of remaining in your milieu also involved keeps tools current. Typically this is updating to the latest stable or within a minor version update thereof.
Modular, Composable Build Steps
Build steps in general should to follow the UNIX philosophy Unix philosophy - Wikipedia. Each step does 1 thing well and the output is typically the input of at least 1 other step.
Uber and/or large monolithic builds are an antipattern. Break those builds up into smaller components that can be easily chained. A long running build needs to be rerun if it fails near the end. If the build is broken into smaller steps that can run in isolation of each other, the process may be restarted at the beginning of the failed step.
CI/CD platform Independence
Build steps should be able to both be done locally and manually without a CI server. Theoretically one should be able to simple run the build manually for reproducible deployment in a local development environment.
Favor Open Source Tools Over Proprietary
Outdated propietary dependencies often cripple applications and builds if they are not kept up to date. While the same can be said for open source counterparts, cost is removed from the available excuses for holding back on needed updates.
Functional Aspects
I will expand on these and the Basic Tenets above in future articles. Roughly there are 5 main aspects. Notice deployment is not mentioned here, that is a land in its own. The key is working on applying the Basic Tenets with the functional aspects.
Storing and Versioning Source and Assets
This involves how you manage the starting ingredients for your builds. It's your C# source code, SVG images, raw photographic images, raw data, etc. Git, Mercurial, Subversion are well known examples of tools used for storing source.
Git LFS and other tools like Snowtrack (still in beta at time of writing) are useful for managing binary assets such as images.
Build Environment Setup
The proper SDKs and runtimes need to be present to compile code, process images, run tests etc. to generate the product that is to be deployed. How this gets done must be kept simple and repeatable.
Orchestration for Automation
Tools like Jenkins, Travis CI, Github Actions orchestrate builds. These call the build steps and run automated tests. Build orchestration is also concerned with how to manage large projects as smaller subprojects with steps and that get pipelined.
Implementing Build Steps
The build steps gets called by what orchestrates the build. Herein are the makefiles and scripts compiling the code, creating the binaries and packages for that eventually get deployed after successful tesing. Build steps should be designed to run on the CI server and the local development environment manually with little or no changes.
Managing Build Artifacts
These should never be stored with source in version control. These are the Docker containers, Python packages, executable images, shared libraries, etc. that are generated at build time. Once again, favor OSS. Example, Nexus OSS is free, you pay alot for JFrog Artifactory. If its just a Docker or Maven repo, Nexus.
Before I Leave, For Now...
I'll be writing articles on how to take the Basic Tenets and apply them to the Functional Apsects of builds. The stuff we go over here should useful anywhere you are building your project, whether a bare metal agent or in a cloud provider like Azure.
I will be updating this article and the following with updated links and references.