# CI/CD & DevOps

> Case study: from manual deployments documented in a TXT file to automated CI/CD with Git, Jenkins, Docker and Kubernetes — releases from 50 minutes to around 10.

- Context: 3 load-balanced production servers
- Before: TXT file, ~50 min manual deploys
- After: Automated, ~10 min deploys
- Stack: Git, Jenkins, Groovy, Docker, Kubernetes
- URL: https://ferencfarkas.dev/work/ci-cd

## 01 — Context

When I joined the company, deploying software was a surprisingly manual process. The deployment instructions were literally stored in a TXT file.

A developer had to connect to three different production servers, manually update the code, run a sequence of commands, and repeat the process on each server.

The servers were behind a load balancer, but the deployment itself wasn't coordinated.

It was slow, error-prone and stressful.

A typical deployment took around **50 minutes**, and it wasn't unusual for something to go wrong along the way — sometimes leaving the system partially updated or temporarily unavailable.

We needed to change that.

## 02 — Get the Code Under Control

The first thing I changed wasn't Jenkins.

It was **Git**.

The entire codebase was still in Subversion. Moving to Git gave us much more flexibility around branching and feature development, and it provided the foundation for everything that followed.

We initially adopted a Git Flow-inspired branching model and then adapted it to our own development process as we learned what worked and what didn't.

This was an important pattern throughout the transformation:

> Don't adopt a process because it's popular. Adapt it to the way your team actually works.

## 03 — Automating the Deployment

Once the source code was in Git, the next problem became obvious.

We didn't want developers manually executing deployment instructions anymore.

I evaluated and experimented with several CI/CD platforms.

They all had strengths and weaknesses. Some had better user experiences, others were easier to get started with, and some offered more built-in functionality.

But I kept running into the same limitation: **I wanted the deployment process to be ours.**

In the end, I chose Jenkins.

It wasn't the easiest option.

The UI wasn't great. Running Jenkins ourselves meant managing controllers and workers, and upgrades could be painful. A managed CI/CD platform would have removed some of that operational overhead.

But Jenkins gave us something I valued more at the time: **flexibility.**

We could build exactly the deployment workflows we needed and extend them with our own code.

## 04 — Building Our Own Deployment Platform

The initial setup was relatively simple:

Git → Jenkins → tests → build → deploy

Jenkins handled the orchestration, while shell scripts and Groovy handled the implementation details.

But the system quickly grew beyond a collection of pipelines.

We introduced shared Jenkins libraries and our own Groovy-based DSL so that common deployment logic didn't have to be duplicated across projects.

Instead of every project implementing its own version of the deployment process, we started building reusable capabilities into the platform itself.

The deployment process became something closer to:

Git push → tests → build → image → deployment

And eventually Docker and Kubernetes became part of the picture.

## 05 — From CI/CD to an Engineering Platform

As the infrastructure evolved, so did the pipeline. We introduced:

- Docker-based builds
- Kubernetes deployments
- Parallel builds & deployments
- Shared Jenkins libraries
- Groovy deployment abstractions
- Automated testing
- Multi-stage builds
- Environment-specific logic
- Blue-green deployments
- Canary-style deployments

The important part wasn't any individual technology.

It was that **the deployment process became software itself**.

Instead of maintaining a document describing how humans should deploy an application, we built a system that knew how to do it.

## 06 — Making Deployments Safer

One of the biggest problems with the original process was that deploying the application meant modifying the running production environment directly.

There was very little room for error.

As we moved towards containerized Kubernetes deployments, we could change that.

**Blue-green deployments** allowed us to prepare a new version separately and switch traffic when it was ready. Later, we also introduced **canary-style deployments** where appropriate.

This fundamentally changed the risk profile of a deployment. A release was no longer:

Connect to server → update files → hope everything works

It became:

Build → test → deploy → verify → shift traffic

That distinction matters.

## 07 — The Result

The deployment process went from roughly **50 minutes to around 10 minutes**.

But speed wasn't actually the most important improvement.

The bigger change was **repeatability**. Developers no longer had to remember a sequence of commands or connect to production servers manually. Deployments became:

- Automated
- Repeatable
- Faster
- Safer
- Easier to audit
- Less dependent on individual knowledge

And because the deployment logic itself was version-controlled, the process could evolve alongside the applications.

### By the numbers

- **50 → ~10 min** — Deployment time
- **Fully automated** — Deployment process
- **Blue-green / Canary** — Production deployment
- **Docker + Kubernetes** — Infrastructure

## 08 — What I Learned

Three lessons stuck with me long after the pipeline was running.

- **Automation is not the goal** — It's tempting to think that CI/CD means putting a Jenkins pipeline in front of a deployment process. That's not really the point. The real goal is to remove unnecessary human decisions from repetitive, error-prone processes. If the deployment requires someone to remember which commands to run and in which order, the process isn't finished yet.
- **Flexibility has a cost** — Jenkins' flexibility was a huge advantage for us, but it came with operational costs — controllers, workers, upgrades and maintenance all became our responsibility. There is no universally best CI/CD platform. The right choice depends on what you value: developer experience, flexibility, operational overhead, integrations, cost or control. At that point in our journey, control and flexibility were worth the additional complexity.
- **Deployment is architecture** — Once CI/CD became deeply integrated with Docker, Kubernetes and our infrastructure, it stopped being just a developer convenience and became part of the platform architecture. That foundation later made our move to the cloud significantly easier. We weren't deliberately building for a future migration — we were solving the problems in front of us. But good engineering decisions tend to compound.

> The best deployment is the one nobody has to think about.

## 09 — Looking Back

The most satisfying part of this project wasn't going from 50 minutes to 10.

It was seeing deployment become **boring**.

No one needed to SSH into three production servers. No one needed to open a TXT file and follow a list of commands. No one had to wonder whether someone forgot step seven.

The system did the work.

**When the deployment process becomes boring, predictable and almost invisible, you've probably done something right.**
