Developing software is a process composed of several key steps:
- Understanding the problem
- Designing a solution
- Implementing it (writing code)
- Testing it
- Deploying to production
- Maintaining
- Iterating
In this article, I will focus on one crucial part of the process: writing code and running it locally.
Demo:
Observations from Startups and Established Companies
Startups tend to move fast. They have fewer meetings, design quickly, implement rapidly, and deploy in record time. However, as a company grows, processes slow down. Meetings multiply, workflows become more complex, and writing code becomes harder and less enjoyable.
One major challenge in larger organizations is the slow feedback loop during development. This article aims to address how developers can improve the efficiency of their workflow, making writing and testing code faster and more enjoyable.
Why a Fast Development Cycle Matters
A fast development cycle ensures a shorter feedback loop, which is critical at every stage of software development. As a software engineer, my focus is on solving problems and delivering value. The faster I can write, test, and deploy code, the more productive and engaged I am. Conversely, a slow development cycle leads to distractions and loss of context, reducing productivity.
It’s also worth noting that engineers should be evaluated based on the impact they deliver, not the number of hours worked. Efficiency and problem-solving should take precedence over rigid time tracking.
The Cost of Slow Feedback Loops
Let’s imagine a scenario where development is painfully slow:
- 5 minutes for a local build
- 7 minutes for a local deploy
Each iteration requires 11 minutes. If a simple change requires four iterations, that’s 44 minutes spent waiting. For more complex tasks, this waiting time balloons further. These delays accumulate, leading to frustration and lost productivity.
A Setup for Rapid Feedback
I still explore this area, but currently I have something that more or less is fast and stable, here is how it works:
- Project Context:
- The current project is a Spring Boot application.
- The application is built into a Docker image, which is published and deployed to a Kubernetes cluster.
- The development environment mirrors production as closely as possible, using the same deployment steps.
- Optimizing the Local Feedback Loop:
- For local development, the source code is exposed in the Docker image.
- Changes in the code are automatically detected by Skaffold, which synchronizes the updated files.
- The code is built locally using an IDE.
- Once the build is complete, Spring Boot DevTools detects the changes and reloads the application.
This workflow significantly reduces the time between making a change and seeing its impact.
Dockerfile.dev – it has .dev because the production Dockerfile create an immutable image.
FROM gradle:8.12-jdk23 AS builder
WORKDIR /app
RUN echo "org.gradle.daemon=true" >> ~/.gradle/gradle.properties
COPY build.gradle.kts settings.gradle.kts gradle/ ./
RUN gradle dependencies
VOLUME /app/
COPY . .
CMD ["gradle", "bootRun"]
.dockerignore – because I don’t want all the files exposed on the Docker
/*
!/src/
!build.gradle.kts
!settings.gradle.kts
!/gradle
!/build
skaffold.yaml
apiVersion: skaffold/v4beta11
kind: Config
metadata:
name: application
build:
local:
push: false
artifacts:
- image: registry.local.lan/daniftodi/application/backend
context: .
docker:
dockerfile: Dockerfile.dev
sync:
manual:
- src: 'src/main/kotlin/**/*.kt'
dest: /app/
- src: 'src/main/resources/**'
dest: /app/
- src: 'build/**'
dest: /app/
deploy:
helm:
releases:
- name: application
chartPath: k8s/application
valuesFiles:
- k8s/application/values.yaml
version: 0.1.0
I run it with:
skaffold dev -v info