Validate before merge.
Promote by version.

How I build CI/CD systems that are testable, traceable, and safe to promote.

Every pull request proves itself in real infrastructure. Every release is versioned and tagged. Every deployment is reversible.

Seven Core Principles

The foundation of reliable delivery

01

Every change must prove itself before merge

A pull request is where infrastructure, build, deployment, and testing happen in a dedicated test environment.

02

Test against real infrastructure

Mocked environments hide real problems. Validate in a real isolated environment before touching shared ones.

03

Main should stay clean and deployable

Merging to main means the change is validated. main represents code safe to deploy.

04

CI and CD are separate concerns

CI proves the change. CD promotes trusted versions. They work together, not merged into chaos.

05

Releases should be versioned, tagged, and traceable

Every deployment maps to a clear version tag. If you cannot tell what version is running where, your pipeline is failing.

06

Promotion should be intentional

Promote using versioned artifacts and tags, not rebuilds. The same tested version moves forward.

07

Rollback must be built in

Every deployment strategy is incomplete without rollback. Every release should be reversible.

The Workflow

From feature branch to production

1

Feature Branch

Developer creates a feature branch for a change.

2

PR Environment Validation

Terraform provisions isolated infrastructure. GitHub Actions builds and deploys. Tests run against real env.

3

Merge to Main

After approval, merge triggers CI pipeline. Build once, deploy to dev, verify merged state.

4

Tag Release

CD creates version tag (v1.2.0-dev). This tag is the release identity.

5

Promote Across Environments

Same Docker image → staging, prod. Only secrets/config change per environment.

6

Rollback by Tag

Deployment fails? Rollback by version tag. Fast, explicit, reversible.

PR Testing

Ephemeral per-PR environments

Recommended

Create isolated PR environments using Terraform on Google Cloud.

  • • Per-PR Kubernetes namespace or Cloud Run service
  • • Branch-specific database/schema
  • • Preview URL for testing
  • • Auto cleanup on merge

Why

Isolation means no flaky results, no coordination overhead, and real proof that the change works before it touches main.

Versioning and Promotion

Same artifact, different environment config

Build once. Version it. Promote same Docker image across environments:

v1.2.0-dev   → deploy to Kubernetes dev namespace
v1.2.0-rc    → release candidate in staging
v1.2.0       → production release

Same image. Secrets/config injected per environment.

This ensures traceability, prevents rebuild drift, and keeps secrets isolated from the artifact.

Release Strategy

Keep it simple: main to tag to deploy

1

Merge to Main

PR validated and approved. All tests pass. Merge to main—main is always production-ready.

2

Create Semantic Tag

Tag main with version (v1.2.3). GitHub Actions auto-generates release notes by diffing against previous tag.

3

Deploy from Tag

Trigger deployment via webhook or manual approval. Full traceability: every deploy tied to a commit and version.

No release branch. No staging merge. Just:

main → semantic tag → GitHub Actions release notes → deploy

Clean, traceable, automatic. Main is production. Tag is history. Diff between tags tells the story of what changed.

What I Avoid

Anti-patterns that break reliability

Manual deployments
Merging unvalidated infrastructure
Treating PRs as code review only
Rebuilding per environment
Unclear versioning
Promoting without traceability
Pipelines with no rollback
Shared test environments as primary validation

Preferred Tooling

GitHub + GitHub Actions + Terraform + Next.js + Google Cloud

GitHub               → Source of truth, PR workflow
GitHub Actions       → CI/CD orchestration
Terraform            → Infrastructure as code
Docker               → Build once, deploy everywhere
Kubernetes           → Scale and manage containers
Google Cloud         → Compute, storage, services

Build and Deploy

GitHub Actions builds your app once. Docker packages it. Kubernetes runs it. Same image, multiple environments.

Infrastructure

Terraform provisions ephemeral per-PR environments on Google Cloud. Easy, repeatable, disposable.

How This Works

Docker + Kubernetes pattern: Build once with GitHub Actions, package in Docker, deploy same image across environments via Kubernetes. Secrets and config injected per environment.

PR opened
  ↓ Terraform provisions isolated GKE namespace
  ↓ GitHub Actions builds and tags Docker image
  ↓ Deploy to pr-123.example.com for testing
  ↓
Merge to main
  ↓ Build Docker image → push with tag v1.2.0-dev
  ↓ Deploy to Kubernetes dev namespace
  ↓
CD promotes same image to staging, prod
  ↓ Only secrets/config changes per environment
  ↓
Rollback: kubectl rollout undo + version tag

Validate before merge.
Promote by version.

Changes proven in isolated real infrastructure. Releases versioned and tagged. Deployments traceable and reversible.