<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Bazel on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/bazel/</link><description>Recent content in Bazel on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Wed, 03 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/bazel/index.xml" rel="self" type="application/rss+xml"/><item><title>Build Systems — Ant, Maven, Gradle, Bazel</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/build-systems/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/build-systems/</guid><description>&lt;p&gt;The Java ecosystem has cycled through several generations of build tooling. Each generation solved real problems with the previous one and introduced new ones of its own.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ant"&gt;Ant
&lt;/h2&gt;&lt;p&gt;XML-based, imperative. You describe exactly what to do and in what order — compile these files, copy to this directory, package this jar. No conventions, no opinions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: total control, predictable, easy to understand what any given build does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;: verbose, no built-in dependency management (Ivy was a separate add-on), and every project reinvents the same targets from scratch. Large Ant builds become hard to maintain.&lt;/p&gt;
&lt;p&gt;Still found in older enterprise Java projects. Worth knowing to read and modify, less worth starting from scratch.&lt;/p&gt;
&lt;p&gt;Used it. It was what it was — at least you always knew exactly what the build was doing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="maven"&gt;Maven
&lt;/h2&gt;&lt;p&gt;Convention over configuration. If your project follows the standard layout (&lt;code&gt;src/main/java&lt;/code&gt;, &lt;code&gt;src/test/java&lt;/code&gt;, etc.) most of the build is declared, not scripted. Dependency management built in via the POM and central repository.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maven 1&lt;/strong&gt;: the original. Repository model and POM concept introduced. Plugin system was limited and the build lifecycle was rigid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maven 2&lt;/strong&gt;: major redesign. The lifecycle phases (validate → compile → test → package → install → deploy) that most people know. Dependency resolution significantly improved. This is the version that won widespread adoption.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maven 3&lt;/strong&gt;: incremental improvements over Maven 2. Better performance, improved parallel builds, polyglot POM support. Most Maven projects today run on 3.x.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: standardised project layout means any Maven project is immediately navigable; dependency management and central repository model that the whole ecosystem built on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;: XML is verbose for expressing logic; the lifecycle is powerful but opaque when things go wrong; plugin configuration gets unwieldy; multi-module builds are better than Ant but still awkward at scale.&lt;/p&gt;
&lt;p&gt;Not a fan. All three weaknesses compound each other: the XML is painful to write, the lifecycle hides what is actually executing and where, and parent POM inheritance chains in multi-module projects make it genuinely hard to answer &amp;ldquo;what does this build actually do?&amp;rdquo; You end up cargo-culting POM snippets from Stack Overflow and hoping the lifecycle does what you think it does.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="gradle"&gt;Gradle
&lt;/h2&gt;&lt;p&gt;Groovy (and later Kotlin) DSL instead of XML. Keeps Maven&amp;rsquo;s dependency management and repository model, drops the rigid lifecycle in favour of a task graph. Incremental builds — only re-runs tasks whose inputs changed.&lt;/p&gt;
&lt;p&gt;Became the default for Android builds and has significant adoption in JVM projects that outgrew Maven.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: expressive build scripts; incremental and cached builds make large projects faster; Kotlin DSL gives type safety and IDE support; flexible enough to model non-standard builds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;: the flexibility is also the trap — Gradle builds vary wildly and can be hard to read; the Groovy DSL is easy to write badly; build times on cold caches can be slow; debugging task dependencies is non-trivial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gradle vs Maven&lt;/strong&gt;: Maven wins on standardisation and predictability; Gradle wins on performance and flexibility. For a standard Java/Kotlin service with no unusual requirements, Maven is often the lower-maintenance choice. For Android, large monorepos, or complex multi-language builds, Gradle.&lt;/p&gt;
&lt;p&gt;Gradle solves the XML problem but introduces a different one: the flexibility makes it very easy to get wrong. Every team ends up with a slightly different Gradle build, and reading someone else&amp;rsquo;s is often just as opaque as Maven&amp;rsquo;s lifecycle — just for different reasons. The problem of &amp;ldquo;what is this build actually doing&amp;rdquo; doesn&amp;rsquo;t go away, it just changes shape.&lt;/p&gt;
&lt;p&gt;The discipline that keeps Gradle manageable: &lt;strong&gt;don&amp;rsquo;t stuff everything into the Gradle build&lt;/strong&gt;. Gradle should compile, test, and package — that&amp;rsquo;s it. Deployment logic, environment setup, Docker builds, release steps — those belong in scripts or other tools that are good at those things. Then wrap the whole thing in a Makefile that gives a single consistent entry point. &lt;code&gt;make build&lt;/code&gt; calls Gradle. &lt;code&gt;make docker&lt;/code&gt; calls a shell script. &lt;code&gt;make deploy&lt;/code&gt; calls Helm. Gradle stays focused and readable; the Makefile is the seam that holds it together.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="sbt"&gt;SBT
&lt;/h2&gt;&lt;p&gt;The Scala Build Tool. Reactive, incremental compilation at the core. Not just a build tool — the REPL and interactive session are part of the workflow.&lt;/p&gt;
&lt;p&gt;Heavy dependency on Scala itself; the build definition is Scala code. Powerful but a significant learning curve for anyone not already in the Scala ecosystem.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Notes to follow.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="bazel--bazelisk"&gt;Bazel &amp;amp; Bazelisk
&lt;/h2&gt;&lt;p&gt;Originally Google&amp;rsquo;s internal build system (Blaze), open-sourced as Bazel. Hermetic builds — each action declares its inputs and outputs explicitly, no implicit filesystem access. Correctness and reproducibility guaranteed by construction.&lt;/p&gt;
&lt;p&gt;Language-agnostic: rules exist for Java, Go, Python, C++, and others. Built for monorepos at scale — incremental and distributed builds, remote caching, remote execution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bazelisk&lt;/strong&gt;: version manager for Bazel. Pin the Bazel version in &lt;code&gt;.bazelversion&lt;/code&gt;, run &lt;code&gt;bazelisk&lt;/code&gt; instead of &lt;code&gt;bazel&lt;/code&gt; — it downloads and uses the pinned version automatically. Essential for team consistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: genuinely reproducible builds; scales to very large codebases; remote caching makes CI fast once warm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;: steep learning curve; rules ecosystem outside Google&amp;rsquo;s own languages is less mature; significant setup cost; overkill for anything smaller than a large monorepo.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Opinions to follow.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="choosing"&gt;Choosing
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Small project&lt;/th&gt;
 &lt;th&gt;Standard service&lt;/th&gt;
 &lt;th&gt;Large monorepo&lt;/th&gt;
 &lt;th&gt;Polyglot&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Ant&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Possible&lt;/td&gt;
 &lt;td&gt;Legacy only&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Maven&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Fine&lt;/td&gt;
 &lt;td&gt;Good default&lt;/td&gt;
 &lt;td&gt;Struggles&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Gradle&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Fine&lt;/td&gt;
 &lt;td&gt;Good&lt;/td&gt;
 &lt;td&gt;Better&lt;/td&gt;
 &lt;td&gt;Partial&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Bazel&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Overkill&lt;/td&gt;
 &lt;td&gt;Overkill&lt;/td&gt;
 &lt;td&gt;Strong&lt;/td&gt;
 &lt;td&gt;Strong&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both Maven and Gradle have the same core problem: you end up not really knowing what your build is doing. Ant at least was honest about it. If forced to choose, Gradle is the least bad option — the DSL flexibility is a trap but it is a trap you can avoid with discipline, whereas Maven&amp;rsquo;s lifecycle opacity and XML are just the deal regardless. For anything in the JVM ecosystem today the choice is usually made for you by the project or the framework — if you get to choose, Gradle with a simple build file and keep it that way.&lt;/p&gt;</description></item></channel></rss>