Mavenizing our code base

Maven is a build tool for Java. It’s more than that actually, but let’s just call it a build tool.

Maven - Welcome to Apache Maven-1

In outbrain we decided we want to replace good old ant with maven.

Changing the company wide used build tool is not a decision taken lightly and may have consequences on product release cycle, but we weighted our options and decided to go for it, so I thought it might be worth mentioning our endeavor.

Everyone familiar with Java programming has probably used ant or at least heard of it. For many years it has been the de-facto standard build tool with large and growing audience, numerous plugins, excellent documentation and IDE support (for example most Java IDEs can automatically generate ant build files). But ant has its shortcomings which we, at outbrain decided we just couldn’t live with. We found maven to fill up most of the gaps.

How do ant and maven differ?

Maven is newer and was built from scratch with many of the lessons learned by ant in mind. Both projects are written and maintained by the high quality, high standard apache software foundation, home of many other wonderful open source products. Both ant and maven are still actively developed and maintained, so it would not be fair to say that maven replaces ant, though many developers tend to think so (including myself).

Ant and maven differ in many ways, but at least to me these are the winning points that actually make the difference and made choose maven:

Maven is declarative. Ant is imperative.

Here’s what an ant build file looks like for a simple java project:

<project name="MyProject" default="dist" basedir=".">
  <description>
    simple example build file
  </description>
  <!-- set global properties for this build -->
  <property name="src" location="src"/>
  <property name="build" location="build"/>
  <property name="dist"  location="dist"/>
 
  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>
 
  <target name="compile" depends="init"
    description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"/>
  </target>
 
  <target name="dist" depends="compile"
    description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}/lib"/>
 
    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>
  </target>
 
  <target name="clean"
    description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>

And here’s the maven one:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <artifactId>MyProject</artifactId>
  <name>MyProject</name>
  <description>simple example build file</description>
  <packaging>jar</packaging>
</project>

Ant line count: 22 (not including spaces, comments)

Maven line count: 7. (and they are real easy ones)

Short is good, especially in software (except perl ;-) ). But except for the fact that mvn has shorter build files, there is something more important hidden here – declarative v.s. imperative.

With ant you have to tell it HOW to build. You have to tell it that it first needs to collect all java sources, then run the javac compiler on them, then collect all classes, then run the jar tool on them to make them a jar. That’s tedious, especially if you have to do it 30 times, for each and every project. In outbrain we have many projects so when we used ant you’d see the exact same ant code patterns again and again (including the mistakes, which survived copy-paste horror). Ant is hard to maintain and is also hard to write. Did any of the readers ever write an ant build file? I doubt that. I’ve been using ant for more than 5 years and never have I written a file from scratch. It’s always used to be either copy-paste or using the IDE support for automatic generation. This is a bad sign of superfluous language.

Maven is declarative. You don’t have to tell it HOW to build, only WHAT to build, so conceptually it’s a higher level build tool. With mvn you only have to say “Look, this is how I call my project and I want you to make a jar from it” that’s all, mvn will figure out the rest. It knows where to find the sources (convention) and it knows what steps it needs to take in oder to create a jar. It will compile your source files, will package all resources for you in that jar, will run tests and create that jar for you. You can intervene with this process, but you don’t have to. You can make jars, wars, ears and more.

Declarative is in many cases preferred over imperative. Think HTML vs. Java. HTML is declarative, Java is imperative. In HTML you say <b>bold</b> which tells the browser you want the text to be bold. You don’t tell it how to make the text bold (e.g. how many pixels, what position etc) only that you want it bold and let the browser figure out how to handle it. In Java you’d have to tell it how to make the text bold, how to space the characters, how to space words around it, how to break lines etc. Declarative is in many cases a lot easier than imperative, you worry less.

Maven is declarative, you only have to say “this is my project, jar it”. With ant you have more control over what the build tool does, so you can go crazy with build scripts and… well… jar before compile, or clean after jar (instead of before it) or package the test code inside production code or whatever, you get the point, you have the freedom to err. 9 times out of 10 you don’t need the level of flexibility provided by ant and you’d be much safer using mvn.

Dependency management.

This is a big thing. That was actually the main reason I wanted to move out of ant. Maven has a wonderful dependency management system built in by default. Ant has nothing built in, although it has ivy as a plugin.

