Build Faster, Ship Sooner: How we reduced 80% of our build time

At Headout, we strive to deliver seamless experiences for our users. But behind the scenes, slow build times were grinding our engineering productivity to a halt. Our largest codebase was taking a staggering ~15 minutes to build, causing frustration, delaying deployments, and increasing CI costs. More waiting time to build meant slower turnaround time to deploy hot fixes, this was a major challenge.

TL;DR:

Using advanced techniques like Build Caching and Remote Build Caching, combined with tools provided by GitHub action, we slashed build times down to just ~3 minutes from ~15 minutes, a whopping 80% savings with the added savings on CI costs and increased productivity!

Let's dive in

When compiling takes ages to complete

🔍 The Problem: Every PR builds taking 15 minutes to complete

Build times for our largest codebase were significantly impacting our workflow:

  • Reduced developer productivity – Engineers spent more time waiting and less time coding.
  • Delayed production fixes – Slower deployments meant prolonged downtime and revenue impact.
  • High CI costs – Increased build times meant higher infrastructure costs.

The frustration of waiting was real. The typical “compiling” excuse became too familiar. It was time to take action.

🚀 Why we invested in build optimization

Build optimisations over time

The Why?

  • Increased productivity 📈 – Engineers working on our largest codebase can focus on building features, not waiting.
  • Faster fixes 🛠️ – Swift deployments to resolve issues quickly.
  • Reduced CI costs 💰 – Efficient builds save compute resources and reduce infrastructure costs.

So, how we did it?

We used combination of Remote Build Cache (self-hosted node) along with Incremental Build Cache (using GitHub Action Cache).

Given the size and complexity of the codebase, we implemented a remote build cache to share cached outputs between CI and across developer branches.

How It Works

  1. CI builds push to the remote build cache:
    Whenever a change is pushed to a branch (including the main branch), CI saves the build outputs to the remote build cache node.
  2. Developer branches pull from the cache:
    When developers push changes, the system pull from the remote cache for incremental compilation which saves time recompiling previously compiled classes which hasn't changed, this means incremental compilation in your CI 🔥

What goes into making a build?

To understand how incrementation compilation works, let's quickly see the life of a build

Life of a build

  • Build: Sequence of steps > Tasks
  • Task: Inputs > Action > Outputs
    • Output of a task can be input to another
Life of a build

It's a simple input-output chain at it's core. Every task has a input and it produces an output. This output can become an input of a next task.

For example, there are 2 modules in your codebase, A and B and let say Module B depends on Module A to build.

  • Task 1: Compiling module A
  • Task 2: Compiling module B (which depends on module A)

So, task 1 here would be compile task for Module A, and task 2 would compile task of Module B. Now, let's see how we can leverage the knowledge of this concept to achieve incremental compilation in your remote builds.

If task is deterministic & inputs haven't changed, re-use output from previous execution

Now, let say you make some changes in Module A (Task 1) and push your changes, once build completes on your CI, the remote build cache will save it's output to the Remote Build Cache node.

Now, you make some changes to Module B (Task 2) and push your changes, you don't need to recompile Module A (Task 1) again, it can reuse previously compiled outputs from the Remote Build Cache node to save the time.

In a nutshell:

Re-Use - Remote Cache

  • Re-use output from a previous global execution where inputs were the same.
  • Use a remote cache of inputsoutputs.
  • If some deterministic task action work had been done globally, don’t repeat it.
Re-use remote cache

As the number of modules increases, the time savings multiply. This approach significantly boosted efficiency for our largest backend codebase at Headout.

Finishing thoughts:

At Headout, optimizing our build process wasn’t just about reducing wait times; it was about empowering our engineers to focus on what they do best—building incredible experiences for our users. By leveraging advanced techniques, including Gradle and GitHub Action’s caching capabilities, we transformed our build times by 5x.

This journey underscores the power of continuous improvement and engineering excellence. By addressing a critical bottleneck, we enhanced our workflow, enabling faster deployments, quicker fixes, and smoother developer experiences.

With this optimization, we’ve solved more than a problem—we’ve built a foundation for efficient scaling and faster delivery of value. Here’s to smoother builds and reaching even greater heights! 🙌

Alpesh Vas’s Profile Image

written by Alpesh Vas

All things Backend at Headout, fostering a test-driven development culture. Alongside core engineering and product challenges, I solve tough cultural problems like improving code quality at scale.

Dive into more stories