Build Tools & Modules

Why Build Tools?

15 min Lesson 1 of 13

Why Build Tools?

You already know how to write Java — classes, generics, streams, concurrency, JDBC. But knowing the language is only part of the picture. The moment a project grows beyond a handful of source files, three problems compound each other: compilation at scale, dependency management, and packaging for deployment. Build tools exist to solve all three, reliably and repeatably.

The Limits of Bare javac

The Java compiler javac is perfectly capable for learning exercises. In a real project it quickly becomes unwieldy.

Imagine a modest service with 40 source files spread across packages, a logging library, a JSON parser, an HTTP client, and a database driver. Compiling it manually looks something like this:

javac -cp lib/slf4j-api-2.0.9.jar:lib/jackson-databind-2.15.2.jar:lib/postgresql-42.6.0.jar \ -d out \ src/com/example/service/*.java \ src/com/example/repository/*.java \ src/com/example/model/*.java \ src/com/example/util/*.java

That command must be re-assembled every time a file or dependency changes. You also have to manually download each .jar, track its version, and find its transitive dependencies — the libraries that your libraries themselves depend on. This is the dependency-hell that build tools eliminate.

The classpath problem is exponential. A single library typically pulls in 5–15 transitive dependencies. Managing 4 direct dependencies by hand often means tracking 30–60 jars, their versions, and their mutual compatibility. One mismatch causes a NoSuchMethodError or ClassNotFoundException at runtime — hours after the code compiled cleanly.

What a Build Tool Actually Does

A build tool is a program that reads a declarative description of your project — what it depends on, how it should be compiled, and what artefact it should produce — and then executes those steps deterministically. The key responsibilities are:

  • Dependency resolution. You declare what you need (e.g., Jackson 2.15.2). The tool fetches the jar and all its transitive dependencies from a central repository, caches them locally, and puts them on the classpath automatically.
  • Compilation orchestration. The tool discovers all source files, compiles them in the correct order, and only recompiles what has actually changed (incremental builds).
  • Testing. Build tools integrate with JUnit 5 and other test frameworks. mvn test or gradle test compiles test sources, runs all tests, and produces a report — one command, no manual setup.
  • Packaging. The tool assembles compiled classes, resources, and dependencies into a deployable artefact — a .jar, a .war for app servers, or a fat/uber-jar with all dependencies bundled inside.
  • Lifecycle management. Steps are ordered: clean → compile → test → package → install → deploy. Skipping or misordering them is not possible by accident.

Reproducibility and the Build as Code

Perhaps the most important property of a build tool is reproducibility. The build descriptor — pom.xml for Maven, build.gradle for Gradle — is committed to version control. Any developer on any machine runs the same command and gets the same output.

Reproducibility is a professional requirement. CI/CD pipelines, Docker images, and security audits all depend on being able to rebuild the exact same artefact from source. A project without a build tool cannot guarantee this — the "works on my machine" problem is a symptom of missing reproducibility.

Compare the two approaches side by side:

  • Manual javac: no dependency record, manual jar downloads, classpath assembled by hand, steps documented in a README that quickly drifts from reality.
  • Maven / Gradle: all dependencies declared with exact versions, fetched automatically, build steps defined in code, same output on every machine in every environment.

Packaging: From Classes to a Runnable Artefact

Compiling produces .class files scattered across directories. Shipping those files to a server is impractical. Build tools automate packaging into a single deployable unit.

The most common artefact types:

  • Thin jar — contains only your compiled classes and resources. The runtime classpath must supply all dependencies separately. Used inside application servers and frameworks that manage the classpath themselves.
  • Fat jar / uber-jar — all dependencies are embedded. You ship one self-contained file: java -jar myapp.jar. The Spring Boot and Quarkus default. Simple to deploy but larger in size.
  • WAR — a web application archive for deployment into a servlet container (Tomcat, Jetty). Dependencies go inside WEB-INF/lib/.

Building a runnable fat jar with Maven is as simple as adding the maven-shade-plugin to the POM and running one command:

mvn package # produces target/myapp-1.0.0.jar # run it anywhere Java is installed: java -jar target/myapp-1.0.0.jar

The Two Dominant Tools: Maven and Gradle

The Java ecosystem has two mature build tools. You will encounter both in professional projects.

  • Maven — convention over configuration, XML-based POM, strict lifecycle phases, enormous plugin ecosystem, the industry standard for enterprise Java for two decades. Excellent for teams that want predictable, boring builds.
  • Gradle — Groovy or Kotlin DSL, highly flexible, incremental and cached builds, the default for Android and increasingly for large multi-module server projects. Faster at scale, more expressive, and requires more discipline to keep consistent.
Which should you use? For a new project without constraints, Gradle with the Kotlin DSL is the modern choice — faster, type-safe, and well-supported. If you join an existing enterprise project, it will almost certainly use Maven. Learn both; the underlying concepts (dependencies, lifecycle, plugins) transfer directly.

Summary

Raw javac scales poorly beyond trivial projects. Build tools solve the three core problems of professional Java development: dependency management (fetch and resolve libraries automatically), compilation (incremental, ordered, classpath-correct), and packaging (produce a deployable artefact). They also make builds reproducible — the same descriptor produces the same output on every machine, which is the foundation of CI/CD, team collaboration, and production deployments. The next lessons explore Maven and Gradle in depth.