CI/CD Lesson 32 – CI/CD for Monolithic Applications | Dataplexa
Section IV · Lesson 32

CI/CD for Monolithic Applications

In this lesson

Monolith CI/CD Challenges Parallel Test Execution Deployment Strategies Database Migration Safety Strangler Fig Pattern

CI/CD for monolithic applications is the practice of applying modern delivery pipeline discipline — fast feedback, automated verification, frequent deployment — to applications where every component is packaged and deployed as a single unit. The monolith is not an architectural failure to be immediately decomposed; it is a valid, widely used architecture that presents its own distinct CI/CD challenges. Build times are longer because the entire application must compile. Test suites are larger because the entire domain must be covered. Deployments are higher-risk because the entire application is replaced in one operation. The goal of CI/CD for monoliths is to manage these characteristics — not to eliminate the monolith.

The Specific Challenges of Monolith Pipelines

A monolith pipeline faces challenges that do not exist at the same scale in microservices pipelines. Understanding them is the prerequisite for addressing them effectively rather than treating them as inherent limitations of the architecture.

Monolith Pipeline Challenges and Their Solutions

🐌
Long build times
The entire application must be compiled on every commit. Mitigation: aggressive build caching at the language and dependency level, incremental compilation where the language supports it, and parallelised build steps for independent modules. A 20-minute compile that developers wait for will be skipped or worked around.
🧪
Large, slow test suites
A monolith accumulates thousands of tests across the entire domain. Running them serially on every PR produces a pipeline that takes 40+ minutes — long enough to break the feedback loop. Mitigation: aggressive parallelisation across multiple runners, test splitting by module, and running the full suite only on merge to main while PRs run a fast subset.
🚀
High-risk deployments
Replacing the entire application in a single deployment operation means every deployment carries the aggregate risk of all changes since the last release. Mitigation: small, frequent deployments rather than large infrequent ones, blue-green deployment to enable instant rollback, and feature flags to decouple deployment from release.
🗄️
Database migration complexity
A monolith typically owns a single large database schema. Every schema migration must be backwards-compatible with the running application during the deployment window. Mitigation: the expand-contract pattern, zero-downtime migration tooling like Flyway or Liquibase run as a pipeline step, and explicit validation that migrations are reversible before they reach production.

The Ocean Liner Analogy

Turning an ocean liner takes more time and distance than turning a speedboat. That is not a flaw — it is physics, and the liner carries far more cargo. The challenge is not to make the liner turn like a speedboat; it is to plan turns far enough in advance, execute them smoothly, and not overcorrect. CI/CD for monoliths works the same way: the pipeline takes longer than a microservices pipeline, deployments require more care, and tests cover more ground. The solution is not to pretend these properties do not exist — it is to design pipeline architecture that accommodates them gracefully: good caching, aggressive parallelism, small frequent deployments, and zero-downtime deployment strategies.

Parallel Test Execution — The Most Impactful Speed Lever

For a monolith with a large test suite, parallelisation is the single most impactful pipeline performance improvement available. GitHub Actions supports job-level parallelism natively — the same test suite can be split across multiple runners, each running a different subset, with the total test time collapsing from the sum of all tests to the duration of the slowest parallel shard.

Parallelised Test Suite — GitHub Actions Matrix Strategy

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4, 5]          # Split tests across 5 parallel runners
      fail-fast: true                    # Cancel remaining shards if one fails

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci

      - name: Run test shard
        run: |
          npx jest \
            --shard=${{ matrix.shard }}/5 \   # This runner handles 1/5 of the test suite
            --forceExit \
            --coverage
        # Jest splits tests by file count across shards automatically
        # A 40-minute suite becomes ~8 minutes across 5 parallel runners

  all-tests-passed:
    needs: test                          # Gates the deploy on all shards passing
    runs-on: ubuntu-latest
    steps:
      - run: echo "All test shards passed — safe to deploy"

What just happened?

Five runners execute simultaneously, each handling one fifth of the test suite. A suite that previously took 40 minutes now completes in approximately 8. The fail-fast option cancels remaining shards immediately if any shard fails — no point running the other 80% of tests if we already know the build is broken. A gate job ensures the deploy stage only proceeds when every shard has passed.

Deployment Strategies for Zero-Downtime Monolith Releases

Deploying a monolith does not have to mean downtime. Two strategies make monolith deployments zero-downtime while preserving fast rollback capability: blue-green deployment and rolling deployment. Both are covered in more depth in Lesson 33, but their application to monoliths is worth highlighting specifically here.

