Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Apache Ant for Legacy Pipelines

Posted on May 3, 2006 By Luis Fernandez

A quick story from the build room

Yesterday a teammate spent an hour chasing a missing jar only to realize the classpath was set on his laptop but not on the build server. We moved the rule into Apache Ant and the next run just worked. No more tribal lore. Just a script that speaks the same on every box.

Why Ant still earns a spot in legacy pipelines

Apache Ant is not shiny, and that is exactly its charm for legacy pipelines. Teams sit on a mix of CVS and Subversion, a few home grown scripts, and a server that nobody dares to reboot. Maven 2 is making noise, but many apps carry hand made layouts that do not fit a fixed convention. Ant thrives here. It is plain XML, declarative tasks, and you can wire anything you need. Pair it with CruiseControl or a nightly cron job and you have a simple path to repeatable builds that is easy to read and easy to fix at two in the morning.

Make Ant your single source of truth

Put every step in build.xml and cut the snowflake steps from developer machines. Declare a clear default target that anyone can run. Use properties for versions and paths so a release change is one edit, not a hunt through tasks. Keep classpath in a named path with a ref id and build it from a lib folder under version control. Wire tasks with depends so the graph reads top to bottom. Targets should be idempotent clean can run twice and not break. Small rule of thumb, if a step needs env tweaks you probably want that expressed as an Ant property.

A compact Ant script you can ship today

<project name="legacy.pipeline" default="package">

  <property name="src.dir" value="src"/>
  <property name="build.dir" value="build"/>
  <property name="dist.dir" value="dist"/>
  <property name="lib.dir" value="lib"/>
  <property name="reports.dir" value="reports"/>

  <path id="cp">
    <pathelement location="${build.dir}"/>
    <fileset dir="${lib.dir}" includes="**/*.jar"/>
  </path>

  <macrodef name="compile.java">
    <attribute name="src"/>
    <sequential>
      <javac srcdir="@{src}" destdir="${build.dir}" debug="true" source="1.5" target="1.5"
             includeantruntime="false" classpathref="cp"/>
    </sequential>
  </macrodef>

  <target name="clean">
    <delete dir="${build.dir}"/>
    <delete dir="${dist.dir}"/>
    <delete dir="${reports.dir}"/>
  </target>

  <target name="prepare">
    <mkdir dir="${build.dir}"/>
    <mkdir dir="${dist.dir}"/>
    <mkdir dir="${reports.dir}"/>
  </target>

  <target name="compile" depends="prepare">
    <compile.java src="${src.dir}"/>
  </target>

  <target name="test" depends="compile">
    <junit printsummary="yes" haltonfailure="yes">
      <classpath refid="cp"/>
      <formatter type="plain"/>
      <batchtest todir="${reports.dir}">
        <fileset dir="${src.dir}" includes="**/*Test.java"/>
      </batchtest>
    </junit>
  </target>

  <target name="package" depends="test">
    <jar destfile="${dist.dir}/app.jar" basedir="${build.dir}"/>
  </target>

  <target name="deploy" depends="package">
    <copy file="${dist.dir}/app.jar" todir="/opt/apps/app" overwrite="true"/>
  </target>

</project>

This sample shows a macrodef so compile flags live in one place. Tests run with JUnit and fail fast on red. The jar is produced only after tests pass. Replace the deploy copy with an ssh task or an rsync task if your server sits on another box. If your app is in Java 5, adjust source and target as needed. Keep third party jars in lib and commit them or use Ivy if you want dependency retrieval without moving to a full stack tool.

Fit Ant into your team flow

Tie the default target to your continuous integration and to developer laptops. CruiseControl runs ant with the same properties as your local shell. Use profiles through property files like build.dev.properties and build.prod.properties and load them with property file. Stamp builds with a property not with a random timestamp or you will miss cache hits. For source control use a clean checkout for every build so the tree matches the tag. Keep the release recipe inside Ant with a target that tags in Subversion or CVS then builds from that tag. That gives you a repeat you can audit.

Ant patterns that age well

Favor depends over antcall so the graph is visible. Keep targets small and name them like a verb. Avoid shell scripts that wrap Ant just to pass a couple of flags. Use property defaults and let the command line override when needed. Put preflight checks early with the condition task and the fail task so bad state explodes fast. Most of all write the build like code. Remove duplication with macrodef, keep things under version control, and review builds like you review production code.

Summary

Apache Ant is a steady hand for aging code and mixed toolchains. It turns private rituals into a script that runs the same everywhere. If your world is a little messy, Ant gives you control without forcing a new structure. Start with a clean build.xml, wire compile test package and deploy, and let your legacy pipeline breathe again.

Development Practices Digital Transformation Productivity & Workflow Software Engineering

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes