CI/CD Doesn't Stand for Continuous Chaos
I've built CI/CD pipelines from scratch for production systems, and I've inherited pipelines that made me question whether the previous author was actively trying to sabotage the project. Both experiences taught me things.
The Problem With Most Pipelines
Most CI/CD pipelines I've encountered in the wild share the same disease: they grew organically. Someone added a build step. Someone else added a deployment. A third person added "just a quick script" to handle that one edge case. Six months later, you have a 500-line Jenkinsfile that nobody fully understands, and the deploy process involves three manual steps that aren't documented anywhere.
The pipeline becomes the thing everyone's afraid to touch. Which means it also becomes the thing nobody maintains. Which means it slowly rots until something breaks at the worst possible time.
What I Actually Do
Keep the Jenkinsfile Stupid Simple
The Jenkinsfile should be a sequence of stages that a new team member can read and understand in five minutes. If it needs a comment to explain what it does, it's too complex.
pipeline {
stages {
stage('Build') { steps { sh 'mvn clean package -DskipTests' } }
stage('Test') { steps { sh 'mvn verify' } }
stage('Analyze') { steps { sh 'mvn sonar:sonar' } }
stage('Deploy') { steps { sh './deploy.sh ${ENVIRONMENT}' } }
}
}
All the actual logic lives in scripts and tools, not in the pipeline definition. The pipeline orchestrates; it doesn't implement.
Fail Fast, Fail Loud
If the build breaks, I want to know immediately. Not after the integration tests have been running for twenty minutes. Not after the deployment to staging has started. The pipeline should stop at the first failure and scream about it.
Notifications go to the team channel. Not email - nobody reads CI/CD emails. A message in the channel that says "build failed on commit abc123" with a link to the logs. That's it.
Environment Promotion, Not Environment Branches
I've seen teams with a dev branch, a staging branch, and a main branch, each with its own pipeline and its own deployment. This is madness. You end up with merge conflicts between environments, cherry-picks that miss commits, and the perpetual question of "which branch is actually deployed to prod right now?"
One branch. One artifact. Promote it through environments. The same JAR that passes tests in dev is the same JAR that gets deployed to staging is the same JAR that goes to production. If it works in staging and breaks in prod, the problem is environmental configuration, not code - and that's a much smaller surface area to debug.
Secrets Management That Isn't Terrible
Secrets in environment variables. Not in the Jenkinsfile. Not in a properties file committed to the repo (yes, I've seen this). Not in a shared credentials file on the Jenkins agent.
Azure Key Vault for anything production-related. Jenkins credential store for pipeline-level secrets. Rotate regularly. Audit access. The basics, but you'd be surprised how many teams skip them.
The Unsexy Truth
Good CI/CD isn't exciting. It's boring. The best pipeline is the one nobody thinks about because it just works - code gets pushed, tests run, artifacts deploy, and everyone goes about their day.
The exciting pipelines are the ones that break. And in my experience, the ones that break are the ones that tried to be clever instead of reliable.
Keep it simple. Keep it maintainable. Keep it boring.