As CI workflows accumulate conditions, dependencies, retries and loops, YAML can turn into brittle scripts with few tools for debugging.

When CI configuration turns into a program
Continuous integration pipelines often begin as compact YAML files that describe a few familiar steps: install dependencies, run checks, and build an artifact. As a project grows, the workflow tends to absorb more decisions. A step might run only for certain branches, several jobs might execute in parallel, and later jobs might wait for earlier ones to finish. Retries and shared templates can reduce repetition, but they also add behavior that readers must track.
That growth creates a mismatch between the job a pipeline performs and the language used to express it. YAML is effective for describing structured data, but conditional logic and repeated operations can make a configuration file harder to read as a system of rules. A small change may affect more than the line it touches because dependencies and conditions determine which jobs run, and in what order.
Retries can blur the source of failure
A retry is useful when a step depends on a service that sometimes fails temporarily, such as a package registry. But a retry attached too broadly can repeat work for the wrong reason. If the same rule also reruns a failing test, the pipeline can conceal the distinction between a transient external problem and a defect in the code.
That distinction matters to anyone diagnosing a red build. Repeated attempts may eventually produce a passing result, but the pass doesn't explain why an earlier attempt failed. Teams can then spend time sorting through inconsistent outcomes instead of fixing the underlying issue. The numbers here are underwhelming: a retry can make a workflow appear more reliable while making its failures less legible.
There isn't a universal rule for retries. Their usefulness depends on where they apply, what kind of failure they address, and whether the system records enough detail to show what happened. When those boundaries are hard to express in the configuration language, operators have to infer behavior from scattered conditions or from the shell commands hidden inside a step.
Repetition exposes the limits of templates
Loops are another pressure point. A pipeline may need to perform a similar operation across several services, while still using service-specific settings or reporting failures separately. If the templating language can't represent that pattern clearly, maintainers may duplicate steps or shift the work into a script. Duplication is easy to understand at first, but later changes can leave similar jobs out of sync.
The problem isn't repetition by itself. It's whether the workflow makes the repeated behavior visible and lets a maintainer see where each service differs. A template that hides too much can make failures harder to trace; copied blocks can make fixes easy to miss. There's a balance between reducing repeated text and keeping the path from a pipeline rule to its effect clear.
If you're working in this space, pay attention to how quickly a change to one job forces you to inspect unrelated sections. That's a practical signal that the workflow may have outgrown its original shape. A loop across six services is a concrete example in the source, but the underlying issue applies to any repeated work the configuration language can't express directly.
The shell block becomes the real workflow
When the configuration can't handle the logic, teams often put it in one run: block. Bash can express conditions, loops, and error handling, so the workaround is understandable. The trouble is that the pipeline's most important behavior then lives in a long script embedded inside a file designed to describe jobs.
At that point, the script behaves like a program but lacks familiar support for maintaining programs: functions to give repeated operations names, types to catch some mismatches, tests to check behavior, and a debugger to inspect execution. These tools don't guarantee correct workflows, and adding them can bring its own complexity. Still, their absence raises the cost of understanding what the pipeline will do when one condition changes.
And this is the part most people overlook: readability is an operational concern, not just a style preference. When a build fails, someone needs to identify which condition selected a path, which dependency blocked a job, and whether a retry changed the result. A long embedded script can answer those questions, but often only after someone reconstructs its control flow by hand. A program-shaped problem hidden in configuration.
What the mismatch means for teams
Here's the thing: workflow tools are easiest to maintain when their language fits the decisions they need to express. Once logic outgrows configuration, teams need ways to structure that logic and inspect its behavior. That might mean dividing a large script into named, testable pieces or choosing a workflow approach with clearer support for conditions and repeated tasks. The source doesn't identify a particular tool, and the right choice depends on how a team builds and operates its software.
This is more significant than it looks. CI pipelines influence how developers interpret test results and how quickly they can trace a failure to its cause. If the workflow itself is difficult to reason about, adding more checks or retries can increase the number of possible paths without making the system easier to trust.
What this means for you is fairly practical: treat pipeline complexity as something to review, not as an inevitable byproduct of adding one more condition. If each change requires more shell code, if retries apply to unrelated steps, or if repeated jobs can't be represented cleanly, the workflow language may no longer be serving the work. Better tools won't remove every failure, but they can make the path from configuration to diagnosis easier to follow.
Discussion
Sign in to join the discussion.