Blue-green deployment maintains two identical production environments — blue (current) and green (next). The new version is deployed to the green environment and smoke tested there before any traffic is switched. When ready, a load balancer cutover moves all traffic from blue to green in seconds. Rollback is equally fast: switch traffic back to blue. The previous version is still running and ready to serve. For a monolith where a deployment touches every component, the ability to test the complete deployed version before it receives production traffic — and to roll back instantly without redeploying — is particularly valuable.

The Strangler Fig Pattern — Evolving Toward Services

Many organisations with monoliths eventually reach a point where parts of the system would benefit from being extracted into independent services — not because monoliths are wrong, but because specific domains have grown to a size where independent deployability and team ownership would accelerate delivery. The strangler fig pattern describes this evolution: new functionality is built as separate services, a routing layer (often an API gateway or reverse proxy) directs traffic to either the monolith or the new service, and over time the monolith's surface area shrinks as more functionality is extracted.

From a CI/CD perspective, the strangler fig pattern introduces a mixed architecture that requires pipeline coordination between the monolith pipeline and the emerging service pipelines. The routing layer must be versioned and deployed alongside the service changes. Contract tests between the monolith and new services catch integration regressions. Feature flags control which requests go to the monolith versus the new service, enabling gradual traffic migration without a hard cutover. The pipeline architecture must evolve alongside the application architecture — neither can lead the other too far without creating a gap that causes incidents.

Warning: Large Infrequent Deployments Are the Biggest Risk in Monolith CI/CD

The most dangerous monolith deployment pattern is the one that accumulates weeks of changes into a large batch release. Each change in isolation may be low-risk, but twenty changes deployed simultaneously produce an aggregate risk that is orders of magnitude higher — more code changed, more potential interactions, harder to identify the culprit when something fails, and a longer fix cycle because every possible change must be investigated. The solution is not to deploy less frequently; it is to deploy more frequently and in smaller batches. A monolith that deploys daily with five changes per release is dramatically safer than one that deploys monthly with a hundred changes. CI/CD discipline applied to a monolith achieves exactly this — small, frequent, verified deployments that keep batch size small and rollback scope narrow.

Key Takeaways from This Lesson

Monoliths have distinct CI/CD challenges that require specific solutions — long build times, large test suites, high-risk deployments, and database migration complexity are all manageable with the right pipeline architecture; they are not inherent reasons to decompose the application.
Test parallelisation is the highest-impact speed improvement for large test suites — splitting across five runners reduces a 40-minute suite to 8 minutes, preserving the feedback loop that makes frequent deployment viable.
Blue-green deployment makes monolith releases zero-downtime and instantly reversible — the new version is fully verified in the green environment before traffic switches, and rolling back is a load balancer change rather than a redeployment.
Small frequent deployments are dramatically safer than large infrequent ones — a monolith deploying daily with five changes per release carries far less aggregate risk than one deploying monthly with a hundred changes, and is far easier to roll back when something goes wrong.
The strangler fig pattern allows gradual service extraction without a big-bang rewrite — new functionality builds as services alongside the monolith, traffic migrates progressively, and the pipeline architecture evolves in step with the application architecture.

Teacher's Note

Before decomposing a monolith into microservices, measure how often you deploy it and how long each deployment takes — if the answer is "infrequently and nervously," fixing the CI/CD pipeline almost always delivers more value faster than an architectural rewrite.

Practice Questions

Answer in your own words — then check against the expected answer.

1. What is the name of the architectural pattern — named after a tree that grows around and gradually replaces its host — that describes extracting microservices from a monolith incrementally, routing traffic to either the monolith or the new service via a proxy layer while the monolith's surface area shrinks over time?



2. What is the technique — supported by test runners like Jest via the --shard flag — that splits a test suite across multiple parallel runners, each executing a distinct subset, so that total test time equals the duration of the slowest subset rather than the sum of all tests?



3. What deployment strategy maintains two identical production environments — one running the current version and one receiving the new version — allowing the new version to be fully smoke tested before a load balancer cutover, with rollback achieved by switching traffic back rather than redeploying?



Lesson Quiz

1. A monolith has a test suite that takes 40 minutes to run serially. Developers have stopped waiting for pipeline results before pushing follow-up commits. What single pipeline architecture change would have the most immediate impact on feedback loop speed?


2. Team A deploys their monolith weekly with an average of five changes per release. Team B deploys monthly with an average of twenty changes per release. Both teams have equivalent test coverage. Which team has lower deployment risk, and why?


3. A team wants to begin extracting services from their monolith without stopping feature development or committing to a full rewrite. What pattern allows them to build new functionality outside the monolith while keeping the existing application running, migrating traffic gradually as each extracted service stabilises?


Up Next · Lesson 33

Blue-Green Deployments

Lesson 32 introduced blue-green as a monolith deployment strategy. Lesson 33 goes deep — the full mechanics, the pipeline implementation, the database considerations, and when blue-green is and is not the right choice.