The Theory of Evolution applied to computer science...
Throughout my career as a developer, I have seen many complex areas of computer science gain in maturity, formalize more and more structured notions, allowing us to manage things with a higher semantic level, freeing us from low-level details, allowing us to focus on more value-added tasks and ultimately becoming a mere commodity.
Think of Maven, which first standardized the life cycle of a Java project (sources layout, dependencies management, build, tests, release, ...).
Before that we had ANT or Makefile. The main purpose of these tools was to build the javac
command line to build the Java project. They offered a very low level of abstraction.
Even though the underlying Maven and Gradle implementation still produces a possibly complex javac
command, you don't have to worry about it anymore. The Java build is no longer a topic today.
The Infra-as-Code tools have also followed the same evolution:
- in the beginning, infrastructure provisioning was done with handwritten shell scripts,
- then came semi-imperative languages such as Chef or Ansible,
- we now have a huge range of IaC tools, where you simply describe the infrastructure you want, and the tool takes care of applying it (iteratively) to your live environments to match what you have specified (Terraform, CloudFormation, Kubernetes, Helm ...).
We could hardly implement Gitops without these latest generation tools.
Front-end development also followed the same path...
- There was a time before jQuery (and yes, I was there :)). Back then, you had to manage the DOM manually, struggle with browser-specific APIs and implementations, and deal with under-tooled JavaScript code.
- Then jQuery appeared, which brought a first level of simplification. It gracefully abstracted some browser specificities, introduced a more efficient way of handling the DOM, but still needed to be handled, and jQuery offered no framework
- Today with Angular, React, Vue.JS and others, you have more mature frameworks, which offer a very high level of semantics to develop your web applications. You hardly see the underlying mechanisms for interacting with browser APIs...
...
So why on earth isn't this (yet) the case with CI/CD tools?
Why are we stuck with tools that still expect us to write low-level commands to build/test/deploy our code?
# run npm lint
npm ci
npm run lint
# run Python unit tests with JUnit output report
pip install .[tests]
python -m pytest --junit-xml=junit.xml tests/
# Terraform plan & apply
terraform init -backend-config prod.tfbackend
terraform plan -var-files prod.tfvars -out prod.tfplan
terraform apply prod.tfplan
# deploy my prod env with kubectl
kubectl apply -f prod.k8s.yml
Over and over again… why?
What would it look like?
What would it look like, that new-generation CI/CD tool I have in mind?
Well, first of all, I wish I could say:
“““
Dear CI/CD tool,
- Here's my Git repository,
- it contains Angular (frontend) and Python code (backend),
- I wish to containerize the Python code with Docker (or even better with Cloud Native Buildpacks),
- I'd like to provision my Kubernetes cluster with Terraform,
- I planned to deploy my frontend to AWS (S3 + CloudFront),
- I planned to deploy my backend to my Kubernetes cluster,
- I run frontend acceptance tests with Cypress,
- I test my APIs with Postman/newman,
- ...
So please, do the necessary to:
- build and test my code,
- analyze my code and scripts (please select the most appropriate tools, I'm no DevSecOps expert),
- package my code,
- provision my environments,
- deploy my code to my environments,
- run my acceptance tests
- ...
- oh! by the way, I want to work according to a basic feature/branch workflow ”””
Instead I still have to write those dumb npm ci
, python -m pytest
and terraform init
commands...
Why should we not be satisfied with current tools?
There are lots of topics that current CI/CD tools have no opinion on (and therefore don't offer any solutions):
- The different Git workflows.
- What is the difference between a pipeline triggered by a commit on a feature branch and a commit on the master branch?
- What is the semantic meaning of a Git tag? Is it legitimate to trigger a server deployment on a Git tag?
- What mapping is recommended between my environments and my Git branches?
- Is there a preferable/more efficient order to sequence its build, unit test, analysis, packaging, deployment, acceptance testing, publish tasks?
- How do you balance the need to shorten the developer's feedback loop with the need to ensure code quality and security?
- ...
The consequences of this lack of opinion are manifold.
At the level of the practitioner first:
- Each of the subjects mentioned above is a real subject, which can be implemented correctly or very badly. A bad Git workflow, for example, can have a disastrous impact on a team's productivity. Not trying to provide a solution in the tool and letting each DevOps engineer manage it is the guarantee of very disparate and often questionable implementations from one project to another.
- How long does it take to become a good DevOps engineer? Example: the day when a practitioner understands why to use
npm ci
rather thannpm install
(or why to commit thepoetry.lock
file) is an epiphany. But why should each practitioner make this discovery by himself? Why isn't this intelligence coded once and for all in the tool, for everyone (even beginners)? - And even an excellent DevOps engineer wastes a lot of time in each new project redoing the same thing over and over again. Are there enough different ways of building a Java project, running pytest tests, building a Docker image, deploying a static website in an S3 bucket that we can't put it all in common ?
But there is also a major consequence at the level of our entire profession: it is the absolute lack of standardization. Each project has its own CI/CD implementation. And even 2 projects relying on the same CI/CD tool can have very different pipelines.
I believe that the time has come for CI/CD tools to raise the semantic level of the notions they handle, to have an opinion on these questions, and to bring at least proposals (if not solutions).
A first attempt...
If you're using GitLab and GitLab CI/CD, to-be-continuous could help. It's an open source project providing DevOps, ready-to-use, composable, configurable and overridable templates that modestly tries to tackle those questions:
-
Git workflows:
to-be-continuous
supports main-branch based workflows and variants (feature-branch, Gitflow) - generic pipeline stages: attempt to standardize and optimize the ordering of CI/CD tasks
- DevSecOps promotion & prescription: each
to-be-continuous
template comes with the major (open source) DevSecOps tools related with the technology - optimized development cycle: how to shorten the developer's feedback loop and ensure code quality and security at the same time
- and of course no more low-level code writing ;)
Get started with to-be-continuous
A few take-away links to get started:
- available templates
- understand key notions and concepts (possibly applies to any CI/CD tool)
- kicker: an interactive online configurer for your project pipeline
-
project samples: code projects using and combining
to-be-continuous
templates into production-like examples
Top comments (1)
I agree that tools can evolve much more but, at least, some of them give you the opportunity to encapsulate that commands in a higher abstraction level. For me, the main point is not keep ourselves in the way of repeating the same thing over and over and think the way you can provide the framework to your use case that evolve that way of work. I mean, your pipeline can have a "package" step and you can encapsulate multiple "package" types (java package, npm package, python package, .net package). Maybe I'm looking the question from other point of view. Maybe you are looking for out-of-the-box tools that do those kind of things. I'm seeing the point of build that templates, tools, frameworks and reuse them.