Jan 14

How to be agile when all about you are not – part 5


In part 4 of this series
, I explained how continuous integration is used to automate the build process. In this article, I shall go on to explain how to further apply the DRY principle with fully automatic standardised build processes that generate code for you.

One of the biggest problems with build scripts is that they are tailored to a specific system and cannot be reused. Furthermore, every developer (and IDE) has their own ideas about how a project should be laid out; where compiled code should be placed and where the dependencies are stored. In some cases there are very good reasons why code is where it is, but more often than not it is because of some kind of workaround to overcome a limitation of the build process.

For a long time, Ant was the king of the Java build process. It is present in just about every mainstream IDE that supports Java and is a solid and reliable workhorse in the development process. However, Ant has many limitations (dependency management and script standardisation being two of the main ones) which is why the developers of Ant moved on to create Maven.

The idea behind Maven is to provide a unified build process that can be applied to just about any project of any size in an intuitive manner. It favours certain conventions that if followed makes your project immediately accessible to anyone else who knows Maven. Learning curves drop considerably for new developers joining the team since they will already know where to find code and how the build process works. Maven is extensible via plugins so that esoteric build requirements can quickly be catered for, and a large number of standard plugins are available for free. These include plugins for creating EAR files, automatically generating code, linking back to Ant scripts and just about every other purpose you could think of.

So how is this in line with the DRY principle? By providing a single point of reference for all project build information that is defined in a consistent format: XML. When I say all project build information, I mean it. Apart from the obvious “project name” and “version” there is a plethora of other information: who the developers are along with contact information; how to run automated tests and where to publish the results; how to generate the javadocs and where to publish them; how to manage versioned releases; how to generate the overall website associated with the project and where to publish it; and, most importantly of all, how to manage the project dependencies.

And in that closing entry lies the distillation of the DRY principle: a single point of reference for all the dependencies of the system, along with their versions so that they do not need to be stored within the project any longer. For many developers this comes as a great relief because there is no need to work out which version of, say, cglib.jar is copmatible with both the Spring 2.5.5 and Hibernate 3.3 frameworks. Instead, all Maven needs to know is what version of Spring you’d like, and which version of Hibernate and it works out the rest.

Maven also provides a very simple but powerful mechanism for avoiding repetition with the project file itself. For example, if the project has an overall dependency on Spring this is actually implemented as a small collection of dependency entries containing the precise modules of Spring that are needed (say core, transactions, web and MVC). If done without thought then there will be 4 places where the version, say 2.5.5, is referenced. However, the designers of Maven have provided a properties mechanism so that this repetition is confined to the name of a property that contains the appropriate version. Thus, to change from 2.5.5 to 2.5.6 all that is required is a simple change in the value of the property and all places where that property is referenced will be automatically updated. In a large build with many modules this saves an enormous amount of work and creates a very orderly build system.

At this point you have a very flexible and easy to maintain build system and a comprehensive set of automated tests that is providing protection against brittle code. So what next? Plenty. You have an agile build process, but you probably don’t have an Agile approach to design and implementation. For that you need to follow the trail “How to be an agile project manager” (coming soon).

Share
Tagged with:
Jan 09

How to be agile when all about you are not – part 4

In part 3 of this series, I explained how automated testing can be used in the build process to greatly reduce the time taken to verify that new code is correct. I also introduced the idea of refactoring to make code easier to test. In this article, I shall go on to explain how to further improve the build process with continuous integration.

The idea behind continuous integration is simple, and stems directly from automated testing. There should be an automated process that continuously checks out the latest code and attempts to build and test it, reporting any failures automatically to the person who last made a commit since they are most likely to have broken the build.

Although this sounds daunting, it’s actually trivial to set up. All that is required (and again I speak from a Java based perspective) is:
1) the free and open source application Hudson
2) the free and open source servlet container Tomcat
3) the free and open source build tool Ant

You’ll doubtless have noticed a distinct bias on my behalf on free and open source software. If you’d like more information on why this is a Good Thing then check out this article about it.

