Back to portfolio
Case study · Platform engineering

Modernizing 60+ GCP Cloud Functions: Gen1 to Gen2, builds halved

Every Cloud Functions pipeline was a little different and most were slow. I standardized all of them, cut checkout time by about 80% and build time in half, and laid down a safe path from Gen1 to Gen2.

By Vitor Schiavo Updated October 2026 ~6 min read
Solution at a glanceMigrate the runtime and verify its identity.
Gen1 functionInventory runtime and triggers
Gen2 deploymentAdapt pipeline and runtime
ValidationCheck behavior and IAM

A staged migration; production follow-up also checked the runtime service account.

Contribution at a glance

My responsibility
Function-pipeline standardization, runtime migration and identity follow-up.
Scope
Phased development/staging delivery and distinct later production operation.
Collaboration & boundaries
I worked on the infrastructure and deploy path; a planning epic is not proof that every production function completed migration.
Outcome
Standardized deployment work and a production follow-up that pinned runtime identity after an implicit service-account change.

A fleet of Cloud Functions tends to grow one function at a time, and the pipelines grow the same way: copied, tweaked, and never unified. The result was dozens of functions across several repositories, each deploying through a slightly different Jenkins pipeline, most of them slow, and none of them ready for Gen2. I standardized the whole set and modernized the runtimes, sanitized here to the technique.

Delivery scope

This was a phased migration and pipeline-standardization effort. DEV/STG work and later production operation were distinct stages. A production follow-up found an implicit runtime service-account change and pinned the deployment identity to prevent recurrence. The planning epic alone does not establish the state of every production function.

Where the time was going

Two things dominated every build. First, the checkout: each build did a full clone of the deploy repository, 30 to 60 seconds of pure waiting before any real work. Second, dependencies: nothing was cached, so every build reinstalled everything from scratch. Multiply that across dozens of functions deploying many times a day and it adds up to real engineer-hours and real cloud build minutes.

What I changed

Along the way I fixed a batch of long-standing papercuts: an incorrect workspace path, a vendoring inconsistency from blank imports, an entry-point mismatch between Gen1 and Gen2, and oversized deployment packages.

Before and after

MetricBeforeAfterChange
Checkout time30 to 60 s5 to 10 s~80% faster
Total build time3 to 5 min1.5 to 2.5 min~50% faster
Deployment size100%40 to 50%~50% smaller
Cache reuse0%80 to 90%new
80%faster checkout
50%faster builds
60+functions standardized
3languages, Gen1 + Gen2

Why Gen2, and how to get there safely

Gen2 is not just a version bump. It runs on Cloud Run under the hood, which brings concurrency (up to 1000 simultaneous requests per instance instead of one), native Eventarc integration, faster cold starts, and more granular pay-per-use. The migration is where people get nervous, so I kept it boring:

What I would tell someone starting this

Decisions and next steps

A retrospective view of the approach and its limits. The next improvement is a proposal, not a delivered result.

Alternative and rationale

Per-function pipelines offer local flexibility; a common deploy path reduces repetition but must preserve runtime and trigger differences.

Trade-off

New runtime generations can change identity and logging as well as compute. Deployment success alone is insufficient acceptance.

What I would improve next

Next improvement: maintain a per-function migration inventory with trigger, identity, logging and rollback acceptance checks.