2023 – 2025 · Principal Software Engineer · 6 min read
GitLab CI/CD Migration
End-to-end owner of the migration of 152+ Node and Angular projects from Jenkins Groovy pipelines to native GitLab CI/CD components. Build times cut by 3–6× depending on project size; flakiness eliminated via job isolation.
- GitLab CI/CD
- Pipeline Components
- Node.js
- Angular
- Docker
- Cloudflare Workers
End-to-end owner of the migration of 152+ Node and Angular projects from Jenkins Groovy pipelines to native, reusable GitLab CI/CD components. Designed a shared component library spanning six distinct archetypes, cut pipeline build times by 3–6× depending on project size (with the worst hour-long cases dropping to around ten minutes), and eliminated the Jenkins “noisy neighbor” flakiness that had plagued the estate for years.
The problem
Jenkins was end-of-life at OneTrust, and the broader organization was consolidating its infrastructure onto GitLab. With every pipeline written in bespoke Groovy DSL, maintenance cost scaled linearly with project count, and the existing estate offered none of the structural primitives a modern CI/CD platform needs: parallel jobs, DAG dependencies, per-job log retrieval, or testable pipeline code. Every new compliance requirement (SBOM generation, signed artifacts, SAST gates) meant a PR against every Jenkinsfile.
The migration was both a business decision (decommission the EOL platform) and a strategic one (rewrite for consistency, security, and testability on the way in).
The approach
The estate catalogued into six archetypes, surfacing the real distribution of the codebase:
| Archetype | Approx. count |
|---|---|
| Node + Angular libraries | ~70 |
| Angular micro-frontends (Docker) | ~50 |
| Node services (Docker) | ~24 |
| Cloudflare Workers, SDKs, misc | ~12 |
The migration proceeded one archetype at a time, starting with libraries, the largest bucket and the simplest, to compound early wins.
Cloudflare Workers were the hardest archetype technically. No standardization existed across those projects to begin with, so building the golden path (standardizations, controls, guardrails) had to precede the migration itself. Angular was the hardest archetype operationally: ~120 projects meant the pipeline work was straightforward but PR approvals across distributed, busy teams were the bottleneck. The group pushed through by swarming: focused communication, coordinated review passes, and treating the rollout as one team rather than 120 independent migrations.
One assumption had to be updated mid-migration: composable templates don’t
automatically yield composable utilities. Template reuse was straightforward;
the code those templates orchestrated, including utilities, tooling, and
scripts, needed its own delivery layer. Two patterns ended up shipping side-by-side:
clone-in-at-runtime (git clone or curl) for simple one-off scripts, and
bake-in (packaging utilities into the runner’s Docker image) for complex
tooling and common dependencies.
The outcome
Pipeline build times dropped 3–6× depending on project size, with the worst-case hour-long builds compressing to around ten minutes:
| Project size | Before (Jenkins) | After (GitLab) |
|---|---|---|
| Small | ~5 min | under 1 min |
| Medium | ~10 min | 3–5 min |
| Large | 20–40 min (some up to 60) | ~10 min |
The speedup came from parallelized jobs, dependency caching, fail-fast gating, and the elimination of Jenkins’ shared-runner “noisy neighbor” flakiness. Per-job encapsulation meant artifacts could be verified at each stage, and pipelines went from occasionally unreliable to consistently performant.
Structurally: per-project pipeline files shrank from hundreds of lines to
roughly 20. A single central component release now ships the next compliance
gate to every project with no per-project PR required. Onboarding a new
service went from “read the runbook” to include one component.
What I’d change
Observability should have come first. With 150+ projects and six distinct archetypes, each with their own variations, the single biggest gap was visibility into the actual state of the code across the ecosystem. Cataloging by hand was expensive, and the mid-migration retrofit of pipeline telemetry cost time that instrumenting the estate up front would have saved. Observing the state of the system is the first step in any scientific approach to large-scale change.