All of those applications are a snap to install and get running. Once Hudson is up and running (it’s packaged as a self-contained WAR file), it is merely a matter of creating a job and configuring it with the version control details and the location of the build script file. I would recommend, though, that the machine on which these applications are installed (I’ll call it the continuous integration server, or CI server for short) is fairly beefy since it will very quickly start to be used by everybody for everything. And therein lies a problem.

At this point it is difficult to introduce the Agile methodology by stealth, since you’re going to need a server somewhere and that means involving other people. Of course, you could just install said CI server on your own machine and let it run overnight which is perfectly acceptable up to a point. Out of the box, Hudson will perform a fairly basic set of operations: check out the code at a particular time (or interval); attempt to run the build script; note any test failures and report back with a green light if all is well.

However, Hudson has an extensible architecture that allows plugins to be created and there is a rich tapestry of possible add-ons available from the supporting website. There is a plugin for the FindBugs application which can identify code that is likely to cause problems by an examination of the byte code. Another keeps track of code coverage so that weak tests can be readily identified. Still another identifies long term trends associated with the number of tests and number of failures.

The list is long and before long the CI server will become as vital to the build process as the version control system, and clearly it will need to be given the same level of support from the network administrators. All being well, if you adopt the nightly build approach based on your own machine, then it won’t be long before you are starting to gather very useful data about the build process. Managers love this kind of information so it should be a fairly painless transition to move Hudson from your development machine to a managed server.

So by now the gradual changes to the development process have started to show real results and others are starting to take notice. It seems that your code is getting created faster than everyone else’s and it almost always works first time. And you’re identifying weak spots in the main application that no-one has ever seen before. Not bad, but surely there is more to it.

In part 5 of this series I will explain the next major shift: automating everything.

Share
Tagged with:
Jan 09

How to be agile when all about you are not – part 3

In part 2 of this series, I explained how the DRY principle can be used in the build process and how the need for automated testing naturally arises from this. In this article, I go on to explain how to implement automated testing in Java using Ant and JUnit.

The subject of automated testing is vast, with many different philosophies and naming conventions springing up around it and a full explanation would take ages. [Include links to other sites]. However, it is well proven that the introduction of automated testing greatly improves the quality of a code base because of what is necessary to support it. There are 3 pre-requisites to successful automated testing:

1) an automatic build system
2) some kind of automated testing framework
3) an open design allowing small parts of the system to be tested in isolation

For the vast majority of projects, some kind of automated build system is already in place within the IDE. In the case of Java this is Ant, and typically the build script is automatically generated as a result of the various options that are selected within the IDE for the project.

Although it’s obvious, it still needs to be said that test code should not be mixed with production code, and that includes the supporting libraries. There are several ways to avoid this, but the best way is to introduce a new source tree that only contains test related classes and resources.

If you are a Java developer, the Maven convention is to define a sub-directory under the main source tree as follows: src/test/java, with production code going under src/main/java. I would strongly urge you to follow this convention as Maven is growing steadily as a replacement for Ant. Test classes then have the same package as the class under test.

The most widely used automated test framework in the Java world is JUnit (NUnit is used in the .Net world). At the time of writing version 4 is well-established and I would strongly urge you to use it. All that is required is the inclusion of a simple dependency into your project and you’re done. If you want to find out more about JUnit here is a good starting point.

The final step is modifying the existing code to support automated testing. This can be easy or hard depending on how well designed the legacy code is. If good programming practices have been followed, then automated testing is straightforward. There will be strong separation between classes, with each class performing a few well defined tasks that contribute to an overall cluster of functionality. Navigation around the cluster of objects will be straightforward since they will have clear naming conventions and follow some kind of established design pattern.

Time to refactor
For the rest of us, it’s time for some hard graft in the form of refactoring. This is the process by which code is rewritten to conform to a higher standard of design principles without changing it’s functionality. If done correctly the outside world is unaware of any change being made, while internally the code is much cleaner, succinct and therefore easier to maintain.

There are many references on the subject of refactoring (here is a good starting point), and your IDE will definitely support a wide variety of refactoring operations. Essentially your goal in refactoring is to break the code apart into smaller testable units that can be instantiated without reference to any of their surrounding classes. Or, if a reference must exist for a unit to work, that the reference is supplied from outside the class, i.e. the test class. This is known as Dependency Injection or Inversion of Control (IOC). A quick way to identify if you’re using IOC is to see if any of the dependency objects are created with the new operator, or are static references to utility classes. Either of these will mean some refactoring to introduce getters and setters so that external classes can pass in the instances for these dependencies.