What is dependency management? There are external and internal dependencies. External dependencies are ones you download from the net, usually open source projects such as Lucene, ActvieMQ, and other open source projects. With maven you only have to declare your dependency on them and they get automatically downloaded. Example:

<dependency>
  <groupId>struts</groupId>
  <artifactId>struts-bean</artifactId>
  <version>1.2.8</version>
</dependency>
With ant you basically have two options. One is download the library yourself and throw it in some folder, call it 3rdParty and add it to the classpath (good luck with keeping track of versioning, who’s using what and your life) or use ivy, which is pretty decent, but as mentioned before not part of the default ant installation.
As for internal dependencies, which means your project which depends (uses) another one of your projects, mvn supports that as well. AFAIK ant does not. With ant, if you have more than one project in your company (and of course you do), you’d have to manually tweak the build scripts so they run in the correct order and dependencies are compiled before they are used. Although it’s possible, that’s sort of nightmarish as companies grows.
Dependency management is a killer feature for mvn and was actually the main reason that prompted me to pursue it. Although ant can have ivy, this was not zero-work, so I decided, heck it we’re going to put some work into it, let’s rebuild the whole thing and get a much better result. So we did.

Conventions vs. configuration

Conventions are good. They save a lot of time and prevent you from doing foolish mistakes (such as packaging test code into production).

By conventions I mean:

  • Where is the source code?
  • Where is the test code?
  • Where are the resources?
  • Where are the web files?
  • etc

With ant you had to create a directory for sources (call it src or Src or source or srce) and tell and where your sources are. Then you’d have to decide where to put your tests. You can push them in tst, test, tests, or even in the same source directory as production code is, maybe in a test package. Next you have to configure ant where to find test code, how to separate it from production code, and heck – how to run it (it really doesn’t know how to do it).

With mvn that’s much easier. Maven promotes conventions such as the standard directory layout which means all sources are at predefined location, all resources are as well etc. There are several advantages to that including that it’s easy to start a new project, you don’t have to think where everything goes, it’s hard to make mistakes by misplacing items, you don’t have to think about the build script and how to configure it and perhaps most important of all, you make all company employees conform to the same layout. That’s a huge gain for the company.

Built-in functionality out of the box

OK, there are plenty of other benefits to mvn but the post is already getting too long so let’s have the last one here.

With mvn you get tons of functionality out of the box. By creating a very simple build file with only the project definition in it you can:

  • compile all sources
  • compile test
  • run unit tests
  • run integration tests
  • package as a jar/war or something else
  • deploy
  • run in tomcat
  • …and much more

With ant what you had to do is for each one of the above listed goals, create an ant goal and configure ant by telling it how to run it. Some of them may be relatively trivial (but still require coding) and some of them aren’t easy at all… that kind of tells you why anters are very good with their CTRL+C and CTRL+V. And with copy-paste comes the pain of silly copy paste errors and difficult maintainability. Life with mvn is better ;-)

Other great and not covered features: Eclipse and other IDE integration (automatically generating projects), great testing and debugging tools, excellent build output, excellent versioning and more.

How did it go?

So, how did it go? You guys have converted your entire codebase build tool. Isn’t that like Netscape’s near death experience while rewriting their entire code base?

Well… no! The nice thing about mvn is that it’s easy to write and easy to learn. We did spend a couple of weeks on that task and had to resolve some unpredictable situations, but it had only a small impact on our schedule (and needless to say that I hope in the long term will have  the most positive impact on release schedule). Heck, we (at least I) even enjoyed it!

Conclusions

I’d definitely recommend mvn. If you’re starting a new project, choose mvn. If you have an existing codebase using ant and you’re thinking about moving to maven, know that it’s certainly feasible and in my opinion, well worth the effort. Expect some work, it’s not zero effort, but I promise you’ll enjoy it.

One Response to “Mavenizing our code base”

  1. Very nicely put Ran.

    Still, in some companies where there’s tons and tons of legacy code, some no longer maintained by anyone… it may be quite difficult to make that move.

    It would have been nice to be able to iteratively migrate your code between one build to another, but it is usually pretty hard – you have to maintain 2 sets of “dependency management data”.

    By Eran on Oct 24, 2009

Sorry, comments for this entry are closed at this time.