So, instead of

private Worker worker = new Worker();

you have

private Worker worker = null;

public void setWorker(Worker worker) {
  this.worker=worker;
}

and now the test class can provide an alternative implementation of the Worker class which behaves in a predictable manner suitable for testing. The above can be achieved by means of the Encapsulate Fields refactoring (which is really for converting public fields into private ones with a setter, but never mind you can see what I’m trying to get across here).

The best approach to refactoring is to target a single change at a time. Your goal is to isolate each class as much as possible with the IOC pattern through a series of field conversions – pushing the external dependencies to the outer class that control the one you’re working with. At this point, the class is ready for automated testing.

Let’s get back to our fictional developer, Bob, and assume that he’s made these refactorings to his code to introduce the new credit card support (see part 2 for a fuller description of this). Instead of the inefficient and laborious process that he previously had to go through, the new approach is something like this:
1) Find the credit card payment system in the code and it’s associated automated tests
2) Modify the tests to include the new credit card and a wide range of test data that should induce failures
3) Run the tests and note any failures (the first time will definitely fail since there is no implementation code)
4) Fix the failures and repeat from step 3) until it’s all good
5) Perform an update from version control and re-run the tests
6) If it’s all OK, check it in

As you can see the cycle time between making a test and seeing the result of it is greatly reduced to a matter of seconds rather than minutes. Also, once the automated test has been written then every time the system is built then it will perform the same checks, over and over again forever. So now, if someone makes a change to that part of the system guarded by the test which causes the test to fail then there is a clear path to what went wrong and how to to fix it. The failure is caught early on in the development cycle and that is the cheapest point to fix it. Also, if the developer introducing the failure is regularly running a build that includes the test then they will remember what change they made that caused the failure in the first place and be quicker to debug and fix it.

In part 4 of this series I will explain what happens next to further improve the build process by introducing the idea of continuous integration.

Share
Tagged with:
Jan 09

How to be agile when all about you are not – part 2

It’s best to start with something familiar and slowly modify it to become more Agile in nature. So let’s start with a typical medium scale project – an imaginary airline booking system – and a typical developer who is part of a team, who we’ll call Bob. He’s been given the task of introducing a new credit card into the overall payment acceptance system, and he’s keen to introduce an Agile approach.

Now, this airline booking system, like thousands of other systems all over the world has been built in a more or less ad hoc fashion. A wide range of developers, some highly skilled, others less so, have stamped their individual style on the code base leaving it a patchwork of different approaches. There is little or no documentation available outside of the code apart from reams of meaningless specification documents that have been more or less ignored since they were created. The code contains a fair amount of comments, and developers try to keep them in step with the code but it is very difficult to gain a big picture perspective on how it all fits together.

Does any of this sound familiar? I imagine that it does because the overwhelming majority of systems that I have encountered all follow that pattern. It is the classic legacy system. It is not legacy because it uses old technology, it is legacy because it is impenetrable, untestable in any meaningful way and extremely brittle meaning that any form of change is going to cause problems in random places.

So I’ll gloss over the details of how Bob actually implements his update, but suffice to say that he follows this process:
1) Fire up a local copy of the system with a debugger attached
2) Work through the user interface making a flight booking until he reaches the payment page
3) Enter test data and note any problems
4) Implement the fixes in the code and rebuild
5) Repeat from step 1 until the code works as expected
6) Check it into version control

Anyone who has any familiarity with Agile practices will look at the above list and rightly shudder, but it is all too common because it is the most obvious way of meeting the objective.

How could Bob apply the DRY principle to the above?

The first step is to isolate any repetitive manual tasks that are involved. In this case it’s the endless building and stepping through the application that is causing all the time and enjoyment to be sucked away. Fortunately, there is a well known solution to this problem: automated testing.

In part 3 I will demonstrate how to organise your project and build system to support automated testing.

Share
Tagged with:

You should follow me on TwitterYou should follow me on Twiiter here.

preload preload preload