<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en">
  <title>Ivan</title>
  <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/" />
  <modified>2009-02-21T16:54:14Z</modified>
  <tagline>Ivan Moore - Putting the Tea into Team.</tagline>
  <id>tag:,2009:/10</id>
  <generator url="http://www.movabletype.org/" version="3.31">Movable Type</generator>
  <copyright>Copyright (c) 2009, ivan</copyright>
  <entry>
    <title>My blog has moved</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000753.html" />
    <modified>2009-02-21T16:54:14Z</modified>
    <issued>2009-02-21T16:53:44+00:00</issued>
    <id>tag:,2009:/10.753</id>
    <created>2009-02-21T16:53:44Z</created>
    <summary type="text/plain">Joe Walnes has hosted my blog for many years - he&apos;s great. Now the time has come for him to retire his server, and so my blog has moved....</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p><a href="http://joe.truemesh.com/blog/">Joe Walnes</a> has hosted my blog for many years - <a href="http://puttingtheteaintoteam.blogspot.com/2008/10/blog-moved-here.html">he's great</a>.</p>

<p>Now the time has come for him to retire his server, and so my <a href="http://puttingtheteaintoteam.blogspot.com">blog has moved</a>.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>New article on new blog - &quot;Is that a POJO or a NOJO?&quot;</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000750.html" />
    <modified>2008-10-23T21:38:21Z</modified>
    <issued>2008-10-23T21:26:21+00:00</issued>
    <id>tag:,2008:/10.750</id>
    <created>2008-10-23T21:26:21Z</created>
    <summary type="text/plain">Joe Walnes has hosted my blog for many years - he&apos;s great. Now the time has come for him to retire his server, and so my blog has moved. Here&apos;s a new blog article on NOJOs - I hope you...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p><a href="http://joe.truemesh.com/blog/">Joe Walnes</a> has hosted my blog for many years - <a href="http://puttingtheteaintoteam.blogspot.com/2008/10/blog-moved-here.html">he's great</a>.</p>

<p>Now the time has come for him to retire his server, and so my <a href="http://puttingtheteaintoteam.blogspot.com">blog has moved</a>.</p>

<p>Here's a new blog article on <a href="http://puttingtheteaintoteam.blogspot.com/2008/10/is-that-pojo-or-nojo.html"><span class="caps">NOJO</span>s</a> - I hope you like it.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>CITCON 2008 - CI SmackDown</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000749.html" />
    <modified>2008-10-07T18:55:39Z</modified>
    <issued>2008-10-06T07:26:29+00:00</issued>
    <id>tag:,2008:/10.749</id>
    <created>2008-10-06T07:26:29Z</created>
    <summary type="text/plain">This session was a demo of several CI servers. CruiseControl, build-o-matic, Hudson, JetBrains TeamCity, Rational BuildForge, Zutubi Pulse2, Cruise. I presented first - build-o-matic. I think my talk was a bit rubbish because I didn’t get across the USP of...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>This session was a demo of several CI servers.</p>

<p>CruiseControl, build-o-matic, Hudson, JetBrains TeamCity, Rational BuildForge, Zutubi Pulse2, Cruise.</p>

<p>I presented first - <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a>. I think my talk was a bit rubbish because I didn’t get across the <span class="caps">USP </span>of build-o-matic. What is the basics of a CI server? I think one of the basics is to tell you which commit broke the build. </p>

<h3>What's the <span class="caps">USP </span>of build-o-matic?</h3>

<p>If there are multiple commits between builds, then build-o-matic is the only CI server that finds out which commit broke the build (<a href="http://ivan.truemesh.com/archives/000730.html">farm</a> and <a href="http://ivan.truemesh.com/archives/000694.html">binary search</a>). None of the others do, or even have the infrastructure to do it (apart from the <span class="caps">EAP </span>version of TeamCity sounds like it now has the infrastructure). Note that Pulse and TeamCity have a valid alternative approach to the problem with “personal builds” - which (I have used the TeamCity version of this feature) is quite cool.</p>

<h3>Why re-invent the wheel?</h3>

<p>I was asked after the talk why I had bothered to write my own CI server. Well, when I wrote the first version of build-o-matic, most of the other CI servers either didn't exist (eg Hudson) or were so new I hadn't heard of them (eg TeamCity). The ones that did exist didn't do what I wanted.</p>

<h3>Why not plugin to the trendy new CI servers?</h3>

<p>I was also asked why I didn't write a plugin for Hudson or TeamCity to achieve the same goals as build-o-matic. I don’t believe it would be possible to write a plugin for Hudson or the current version of TeamCity to do it, because they have no concept of running the build on a previous revision of the code (although the next version of TeamCity, the <span class="caps">EAP </span>version, will - so it may be possible with that). </p>

<h3>What did I think of the competitors?</h3>

<p>I have used TeamCity and think it's very good. Cruise looked very interesting. I heard good things about Pulse from people who had tried it. I can't see what the fuss is about Hudson (maybe I'm just not trendy enough). The others looked very complicated.</p>

<h3>The future of build-o-matic</h3>

<p>I'm not sure whether it has a future. The other CI servers have teams of people working on them full time (even though they still can't tell you which commit broke the build) and have lots of enterprise friendly features. I might keep working on build-o-matic - but many of the features that I'd like it to have are already in TeamCity. Talking to Yegor Yarko of JetBrains - the makers of TeamCity - it sounds very much like TeamCity is going to catch up with build-o-matic on it's currently unique features in the next version, which makes me wonder whether it's worth continuing with build-o-matic.</p>

<h3>Comments</h3>

<p>Jason Sankey wanted to add a comment - but unfortunately comments are switched off - so he sent me an email, which I've included here:</p>

<p>It was cool to see the binary search for the breaking build revision in build-o-matic.  This is a feature that has been on our wish list for Pulse for a while, and in particular our customers with long builds would like it.  I've yet to see anyone else who has actually done this.</p>

<p>Since the earliest versions of Pulse we have included a feature called "changelist isolation", which will trigger builds for each individual revision -- so if you have the resources to run a build on every revision you will be able to easily see when the build was broken. Obviously we don't advertise this feature enough!</p>

<p>It is also possible to trigger builds at a specified revision using the Pulse web UI or remote (XML-RPC) <span class="caps">API, </span>so in theory you could script the binary search externally and drive Pulse to build the desired revisions.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>CITCON 2008 - flickering builds</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000747.html" />
    <modified>2008-10-06T16:12:07Z</modified>
    <issued>2008-10-05T15:29:42+00:00</issued>
    <id>tag:,2008:/10.747</id>
    <created>2008-10-05T15:29:42Z</created>
    <summary type="text/plain">I&apos;ve just come back from CITCON 2009. I facilitated a session there on what causes random build failures, and solutions that people have used, or could potentially use, to deal with them. Rather than writing up in two places -...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>I've just come back from <a href="http://citconf.com/wiki/index.php?title=Main_Page#CITCON_Europe_2008_Amsterdam_Netherlands"><span class="caps">CITCON</span> 2009</a>. I facilitated a <a href="http://citconf.com/wiki/index.php?title=FlickeringBuilds">session</a> there on what causes random build failures, and solutions that people have used, or could potentially use, to deal with them.  Rather than writing up in two places - I've put the write up on the <a href="http://citconf.com/wiki/index.php?title=FlickeringBuilds"><span class="caps">CITCON </span>wiki</a> and linking to it here.</p>

<p>I enjoyed this session - but that's probably not too surprising as I really wanted some answers! Flickering builds are a curse.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>Nasty bug in selenium</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000744.html" />
    <modified>2008-08-03T09:57:00Z</modified>
    <issued>2008-08-03T09:55:02+00:00</issued>
    <id>tag:,2008:/10.744</id>
    <created>2008-08-03T09:55:02Z</created>
    <summary type="text/plain">On Friday I came across a nasty bug in selenium (but I still like it). Selenium wouldn&apos;t start The build was failing - and the reason was the last thing I expected (of course). The usually reliable selenium server just...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>On Friday I came across a nasty bug in <a href="http://selenium.openqa.org/">selenium</a> (but I still like it).</p>

<h3>Selenium wouldn't start</h3>

<p>The build was failing - and the reason was the last thing I expected (of course). The usually reliable selenium server just wouldn't start. Looking at the stack trace, then searching for likely looking parts of it (btw, for the search I tried, google, yahoo and altavista find the answer, but msn, cuil and askjeeves don't. Just thought I'd try them out because of the launch of cuil).</p>

<p>I found the <a href="http://clearspace.openqa.org/message/46309">answer</a> (I think you can turn the popup blocker back on again too - I think the problem is because the registry entry is missing rather than because of its value).</p>

<p>The stack trace is like this (in case you are searching too):</p>



<pre>
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.String.substring(Unknown Source)
at org.openqa.selenium.server.browserlaunchers.WindowsUtils$RegKeyValue.&lt;init&gt;(WindowsUtils.java:622)
at org.openqa.selenium.server.browserlaunchers.WindowsUtils.deleteRegistryValue(WindowsUtils.java:577)
at org.openqa.selenium.server.browserlaunchers.WindowsProxyManager.handleEvilPopupMgrBackup(WindowsProxyManager.java:106)
at org.openqa.selenium.server.browserlaunchers.WindowsProxyManager.init(WindowsProxyManager.java:81)
at org.openqa.selenium.server.browserlaunchers.WindowsProxyManager.&lt;init&gt;(WindowsProxyManager.java:65)
at org.openqa.selenium.server.browserlaunchers.InternetExplorerCustomProxyLauncher.&lt;init&gt;(InternetExplorerCustomProxyLauncher.java:48)
at org.openqa.selenium.server.browserlaunchers.InternetExplorerCustomProxyLauncher.&lt;init&gt;(InternetExplorerCustomProxyLauncher.java:41)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory.createBrowserLauncher(BrowserLauncherFactory.java:124)
at org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory.getBrowserLauncher(BrowserLauncherFactory.java:81)
at org.openqa.selenium.server.htmlrunner.HTMLLauncher.runHTMLSuite(HTMLLauncher.java:87)
at org.openqa.selenium.server.htmlrunner.HTMLLauncher.runHTMLSuite(HTMLLauncher.java:141)
at org.openqa.selenium.server.SeleniumServer.runHtmlSuite(SeleniumServer.java:1138)
at org.openqa.selenium.server.SeleniumServer.main(SeleniumServer.java:386)
</pre>



<h3>Now that wasn't so difficult was it?</h3>

<p>The problem was caused by something I didn't do (but rather by some remote machine update thingie) which made it difficult, and the error message didn't give much of a clue (although looking at it carefully it does look like the registry value for the EvilPopupMgr can't be deleted because it doesn't currently exist).</p>

<h3>Having fixed the problem the CI build still won't work</h3>

<p>Having thought I'd fixed the problem - I fix up the CI build farm but the build still fails. Running the build manually on the CI farm machines is fine - running through the CI server isn't. The problem is that the CI server doesn't run the build as me - doh - so the "HKEY_CURRENT_USER" I've just fixed isn't the right one!</p>

<h3>Time for a cup of tea</h3>

<p>From the searches I've done it looks like I'm not the only person who has come across this problem - I hope by writing it up it'll save someone a bit of time. What would save even more time would be if the bug gets fixed - there is a suitable looking <a href="http://jira.openqa.org/browse/SRC-475">issue open</a> for this.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>SPA 2009</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000741.html" />
    <modified>2008-07-21T20:46:31Z</modified>
    <issued>2008-07-21T20:11:59+00:00</issued>
    <id>tag:,2008:/10.741</id>
    <created>2008-07-21T20:11:59Z</created>
    <summary type="text/plain">SPA 2009 is now open for session proposals. I&apos;m honored to be programme co-chair (with Mike Hill) for SPA 2009, having attended this great conference for many years. Although there are several conferences that I enjoy - SPA is unique...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p><a href="http://www.spaconference.org/spa2009/index.php"><span class="caps">SPA</span> 2009</a> is now <a href="http://www.spaconference.org/spa2009/index.php?page=lead-a-session">open for session proposals</a>.</p>

<p>I'm honored to be programme co-chair (with <a href="http://2hills1000miles.blogspot.com/">Mike Hill</a>) for <span class="caps">SPA</span> 2009, having attended this great conference for many years. Although there are several conferences that I enjoy - <span class="caps">SPA </span>is unique thanks to being residential and extremely interactive. The high proportion of attendees who are presenting, and the level of interactivity of the sessions, means that everyone is involved in the sessions - there aren't any powerpoint "sit and listen" sort of sessions. There's also a lot of interesting discussions outside of the sessions and in the evenings.</p>

<p>Please consider <a href="http://www.spaconference.org/spa2009/index.php?page=lead-a-session">submitting a session proposal</a> particularly if you've never presented before - it's a great conference to be a first time presenter at because of the shepherding and the interactivity of the sessions - good sessions don't have to start with all the answers - they are often ones that start with a good question. This year we are offering a free place to the best proposal from a new presenter. Of course, I'm also hoping for lots of proposals from experienced presenters too!</p>

<p>The submission deadline is Monday 15th September 2008.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>CITCON 2008</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000734.html" />
    <modified>2008-04-27T15:53:13Z</modified>
    <issued>2008-04-27T15:43:40+00:00</issued>
    <id>tag:,2008:/10.734</id>
    <created>2008-04-27T15:43:40Z</created>
    <summary type="text/plain">I have really enjoyed the CITCON conferences that I&apos;ve been to. CITCON is an open space conference about continuous integration and testing - excellently run by Jeffrey Fredrick and Paul Julius. CITCON 2006 in London inspired some of the more...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>I have really enjoyed the <a href="http://www.citconf.com/"><span class="caps">CITCON</span></a> conferences that I've been to. <span class="caps">CITCON </span>is an open space conference about continuous integration and testing - excellently run by <a href="http://www.agitar.com/company/management.html#jeffrey">Jeffrey Fredrick</a> and <a href="http://www.pauljulius.com/">Paul Julius</a>. <span class="caps">CITCON</span> 2006 in London inspired some of the more advanced features of <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a> (which I am really happy about). <span class="caps">CITCON</span> 2007 prompted me to produce a much simplified version of <a href="http://sourceforge.net/project/showfiles.php?group_id=23959&amp;package_id=255521">jester</a> (which I am really happy about and have had some positive feedback about). <a href="http://www.citconf.com/amsterdam2008/"><span class="caps">CITCON</span> 2008</a> is going to be in Amsterdam on 3 &amp; 4th October this year; I have registered and I'm looking forward to it - even though on past experience it'll end up prompting me to do lots of extra work as a result!</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>Programming as if the domain mattered (SPA 2008)</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000733.html" />
    <modified>2008-03-21T12:15:52Z</modified>
    <issued>2008-03-21T11:36:43+00:00</issued>
    <id>tag:,2008:/10.733</id>
    <created>2008-03-21T11:36:43Z</created>
    <summary type="text/plain">Keith Braithwaite and I presented a session called Programming as if the domain mattered at SPA 2008. I feel that the part of the session that I prepared code for didn&apos;t go great (I hadn&apos;t prepared the packaging of the...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p><a href="http://www.keithbraithwaite.demon.co.uk/professional/index.html">Keith Braithwaite</a> and I presented a session called <a href="http://www.spaconference.org/spa2008/sessions/session134.html">Programming as if the domain mattered</a> at <a href="http://www.spaconference.org/spa2008/index.php"><span class="caps">SPA</span> 2008</a>. I feel that the part of the session that I prepared code for didn't go great (I hadn't prepared the packaging of the code well enough, so it took longer for people to get set up than it should have). I think the part Keith prepared for went better.</p>

<h3>Domainification</h3>

<p>The results from the whole session came as a suprise to us. It was an experimental session from which we wanted to encourage participants to include as much domain stuff as possible (I'll call this "domainification") in some code and find how far people think this should be taken.</p>

<p>It seemed from the retrospective at the end of the session that many participants didn't feel it was worth taking the "domainification" as far as myself and Keith had expected.</p>

<p>I now wonder whether this was because of the guidance (or lack of) that we gave, due to the time being too short, or whether I just have a weird programming style (and by implication Keith too, but I shouldn't really say that!).</p>

<p>Therefore, I'm writing this to explain what I thought would happen to the code. I wonder whether people's opinions would have been different if they had had time to go further with the example. I hope this article will show participants of the session (and readers of this blog) some domainification of code to consider whether it is worth "programming as if the domain mattered".</p>

<p>There are more improvements that can be made ... and what has been done could have been done differently ... this article just shows some things that could have been done ...</p>

<h3>"Traction control" - a simple continuous integration server</h3>

<p>My example code was "Traction control" - written specifically for the session and roughly based on how the very first incarnation of <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a> worked (which is now very considerably different!). What "Traction control" does is:</p>

<p>1 an update (svn up)<br />
2 if there are changes, writes a web page with a text area for the build output<br />
3 runs the build putting the build output into the file used on the web page to show the build output<br />
4 once the build has finished, writes either a red or green web page<br />
5 go to 1</p>

<h3>The code</h3>

<p>Here's the code (with the least interesting bits missed out, and a bogus space in "&lt; script&gt;" due to rendering problem):</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		String buildCommand = args[0];
		int previous = -1;
		while (true) {
			exec(&quot;svn up&quot;);
			String output = exec(&quot;svnversion&quot;);
			int current = Integer.parseInt(output.trim());
			if (current != previous) {
				StringBuffer stringBuffer = new StringBuffer();
				stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
				stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
				stringBuffer.append(&quot;&lt;title&gt;Traction Control build running&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
				stringBuffer.append(&quot;&lt;iframe src=\&quot;buildoutput.txt\&quot; width=\&quot;600\&quot; height=\&quot;180\&quot;/&gt;&quot;);
				stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
				write(&quot;results.html&quot;, stringBuffer.toString());
				exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);
				output = readContents(&quot;buildoutput.txt&quot;);
				stringBuffer = new StringBuffer();
				boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
				stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
				stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
				stringBuffer.append(&quot;&lt;title&gt;Traction Control results&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
				stringBuffer.append(&quot;&lt; script&gt;document.bgColor=\&quot;&quot;);
				stringBuffer.append(success ? &quot;#33ff33&quot; : &quot;#ff3333&quot;);
				stringBuffer.append(&quot;\&quot;;&lt;/script&gt;&quot;);
				stringBuffer.append(&quot;Build &quot; + (success ? &quot;passed&quot; : &quot;failed&quot;));
				stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
				write(&quot;results.html&quot;, stringBuffer.toString());
				previous = current;
			}
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
			}
		}
	}
	
	// definitions of &quot;exec&quot;, &quot;readContents&quot; and &quot;write&quot; follow - you can guess roughly what they are ...
</pre>



<h3>Procedural refactoring</h3>

<p>The most horrible feature of this code is the creation of the web pages - I'll factor those out into methods:</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		String buildCommand = args[0];
		int previous = -1;
		while (true) {
			exec(&quot;svn up&quot;);
			String output = exec(&quot;svnversion&quot;);
			int current = Integer.parseInt(output.trim());
			if (current != previous) {
				writeBuildingPage();
				exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);
				writeResultsPage();
				previous = current;
			}
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
			}
		}
	}

	private static void writeResultsPage() throws IOException {
		String output = readContents(&quot;buildoutput.txt&quot;);
		StringBuffer stringBuffer = new StringBuffer();
		boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control results&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt; script&gt;document.bgColor=\&quot;&quot;);
		stringBuffer.append(success ? &quot;#33ff33&quot; : &quot;#ff3333&quot;);
		stringBuffer.append(&quot;\&quot;;&lt;/script&gt;&quot;);
		stringBuffer.append(&quot;Build &quot; + (success ? &quot;passed&quot; : &quot;failed&quot;));
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	private static void writeBuildingPage() throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control build running&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt;iframe src=\&quot;buildoutput.txt\&quot; width=\&quot;600\&quot; height=\&quot;180\&quot;/&gt;&quot;);
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}
		
	// definitions of &quot;exec&quot;, &quot;readContents&quot; and &quot;write&quot; follow - you can guess roughly what they are ...
</pre>



<h3>Introducing more domain</h3>

<p>Factoring out those methods makes it much clearer, but there is a missing domain concept - the results page, so I'll introduce that:</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		String buildCommand = args[0];
		int previous = -1;
		while (true) {
			exec(&quot;svn up&quot;);
			String output = exec(&quot;svnversion&quot;);
			int current = Integer.parseInt(output.trim());
			if (current != previous) {
				new ResultsPage().writeBuildingPage();
				exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);
				new ResultsPage().writeResultsPage();
				previous = current;
			}
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
			}
		}
	}
	
	// now, definition of &quot;exec&quot; only
</pre>



<p>and </p>



<pre>
public class ResultsPage {
	public void writeResultsPage() throws IOException {
		String output = readContents(&quot;buildoutput.txt&quot;);
		StringBuffer stringBuffer = new StringBuffer();
		boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control results&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt; script&gt;document.bgColor=\&quot;&quot;);
		stringBuffer.append(success ? &quot;#33ff33&quot; : &quot;#ff3333&quot;);
		stringBuffer.append(&quot;\&quot;;&lt;/script&gt;&quot;);
		stringBuffer.append(&quot;Build &quot; + (success ? &quot;passed&quot; : &quot;failed&quot;));
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	public void writeBuildingPage() throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control build running&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt;iframe src=\&quot;buildoutput.txt\&quot; width=\&quot;600\&quot; height=\&quot;180\&quot;/&gt;&quot;);
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}
		
	// definitions of &quot;readContents&quot; and &quot;write&quot; follow - you can guess roughly what they are ...
</pre>



<h3>Responsibilities mixed up</h3>

<p>In the code as shown, the results page is responsible for running the build and deciding whether it passed. This doesn't seem right. Looking at the code, there is no reason why the "success" of the build shouldn't be passed in to the writeResultsPage method, and I feel like it would be better. This refactoring results in:</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		String buildCommand = args[0];
		int previous = -1;
		while (true) {
			exec(&quot;svn up&quot;);
			String output = exec(&quot;svnversion&quot;);
			int current = Integer.parseInt(output.trim());
			if (current != previous) {
				new ResultsPage().writeBuildingPage();
				exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);		
				output = readContents(&quot;buildoutput.txt&quot;);
				boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
				new ResultsPage().writeResultsPage(success);
				previous = current;
			}
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
			}
		}
	}

	// definitions of &quot;exec&quot; and &quot;readContents&quot; follow - you can guess roughly what they are ...
</pre>



<p>and ResultsPage:</p>



<pre>
public class ResultsPage {
	public void writeResultsPage(boolean success) throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control results&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt; script&gt;document.bgColor=\&quot;&quot;);
		stringBuffer.append(success ? &quot;#33ff33&quot; : &quot;#ff3333&quot;);
		stringBuffer.append(&quot;\&quot;;&lt;/script&gt;&quot;);
		stringBuffer.append(&quot;Build &quot; + (success ? &quot;passed&quot; : &quot;failed&quot;));
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	public void writeBuildingPage() throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control build running&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt;iframe src=\&quot;buildoutput.txt\&quot; width=\&quot;600\&quot; height=\&quot;180\&quot;/&gt;&quot;);
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	// definition of &quot;write&quot; follows - you can guess roughly what it is ...
</pre>



<h3>Naming, getting more abstract and other improvements.</h3>

<p>During the session, <a href="http://www.cems.uwe.ac.uk/~cjwallac/">Chris Wallace</a> suggested that instead of writing a web page, the code could usefully be made more abstract. Rather than having a results page, the missing domain concept could be thought of as "build results reporting". Maybe the interface to this would have a "buildStarted" and "buildFinished(boolean)" method - and maybe that is what the names of the methods of ResultsPage should be? Having created the ResultsPage class, it's clear now that it would be easy to introduce new forms of notification without having to significantly change the rest of the code. (Chris also pointed out that the way the code sets the background color is horrible, but for the purposes of the exercise it doesn't matter).</p>

<p>Maybe the magic values "results.html" and "buildoutput.txt" should be passed in on construction? Should TractionControl make one instance of the ResultsPage or is it OK to keep creating new instances? Which is more logical?</p>

<p>One of the benefits of creating ResultsPage is now it's clear that the helper method "write" is only used to write the result page and not for anything else.</p>

<h3>Removing duplication</h3>

<p>In this article, I won't bother taking ResultsPage any further; there is obvious duplication that could be removed but it'll make the article too long.</p>

<p>During the <span class="caps">SPA </span>session, one pair replaced ResultsPage creation with some sort of template thing, which seems sensible. ResultsPage as defined above is a bit horrible because of the duplication of code to do with <span class="caps">HTML </span>- so using some templating thing (like <a href="http://velocity.apache.org/">Velocity</a>) would be a clean way of refactoring this code.</p>

<h3>More procedural refactoring</h3>

<p>There are three lines of code that I don't want to continue repeating in this article because they just give you a few more lines to look at without doing anything interesting - it's the Thread.sleep() code. So, I've also factored that out the new method "sleep" - you'll see that soon.</p>

<h3>More domainification</h3>

<p>The determination of whether the build was successful or not, and the running of the build command, are all a bit ugly. The domain concept missing from the code is the build command. Introducing that, the code becomes (with my little procedural refactoring introducing the sleep method too):</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		BuildCommand buildCommand = new BuildCommand(args[0]);
		int previous = -1;
		while (true) {
			exec(&quot;svn up&quot;);
			String output = exec(&quot;svnversion&quot;);
			int current = Integer.parseInt(output.trim());
			if (current != previous) {
				new ResultsPage().writeBuildingPage();
				boolean success = buildCommand.runTheBuildAndItPasses();
				new ResultsPage().writeResultsPage(success);
				previous = current;
			}
			sleep();
		}
	}

	private static void sleep() {
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
		}
	}
	
	// definition of &quot;exec&quot; only
</pre>



<p>and BuildCommand:</p>



<pre>
public class BuildCommand {
	private String buildCommand;
	
	public BuildCommand(String buildCommand) {
		this.buildCommand = buildCommand;
	}

	public boolean runTheBuildAndItPasses() throws IOException {
		TractionControl.exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);		
		String output = readContents(&quot;buildoutput.txt&quot;);
		boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
		return success;
	}
		
	// definition of &quot;readContents&quot; only
</pre>



<p>This is controversial because "runTheBuildAndItPasses" executes the build and returns whether it was successful or not. Many developers like methods that return a boolean to have no side effects, i.e. to separate the executing of the build command from the returning whether that was successful or not. I suspect this is a <a href="http://blog.nayima.be/2006/03/29/spa-2006-cargo-cults-angry-monkeys-and-platypuses/">cargo cult</a> similar to <a href="http://ivan.truemesh.com/archives/000611.html">structured programming</a>, but I'm undecided. Some people think I'm a crazy hacker; opinions may vary. Generally, I'll stick with the team style in a day job, and do things like "runTheBuildAndItPasses" when I only have to answer to myself (e.g. my open source projects). I think in this case it's better to do what I've done, but I know some people will absolutely hate it. Overall, I don't care about the details for this - the important thing is the introduction of the BuildCommand - details of it's implementation may vary depending on taste.</p>

<h3>Yuk - but I won't fix it here</h3>

<p>There is a magic string all around this code - "buildoutput.txt" - I'd want to sort that out before I feel like the code is OK - but I won't bother here - it would just make the article too long.</p>

<h3>Last of the really important domain things</h3>

<p>So - looking at the code, and thinking about what it's doing - the most important domain concept still missing from this is the stuff related to subversion. There is possibly more to it than one class - but for the time being, I'll introduce the concept of the "working copy" - which is where Traction Control has got the code checked out to. One of the participants, <a href="http://chatley.com/">Robert Chatley</a> had a class called "Checkout" (along with lots of other classes - he really got into the spirit of the session - thanks Robert).</p>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		BuildCommand buildCommand = new BuildCommand(args[0]);
		WorkingCopy workingCopy = new WorkingCopy();
		while (true) {
			if (workingCopy.doUpdateAndThereWereIncomingChanges()) {
				new ResultsPage().writeBuildingPage();
				boolean success = buildCommand.runTheBuildAndItPasses();
				new ResultsPage().writeResultsPage(success);
			}
			sleep();
		}
	}

	// definitions of &quot;exec&quot; and &quot;sleep&quot; follow ...
</pre>



<p>and the working copy is represented by:</p>



<pre>
public class WorkingCopy {
	private int previous = -1;
	
	public boolean doUpdateAndThereWereIncomingChanges() throws IOException{
		TractionControl.exec(&quot;svn up&quot;);
		String output = TractionControl.exec(&quot;svnversion&quot;);
		int current = Integer.parseInt(output.trim());
		boolean thereWereIncomingChanges = current != previous;
		previous = current;
		return thereWereIncomingChanges;
	}
}
</pre>



<p>Again - controversial use of a method which does something and returns a boolean result. I'd be prepared to do this differently, i.e. with the update and the checking to see if there were updates separately - it's not something I feel as strongly about as "structured programming" - which I think is definitely a cargo cult. Again, the important thing is introducing the concept of the working copy (or something else that encapsulates the use of subversion) rather than the exact details of it's implementation.</p>

<h3>Is it any better?</h3>

<p>There is more that could be done - the exceptions are nasty, the use of the static "exec" on class TractionControl is nasty. I'd maybe introduce a "CommandLine" class to do the "exec". I would probably make it such that there is one "ResultsPage" that is reused. Some of the names could be better - there are lots of things that could be better (if I was pairing, I'm sure it would be much better!).</p>

<p>I think this code is better than the original; I find it much easier to understand. Based on comments in the session, some people don't think it's worth the effort. Some don't think it's much better. Many think it isn't worth going this far.</p>

<p>I found it interesting that there was such a large variety of what people did in the workshop. Probably the largest group of similar results was where people refactored just within the class "TractionControl" without introducing any more classes - or maybe only one more. I had expected that the domain concepts of the build command, subversion stuff and the web pages would have been represented by classes, as in this article, by a larger number of people (but of course, done slightly differently). Maybe the reason people didn't do this is because each of these doesn't encapsulate much code? I think a lot of people were happy just with the benefits of doing a "procedural" style refactoring.</p>

<p>I'd like to hear from people what they think - but unfortunately, comments are switched off due to high levels of spam. If you are in London, come along to <a href="http://www.xpdeveloper.net/xpdwiki/Wiki.jsp?page=XtC"><span class="caps">XTC</span></a> some time when I'm there and talk to me, or email me at ivan at tadmad dot co-dot-uk.</p>

<h3>Appendix - the code shown all together as it is the end of this article</h3>



<pre>
public class TractionControl {
	public static void main(String[] args) throws IOException {
		BuildCommand buildCommand = new BuildCommand(args[0]);
		WorkingCopy workingCopy = new WorkingCopy();
		while (true) {
			if (workingCopy.doUpdateAndThereWereIncomingChanges()) {
				new ResultsPage().writeBuildingPage();
				boolean success = buildCommand.runTheBuildAndItPasses();
				new ResultsPage().writeResultsPage(success);
			}
			sleep();
		}
	}

	// definitions of &quot;exec&quot; and &quot;sleep&quot; follow ...
</pre>





<pre>
public class ResultsPage {
	public void writeResultsPage(boolean success) throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control results&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt; script&gt;document.bgColor=\&quot;&quot;);
		stringBuffer.append(success ? &quot;#33ff33&quot; : &quot;#ff3333&quot;);
		stringBuffer.append(&quot;\&quot;;&lt;/script&gt;&quot;);
		stringBuffer.append(&quot;Build &quot; + (success ? &quot;passed&quot; : &quot;failed&quot;));
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	public void writeBuildingPage() throws IOException {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(&quot;&lt;html&gt;&lt;head&gt;&quot;);
		stringBuffer.append(&quot;&lt;META HTTP-EQUIV=\&quot;Refresh\&quot; CONTENT=\&quot;5\&quot;&gt;&quot;);
		stringBuffer.append(&quot;&lt;title&gt;Traction Control build running&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&quot;);
		stringBuffer.append(&quot;&lt;iframe src=\&quot;buildoutput.txt\&quot; width=\&quot;600\&quot; height=\&quot;180\&quot;/&gt;&quot;);
		stringBuffer.append(&quot;&lt;/body&gt;&lt;/html&gt;&quot;);
		write(&quot;results.html&quot;, stringBuffer.toString());
	}

	// definition of &quot;write&quot; follows - you can guess roughly what it is ...
</pre>





<pre>
public class BuildCommand {
	private String buildCommand;
	
	public BuildCommand(String buildCommand) {
		this.buildCommand = buildCommand;
	}

	public boolean runTheBuildAndItPasses() throws IOException {
		TractionControl.exec(buildCommand + &quot; &gt; buildoutput.txt&quot;);		
		String output = readContents(&quot;buildoutput.txt&quot;);
		boolean success = output.toLowerCase().contains(&quot;build successful&quot;);
		return success;
	}
		
	// definition of &quot;readContents&quot; only
</pre>





<pre>
public class WorkingCopy {
	private int previous = -1;
	
	public boolean doUpdateAndThereWereIncomingChanges() throws IOException{
		TractionControl.exec(&quot;svn up&quot;);
		String output = TractionControl.exec(&quot;svnversion&quot;);
		int current = Integer.parseInt(output.trim());
		boolean thereWereIncomingChanges = current != previous;
		previous = current;
		return thereWereIncomingChanges;
	}
}
</pre>]]>
      
    </content>
  </entry>
  <entry>
    <title>Maven2 - it can be quite good</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000731.html" />
    <modified>2008-02-28T08:03:00Z</modified>
    <issued>2008-02-28T07:59:44+00:00</issued>
    <id>tag:,2008:/10.731</id>
    <created>2008-02-28T07:59:44Z</created>
    <summary type="text/plain">I&apos;ve been a bit late to catch on to Maven2 - it&apos;s been around for about two and half years now. I&apos;ve previously been put off by the reputation of Maven1, the language used to describe Maven and some fanatical...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>I've been a bit late to catch on to <a href="http://maven.apache.org/">Maven2</a> - it's been around for about two and half years now. I've previously been put off by the reputation of Maven1, the language used to describe Maven and some fanatical "fanboys". I know a lot of people of who are generally up-to-date on the latest technologies who haven't given Maven2 much of a chance, I'm guessing for similar reasons.</p>

<p>I'm no "fanboy" - more like a "somewhat in favour of it middle aged man" - so I want to explain to my fellow sceptical oldies who have avoided Maven why they should give it a chance.</p>

<h3>Maven fails to sell itself by trying too hard</h3>

<p>The very first sentence on the <a href="http://maven.apache.org/">Maven web site</a> is exactly the sort of language that puts me off it.</p>

<p>"Maven is a software project management and comprehension tool."</p>

<p>It doesn't tell what it really is and it doesn't sound like what I'm looking for. I'm going to try to explain what it is in a way that would have got me trying it sooner, so I hope my description is suitable for others too. To be fair to the authors of <a href="http://www.devzuz.com/c/document_library/get_file?folderId=8&amp;name=DLFE-52.pdf">Better Builds with Maven</a> they do say that calling it a "project management framework" doesn't really tell you what it is. I know the first author, <a href="http://blogs.codehaus.org/people/vmassol/">Vincent Massol</a>, from <a href="http://www.xpdeveloper.net/xpdwiki/Wiki.jsp?page=XtC"><span class="caps">XTC</span></a> and he's a cool guy so I hope he won't take offense at my somewhat simplistic description of Maven - I'm missing out lots, I'm no expert in Maven, and I'm describing it with a different philosophy than the authors of Maven may have intended - but I am trying to describe what is it in a way that I would have "got it" if it had been described like this to me.</p>

<p>For example, I say that Maven is Java-centric. You can use it for non Java projects, but in many ways it shows it's "Java project" based history. And that's <span class="caps">OK.</span></p>

<h3>Maven is like lots of standard "Ant" things refactored</h3>

<p>I've seen the same stuff in <a href="http://ant.apache.org/">Ant</a> build files repeated again and again from project to project. Compiling the code, running the unit tests, packaging into a war etc. Either hand-crafted for that particular project, or copied and pasted from a previous project. These Ant build files say much the same stuff but not exactly the same stuff.</p>

<p>Maven is like a set of sensibly written Ant targets which can be used with hardly any effort as long as you use the <a href="http://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html">standard Maven directory structure</a>. Things like compiling, running the unit tests and creating a war. It also provides a suprisingly large number of more advanced build related things, like running <a href="http://cobertura.sourceforge.net/">code coverage</a> or <a href="http://pmd.sourceforge.net/">pmd</a> reports - also very easy to use without having to write a load of stuff.</p>

<h3>Standard directory structure</h3>

<p>Some people object to Maven "telling them that their directory structure should be". It does seem like the tail is wagging the dog. However, I say "get over it". Personally, as long as the directory structure is sensible then I don't care much exactly what it is. There are lots of directory structures and naming that would be equally OK - the choice, in many cases, seems somewhat arbitrary to me. The Maven standard directory structure is as sensible and good as most, so my advice is don't fight it - just go with it. If you really care that your source is in a folder called "source" rather than "src" - or whatever - then I think you should get out more. If you really care then you can configure Maven to cope with whatever you want - but why bother? I think the benefits outweigh the righteous indignation of "not being dictated to by a tool over the names of folders".</p>

<h3>Dependency management - i.e. an alternative to "lib" folders containing jars.</h3>

<p>Ant built projects often have a "lib" folder containing the jars that they depend on. Using Maven, instead of having a lib folder per project, you have a "local repository" (just a bunch of files in a particular directory structure) instead where your jars can be used by multiple projects. Each project defines it's dependencies in a sensible declarative way and Maven sorts out the classpath for compiling the code, running the tests, or even packaging everything into a self contained executable jar with all dependencies. This sort of stuff is what Maven means by the phrase "dependency management"; "dependency" is one of those words which means lots of other things to other people.</p>

<h3>Once and only once</h3>

<p>Having the project's dependencies specified in the Maven way means that you only need this information in one place. For example, you don't need to specify this in your <span class="caps">IDE </span>as well (if you use one of the supported <span class="caps">IDE</span>s) - Maven can generate the project files for <span class="caps">IDEA </span>or Eclipse, so if you add a new dependency, rather than fiddling with both the build and the <span class="caps">IDE'</span>s classpath, you just specify the depenency in the appropriate Maven file (pom.xml) and it sorts out the classpath for compiling, running the tests etc for both Maven and the <span class="caps">IDE.</span></p>

<h3>When what it does is what you want, it's great. Otherwise ...</h3>

<p>I've found Maven to be overall pretty good for some smallish projects that I've written recently. However, sometimes I've found it difficult to get it to do what I want. The documentation is patchy (particularly if you want to do something a little bit different from the standard case) - but I would recommend downloading <a href="http://www.devzuz.com/c/document_library/get_file?folderId=8&amp;name=DLFE-52.pdf">Better Builds with Maven</a>.</p>

<h3>New kid on the block</h3>

<p>I haven't tried it, but I've heard that <a href="http://Ant.apache.org/ivy/index.html">ivy</a> does similar "dependency management" stuff to maven - but it's not clear to me whether it does the same other build stuff too.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>build-o-matic build farm</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000730.html" />
    <modified>2008-02-01T22:02:28Z</modified>
    <issued>2008-02-01T21:25:25+00:00</issued>
    <id>tag:,2008:/10.730</id>
    <created>2008-02-01T21:25:25Z</created>
    <summary type="text/plain">I&apos;ve released a new version of build-o-matic (actual over a month ago now - just shows how far behind I&apos;ve got with writing) which includes support for a &quot;build farm&quot;. I&apos;ve been using it at my current client, Caplin Systems...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>I've released a new version of <a href="http://sourceforge.net/project/showfiles.php?group_id=178854">build-o-matic</a> (actual over a month ago now - just shows how far behind I've got with writing) which includes support for a "build farm".</p>

<p>I've been using it at my current client, <a href="http://www.caplinsystems.co.uk">Caplin Systems Ltd</a> for their streaming ajax trading platform (very cool) implemented in JavaScript (client), Java and C (server), built using ant and maven, and using Perforce for source control. A free version of the server is now <a href="http://www.freeliberator.com/">available</a>.</p>

<p>The farm feature of <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a> is working fantastically well - exactly what I want.</p>

<h3>What should a continuous integration server do?</h3>

As far as I'm concerned, the big things are:<br />
<ol>
<li>Tell you whether your build is currently broken or not</li>
<li>Tell you when the build gets broken</li>
<li>If the build is broken, help you work out why</li>
</ol>

<p>Where build-o-matic is different from other continuous integration servers is what I've done for (3).</p>

<p>Many continuous integration servers have invested a lot of effort in telling you the symptoms of why the build has failed - for example, with strong integration with the running of your build's tests.</p>

<p>I've taken a quite different approach - I'm not interested in the symptom of the build breakage, but the cause. Therefore, I have concentrated on getting build-o-matic to tell me which commit caused the build to break.</p>

<p>Having your continuous integration server tell you that the build has failed because a particular test has failed is interesting, but I think it's actually more useful to know that the build started failing from commit 5634.</p>

<h3>Why this interpretation of "why"?</h3>

<p>If the build gets broken, someone has to fix it. The best person/pair to fix it is the person/pair who broke it. Build-o-matic helps find out who broke the build.</p>

<p>The easiest way to find out what caused the failure (rather than the symptom of the failure) is to find out what change caused the build to fail. I expect the build itself to tell you why it is broken, rather than the continuous integration server (which, as far as I'm concerned, should only be running your build).</p>

<h3>But all continuous integration servers already tell you which commit broke the build, don't they?</h3>

<p>Not quite. Where other continuous integration servers fail is when you have multiple commits between builds. There are a few alternative solutions to this, including:</p>

<ol>
<li>Have an integration token - i.e. never have multiple commits between builds.</li>
<li>Use Team City's <a href="http://www.jetbrains.com/teamcity/delayed_commit.html">Pre-tested Commit feature</a> (or equivalent, if there are others)</li>
<li>Use build-o-matic's farm feature (or equivalent, if there are others)</li>
</ol>

<p>I have used approach (1) - it works very well for a co-located and smallish team. You don't need any continuous itegration server at all for that approach.</p>

<p>I haven't tried Team City's "Pre-tested Commit feature" - my concern would be what do you do if the commit fails? You've then got to sort out the problem with the changes you tried to commit, plus any other changes you made since that attempted commit. If the build is successful (as it should be) then you've delayed integrating - it's a bit less continuous than it could be.</p>

<p>The build-o-matic farm does just what I want.</p>

<h3>Multiple revisions at the same time</h3>

<p>It's easiest to describe what build-o-matic does by example. Say you've got three machines in the farm, and more than three projects being built by the build-o-matic farm.</p>

<p><a href="http://ivan.truemesh.com/allQuiet3.html" onclick="window.open('http://ivan.truemesh.com/allQuiet3.html','popup','width=1120,height=840,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://ivan.truemesh.com/allQuiet-thumb.PNG" width="350" height="262" alt="" /></a></p>

<p>If there are commits to three different projects all at the same time, then each project will be built on a different machine (just like you'd expect from a build farm). Here's a build-o-matic management page that shows build activity on different machines:</p>

<p><a href="http://ivan.truemesh.com/threeSameTime.html" onclick="window.open('http://ivan.truemesh.com/threeSameTime.html','popup','width=1120,height=840,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://ivan.truemesh.com/threeSameTime-thumb.PNG" width="350" height="262" alt="" /></a></p>

<p>Where build-o-matic is special, is if there are multiple commits to one project at the same time. For example, if there are two commits to project A, and one commit to project B, all at the same time, then build-o-matic will build both commits for project A (in parallel on two different machines) and the commit for project B.</p>

<p><a href="http://ivan.truemesh.com/threeSameTime-twoPlusOne.html" onclick="window.open('http://ivan.truemesh.com/threeSameTime-twoPlusOne.html','popup','width=1120,height=840,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://ivan.truemesh.com/threeSameTime-twoPlusOne-thumb.PNG" width="350" height="262" alt="" /></a></p>

<p>The build overview page in this case looks like this:</p>

<p><a href="http://ivan.truemesh.com/buildsBuilding1.html" onclick="window.open('http://ivan.truemesh.com/buildsBuilding1.html','popup','width=1120,height=840,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://ivan.truemesh.com/buildsBuilding-thumb.PNG" width="350" height="262" alt="" /></a></p>

<p>If there were three commits to project A and none others, then all three revisions of project A would be built in parallel on different machines.</p>

<p>If there were three commits to project A and one to project B, then project B would be built and two of the three commits to project A. Then, when one of the machines has finished it would build the revision of project A that hasn't been built yet.</p>

<p>The "activity" pages are a bit horrible at the moment, but it works and does just what I want. All of the pages are served rather slowly in the current version of build-o-matic - it'll improve in some future version.</p>

<h3>What does your continuous integration server do?</h3>

<p>I find it difficult to tell what other continuous integration servers do - there's not yet an agreed "language" to describe their features. The <a href="http://confluence.public.thoughtworks.org/display/CC/CI+Feature+Matrix">CI Feature Matrix</a> which the <a href="http://damagecontrol.codehaus.org/">Damage Control</a> team produced is a great effort, but doesn't include the features that make <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a> special.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>Simple Jester</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000725.html" />
    <modified>2007-12-13T12:50:45Z</modified>
    <issued>2007-12-12T23:39:06+00:00</issued>
    <id>tag:,2007:/10.725</id>
    <created>2007-12-12T23:39:06Z</created>
    <summary type="text/plain">At CITCON I wanted to demo Jester in the session on mutation testing. I tried to run Jester on Nat Pryce and Steve Freeman&apos;s JMock2 code but couldn&apos;t get it to work in the time I had available, so I...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    <dc:subject>Tools</dc:subject>
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>At <a href="http://www.citconf.com/"><span class="caps">CITCON</span></a> I wanted to demo <a href="http://jester.sourceforge.net/">Jester</a> in the <a href="http://citconf.com/wiki/index.php?title=MutationTesting">session on mutation testing</a>. I tried to run Jester on <a href="http://nat.truemesh.com/">Nat Pryce</a> and <a href="http://www.m3p.co.uk/blog">Steve Freeman's</a> <a href="http://www.jmock.org/">JMock2</a> <a href="http://svn.codehaus.org/jmock/trunk/jmock2/">code</a> but couldn't get it to work in the time I had available, so I gave up and wrote some simple code specifically for the demo.</p>

<p>As a result of this bad experience, I thought I'd make Jester much simpler to use. I've called the result "simple-jester" and the first release of it is available from the <a href="http://sourceforge.net/project/showfiles.php?group_id=23959&amp;package_id=255521">Jester sourceforge site</a>.</p>

<h3>An example of using simple-jester</h3>

<p>This article describes an example of how to run simple-jester, by showing how to run it on <a href="http://www.jmock.org/">JMock2</a>. I have chosen JMock2 because I know that it'll be well written and well tested (because I know Nat Pryce and Steve Freeman), it's not too small and not too big, the build is fast, it wasn't written with Jester in mind, and I didn't write it.</p>

<p>This article describes what I did - on windows, on a machine which I've already got Java, ant, Python and subversion installed on. This is what really happened ... warts and all, so I hope the hints and tips that arise from the problems described here will help you get simple-jester working for yourself. </p>

<h3>A note on the style of this article</h3>

<p>Sorry if you find the writing style odd (even by my standards) - it's a mix of the instructions that I thought I should write, what is happening as I'm trying to follow them and then instructions to fix the mistakes I made with the earlier instructions. I've written it like this because in practice you will end up going through something similar to get Jester to work - the mistakes weren't deliberate and are instructional.</p>

<h3>Download and install Jester</h3>

<ul>
<li>Download <a href="http://downloads.sourceforge.net/jester/simple-jester-1.0.zip?use_mirror=osdn">simple-jester-1.0.zip</a> to c:\temp</li>
<li>unzip - it gets unzipped into C:\temp\simple-jester-1.0</li>
</ul>

<p>Check that everything is OK by executing, in a command prompt:</p>

<ul>
<li><tt>cd C:\temp\simple-jester-1.0</tt></li>
<li><tt>setcp.bat</tt></li>
<li><tt>test.bat</tt></li>
</ul>

<p>Then:</p>

<ul>
<li>wait about a minute for Jester to finish it's run</li>
<li>open "jester.html" to see that Jester has done something.</li>
</ul>
   <br />
I've just realised that <tt>setcp.bat</tt> added JUnit 3.8 to the classpath - that might not be good for running the JMock2 build. Jester doesn't need JUnit on it's classpath to run. So:

<ul>
<li>close this command prompt</li>
</ul>

<h3>Get JMock source code</h3>

<ul>
<li>open a new command prompt</li>
<li>cd c:\temp</li>
<li>svn co http://svn.codehaus.org/jmock/trunk/jmock2/</li>
<li>cd c:\temp\jmock2</li>
<li>execute "<tt>ant</tt>"</li>
</ul>
   <br />
Unfortunately, the build fails:



<pre>
run.tests:
    [mkdir] Created dir: C:\temp\jmock2\build\reports\tests
    [junit] Testsuite: org.jmock.example.announcer.AnnouncerTests
    [junit] Tests run: 4, Failures: 0, Errors: 0, Time elapsed: 0.031 sec

    [junit] Testsuite: org.jmock.example.qcon.DJTests
    [junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0 sec

    [junit] Testcase: warning(junit.framework.TestSuite$1):     FAILED
    [junit] No tests found in org.jmock.example.qcon.DJTests
    [junit] junit.framework.AssertionFailedError: No tests found in org.jmock.ex
ample.qcon.DJTests



BUILD FAILED
C:\temp\jmock2\build.xml:208: Tests failed
</pre>



<p>Sorry Nat and Steve. Maybe I've got something wrong with the environment. I don't have time to investigate. Almost certainly something I've got wrong and I'm not worried anyway, because the fix is simple and I don't expect the side-effects of the fix to be too bad.</p>

<p>so:</p>

<ul>
<li>delete C:\temp\jmock2\test\org\jmock\example\qcon\DJTests.java</li>
<li>execute "<tt>ant</tt>"</li>
<li><span class="caps">BUILD SUCCESSFUL </span>- on my machine it took 14 seconds</li>
</ul>
   <br />
<h3>Run Jester on JMock2</h3>

<ul>
<li>add Jester to the classpath by executing <br />"<tt>set classpath=C:\temp\simple-jester-1.0\simple-jester.jar;C:\temp\simple-jester-1.0</tt>"</li>
<li>execute "<tt>java jester.TestTester ant src</tt>"</li>
</ul>
   <br />
Unfortunately, this fails:



<pre>
Jesting failed!
jester.SourceChangeException: couldn't run tests
        at jester.RealTestRunner.testsRunWithoutFailures(RealTestRunner.java:26)

        at jester.TestTester.run(TestTester.java:144)
        at jester.TestTester.doMain(TestTester.java:127)
        at jester.TestTester.main(TestTester.java:81)
Caused by: java.io.IOException: CreateProcess: ant error=2
        at java.lang.ProcessImpl.create(Native Method)
        at java.lang.ProcessImpl.&lt;init&gt;(ProcessImpl.java:81)
        at java.lang.ProcessImpl.start(ProcessImpl.java:30)
        at java.lang.ProcessBuilder.start(ProcessBuilder.java:451)
        at java.lang.Runtime.exec(Runtime.java:591)
        at java.lang.Runtime.exec(Runtime.java:429)
        at java.lang.Runtime.exec(Runtime.java:326)
        at jester.Util.runCommand(Util.java:10)
        at jester.RealTestRunner.testsRunWithoutFailures(RealTestRunner.java:21)

        ... 3 more
</pre>



<p>Doh! I made the mistake that I now remember documenting in the <span class="caps">README.</span>html for Jester. You have to tell Jester to run <tt>ant.bat</tt> on windows, rather than just <tt>ant</tt>.</p>

<p>To prove this, execute "<tt>java jester.RealTestRunner ant</tt>" which runs the code that Jester will run for executing your build command. This produces the same output as above. If you now execute "<tt>java jester.RealTestRunner ant.bat</tt>" instead, then the result is:</p>



<pre>
C:\temp\jmock2&gt;java jester.RealTestRunner ant.bat
true
</pre>



<p>which means that this build passes for Jester.</p>

<p>So - trying again:</p>

<ul>
<li>execute "<tt>java jester.TestTester ant.bat src</tt>"</li>
<li>the "progress" dialog opens, and after 30 seconds or so, you should see something happening in the "progress" dialog</li>
<li>lots of stuff gets printed out in the command prompt - it's ugly and alarming. Just ignore it.</li>
<li>now go and make a cup of tea; this could take some time. </li>
</ul>
   <br />
I feel happy that I've been able to get Jester to work in a very short time. This would have been enough for <span class="caps">CITCON </span>- I could have run it on a small subset of the source rather than all the source and it would have all been done, set up and executed, in less than 30 minutes. Here I'm being more ambitious by trying to run Jester on all of the JMock2 source.

<h3>It goes horribly wrong</h3>

<p>I've gone back to the machine - the progress bar showing not much progress. Not much seems to be happening, but after waiting about 30 seconds I see some more stuff in the "progress" dialog and return to the important task of drinking the tea that I've just made.</p>

<p>I go back to the machine much later - it really does look stuck now. One of the cores is maxed out running a java process, and the "progress" dialog has shown: "<tt>src\org\jmock\lib\concurrent\SynchronousScheduledExecutor.java - changed source on line 54 (char index=2029) from if ( to if (false &amp;&amp;</tt>" for several minutes.</p>

<p>So:</p>

<ul>
<li>kill jester (ctrl-c)</li>
<li>execute "<tt>ant</tt>" and the result is:</li>
</ul>
   


<pre>
BUILD FAILED
C:\temp\jmock2\build.xml:15: Unable to delete file C:\temp\jmock2\build\jmock-2-
SNAPSHOT\jmock-2-SNAPSHOT.jar
</pre>



<p>I open the task manager and find a java process that is hanging around that I wouldn't expect so:</p>

<ul>
<li>kill any stray java processes</li>
<li>delete the offending jmock-2-SNAPSHOT.jar file. </li>
</ul>
   <br />
I suspect a problem with Jester - if a mutation causes the build not to terminate, then Jester times out that run of the build and keeps going. In this case, it looks like that's what has happened, and there is a java process hanging around that is keeping a lock on the jmock-2-SNAPSHOT.jar file which the build needs to delete in order to continue, so now it's hung. 

<p>One of the least endearing features of Jester is that if the whole run isn't successful then you can't easily get anything out of Jester - i.e. mostly either the complete run of Jester works or doesn't work, if it crashes or is killed before it's finished then you don't get much useful out of it. I might fix this in the future. In the meantime, it's safest to run Jester on subsets of your code base (e.g. at most a package) at a time. But I won't follow my own advice here because I'm determined to get Jester to work for all the source files of JMock2 in one run.</p>

<p>To try to get Jester to work for JMock2, I'll get Jester to ignore the part of the source file where it made the mutation that caused the hang problem.</p>

<ul>
<li>revert changes to "SynchronousScheduledExecutor.java" by executing: <tt>svn revert src\org\jmock\lib\concurrent\SynchronousScheduledExecutor.java</tt></li>
<li>add "//stopJesting" as line 58 (if you have a look at the original source you'll see that's immediately before the mutation that was described in the "progress" dialog).</li>
<li>add "//resumeJesting" as line 60</li>
<li>to check that there are no other problems for Jester with this file, run Jester only on this file rather than on everything, i.e. execute: <br />"<tt>java jester.TestTester ant.bat src\org\jmock\lib\concurrent\SynchronousScheduledExecutor.java</tt>"</li>
<li>Jester will complete mutation testing of this (takes about a minute)</li>
<li>execute "<tt>java jester.TestTester ant.bat src</tt>"</li>
<li>some time later, a lot later, Jester will finish. Yipee. Here's the last couple of lines of output:</li>
</ul>
   


<pre>
43 mutations survived out of 284 changes. Score = 85
took 42 minutes
</pre>



<p>I'd expect that having deleted one of the tests (which might not have been the best way to get the build to pass) will have slightly worsened the results. Nevertheless, these results indeed show that the tests for JMock2 are impressively good. Jester often gives lower "scores" than code coverage tools. I'd be interested to know what the results are for a code coverage tool on the same code but my time has run out for this and my tea has gone cold.</p>

<p>However - I still have time for one thing, which is possibly the most important output from Jester:</p>

<ul>
<li>execute "<tt>python c:\temp\simple-jester-1.0\makeWebView.py</tt>"</li>
</ul>
   <br />
This produces web pages that show the modifications that Jester can make where the tests still pass. I'll send these to Nat (here's <a href="http://ivan.truemesh.com/ExpectationError.html">an example</a> chosen because it's short, which I have hand edited to add his license in for publication). These files are the most interesting output from Jester.

<h3>The future of Jester</h3>

<p>I'm pleased with both the code and usage simplifications to Jester in "simple-jester". The downside is that it's now even slower than before; but that doesn't worry me unduly because at least now I and others will be able to get it to work for many more projects. There are still some areas of improvement (e.g. if your build calls ant multiple times then Jester can think the build was successful even if it wasn't) - but generally it's much improved thanks to some judicious deletion of code.</p>

<p>If someone wants to pay me to do it, I'd be interested in making Jester faster by having it clustered on multiple machines. There are alternative approaches - e.g. <a href="http://jumble.sourceforge.net/">jumble</a> which uses byte code mutation (it's Java specific) which is much faster than Jester's approach.</p>

<p>I'm not planning on spending lots more time on Jester (unless someone pays me to do it) because I'm now happy that Jester is both simplified and improved (as simple-jester) such that it's much more usable, as I hope this article shows with it's "warts and all" commentary, because the warts really weren't very large.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>CITCON 2007</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000720.html" />
    <modified>2007-10-28T13:39:08Z</modified>
    <issued>2007-10-28T13:35:54+00:00</issued>
    <id>tag:,2007:/10.720</id>
    <created>2007-10-28T13:35:54Z</created>
    <summary type="text/plain">I attended CITCON (Continuous Integration and Testing CONference) in Brussels last weekend - and just like last year, it was great! It&apos;s an expertly facilitated open space conference by Jeffrey Fredrick and Paul Julius, through their Open Information Foundation. I...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>I attended <a href="http://www.citconf.com/"><span class="caps">CITCON</span></a> (Continuous Integration and Testing <span class="caps">CON</span>ference) in Brussels last weekend - and just like <a href="http://ivan.truemesh.com/archives/000651.html">last year</a>, it was great! It's an expertly facilitated open space conference by Jeffrey Fredrick and Paul Julius, through their <a href="http://www.openinformationfoundation.org/">Open Information Foundation</a>. I think I enjoyed it even more this year than last. It really is an exellent conference. I'm already looking forward to the next one.</p>

<h3>Mutation testing - Jumble and Jester</h3>

<p><a href="http://douglassquirrel.com/">Squirrel</a> proposed a <a href="http://citconf.com/wiki/index.php?title=MutationTesting">session on mutation testing</a>; he wanted to demo <a href="http://jumble.sourceforge.net/">Jumble</a> and suggested that I should say something about <a href="http://jester.sourceforge.net/">Jester</a>. It turned out to be a very popular session - it's still an idea that people are interested in.</p>

<h3>Mutation testing</h3>

<p>In a nutshell:<br />
   * Make some change to your code<br />
   * Run your tests<br />
   * If the tests pass then either you are missing a test or you have unnecessary code.</p>

<h3>Jester difficult to use</h3>

<p>It's a long time since I've tried to use Jester - I haven't worked on it for a long time - I checked with sourceforge and the last release was February 26, 2005. I spent an hour or so before the session, preparing a demo. It was quite horrible trying to get it to work.</p>

<h3>New version of Jester</h3>

<p>After finding out the level of interest, and being reminded of the difficulty getting Jester to work, I'm intending to release a new version of Jester - probably a fork because I've handed over project admin to <a href="http://www.elharo.com/">Elliotte Rusty Harold</a> and what I've got in mind is a simplification that has some disadvantages as well as some advantages. It'll be simpler code, simpler to use, but even less efficient. For me, making it simple to use is more important than the performance.</p>

<h3>Jumble and others</h3>

<p>Since Jester was released, there have been other implementations of the same sort of thing. Squirrel demoed <a href="http://jumble.sourceforge.net/">Jumble</a>, which he was keen on. An ex-ThoughtWorks colleague of mine, <a href="http://digital-compulsion.blogspot.com/">Stacy Curl</a>, wrote "ajester" which, like Jumble, used byte code mutation rather than source mutation - but I don't know if he ever released it - I can't find a release package. Michael Nyika wrote <a href="http://sourceforge.net/projects/grester">Grester</a> - a Maven2 Plugin for Jester. There's also a dot net version of Jester, called <a href="http://sourceforge.net/projects/nester">Nester</a>. Another mutation testing tool is <a href="http://ise.gmu.edu/~ofut/mujava/">muJava</a> which apparently now has an eclipse plugin. There is an eclipse plugin for Jester, but I'm afraid it never quite got properly released (sorry).</p>

<h3>CruiseControl - still the daddy</h3>

<p><span class="caps">CITCON </span>is about continuous integration as well as testing. It's very clear from <span class="caps">CITCON </span>that cruisecontrol is still <b>by far</b> the most used continuous integration server. There are dozens of other continous integration servers available but none of them have much "market share". I was suprised by how few people had even heard of <a href="http://www.jetbrains.com/teamcity/">TeamCity</a> because it seems to be becoming trendy with the London XP crowd. </p>

<p>The second most used continous integration server by <span class="caps">CITCON </span>attendees appeared to be <a href="https://hudson.dev.java.net/">Hudson</a> demoed by <a href="http://ericlefevre.net/wordpress/2007/10/24/citcon-brussels-2007-hudson/">Eric Lefevre</a> which looks nice enough but still doesn't do what I want.</p>

<p>The only continous integration server that does what I want is <a href="http://build-o-matic.sourceforge.net/">build-o-matic</a> - because I wrote most of it! I did a demo which I think went OK - I think the feature people like the most is <a href="http://ivan.truemesh.com/archives/000654.html">putting the pictures on the results page</a>. <a href="http://nat.truemesh.com/">Nat Pryce</a> wrote a plugin called <a href="http://code.google.com/p/team-piazza/">team-piazza</a> that does something similar for <a href="http://www.jetbrains.com/teamcity/">TeamCity</a>. </p>

<p>More on the latest developments of build-o-matic in a future post.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>No bug reports != no bugs</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000717.html" />
    <modified>2007-10-01T07:50:43Z</modified>
    <issued>2007-10-01T07:36:30+00:00</issued>
    <id>tag:,2007:/10.717</id>
    <created>2007-10-01T07:36:30Z</created>
    <summary type="text/plain">It&apos;s difficult getting feedback about software. Do people log bugs? Mostly, you get no feedback. Sometimes people log bugs, but not very often. With open source software, or web sites providing some service, if it doesn&apos;t work straight away (and...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>It's difficult getting feedback about software.</p>

<h3>Do people log bugs?</h3>

<p>Mostly, you get no feedback. Sometimes people log bugs, but not very often. With open source software, or web sites providing some service, if it doesn't work straight away (and very simply), mostly people will ignore it and try the next thing that looks like it might do what they want.</p>

<p>If an open source project has no bugs logged against it, chances are that it's very little used (possibly because it's totally broken) rather than because it is so high quality that it has no bugs. When people do log bugs, it's against software that they want to improve, not software that they can't be bothered with. In fact, I'd say it's generally a good sign if a project has several bugs logged against it and a bad sign if it has none. And yes, I have at least one open source project in the no bugs category (and I think it's good despite what I've just written)!</p>

<h3>From "oops" to "ah ha"</h3>

<p>I remember a few years ago, I released a new version of <a href="http://jester.sourceforge.net">Jester</a> (as a zip file). It was downloaded a couple of dozen times before someone (thanks - sorry I've forgotten who it was) told me that they couldn't unzip the file. It turned out that it had got corrupted on upload to sourceforge - I hadn't expected that. Of course, since then, I download and check that the zip wasn't corrupted (a bit of a pain, and I've not had a file corruption problem since, but I guess these things happen once in a while).</p>

<h3>Your responsibility to kittens</h3>

<p>In his keynote speach at last years <a href="http://www.accu.org/index.php/conferences"><span class="caps">ACCU </span>conference</a>, <a href="http://www.markshuttleworth.com/">Mark Shuttleworth</a> (<a href="http://www.ubuntu.com/">Ubuntu</a> founder) said something along the lines of "every time you fail to log a bug, a kitten dies". I like the sentiment. Please do log a bug, even if it seems so obvious that you think that surely the creator of the software (or person running a website) knows about it.</p>

<h3>Knives behind your back</h3>

<p>In the case of my open source projects, sometimes I'll google to see if someone has said anything about any of them. Sometimes I'll find an article slagging off one of my beautiful creations by someone who hasn't thought to tell me their criticisms or log any bugs.</p>

<h3>Ignoring feedback</h3>

<p>When using the <a href="http://shop.o2.co.uk/home">O2 website</a> I tried to do a search and got the following very obvious error:</p>

<p><a href="http://ivan.truemesh.com/o2shop.html" onclick="window.open('http://ivan.truemesh.com/o2shop.html','popup','width=872,height=602,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://ivan.truemesh.com/o2shop-thumb.PNG" width="581" height="401" alt="o2 shop" /></a></p>

<p>Try it yourself. Simply enter anything in the search text box and press the search button. Following my own advice, I sent o2 an email about this (8th September). I tried again tonight (30th September) and got the same error. Never mind. I'll continue to try to save the kittens.</p>

<h3>And another thing</h3>

<p>There is a whole other article I could write about how to write bug reports ...</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>Project management in the small - it isn&apos;t all glamourous.</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000715.html" />
    <modified>2007-09-24T21:28:25Z</modified>
    <issued>2007-09-24T21:21:59+00:00</issued>
    <id>tag:,2007:/10.715</id>
    <created>2007-09-24T21:21:59Z</created>
    <summary type="text/plain">Following on from my previous article on Project Management in the small, in this article I want to talk about the glamourless, but worthy, side of working as a Project Manager. The glamourless side of Project Management One of the...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>Following on from my previous article on <a href="http://ivan.truemesh.com/archives/000697.html">Project Management in the small</a>, in this article I want to talk about the glamourless, but worthy, side of working as a Project Manager.</p>

<h3>The glamourless side of Project Management</h3>

<p>One of the most important things for a Project Manager to do is to fix (or cause to be fixed) the sort of problems that the rest of team don't want to, and would be a waste of their talents to spend time on.</p>

<p>For example, if you need to buy a license for some software, then the Project Manager should do that (or have it done), rather than wasting the time of a developer, who probably prefers developing software and probably is quite good at developing software but may not be so motivated at (for example) filling in forms, paying using their own credit card, spending time filling in more forms to claim the money back etc.</p>

<p>The glamourless truth is that part of a Project Manager's job is being a "gopher".</p>

<h3>Being a cost-effective gopher.</h3>

<p>When working as a Project Manager, if you can get someone else to do this sort of thing (who isn't in your team) then that's even better. Some companies have PAs or other useful employees who can do these things for you, which is probably better value for the company. If your company doesn't have such people (as many very small companies don't) then in my opinion it's overall more cost effective to have the Project Manager do things like this rather than developers (even if the Project Manager is paid more per hour) because of the flow-destroying, and in some cases morale sapping, nature of these tasks. Having one person with lots of interruptions and odd-jobs is more efficient than having these spread throughout the team.</p>

<h3>Is that gopher or groundhog?</h3>

<p>To be an even better Project Manager you need to be a like a gopher, but able to predict the future, so I guess that means being a groundhog. Rather than just removing obstacles when they appear, a better Project Manager prevents them from appearing. For example, if you are working at a bank and know that someone new is going to be joining the team, then get their security clearance sorted out before they join so they can get their security pass to allow them around the building as soon as possible, rather than getting it sorted out after they have joined and having to wait for a couple of weeks before they can go to the bathroom unattended.</p>

<h3>Avoiding groundhog days</h3>

<p>If you work as a groundhog you might expect to have many groundhog days. If something is cropping up as a persistent problem then you need to make sure it gets fixed, even if the investment seems high compared to the value. It is very good for morale for things to get better, and morale is very important for productivity, so time spent fixing problems usually not only gets back the time that the problem was wasting, but also some extra by making things better and people happier.</p>]]>
      
    </content>
  </entry>
  <entry>
    <title>Project management in the small - it isn&apos;t all hard work.</title>
    <link rel="alternate" type="text/html" href="http://ivan.truemesh.com/archives/000697.html" />
    <modified>2007-07-01T13:19:11Z</modified>
    <issued>2007-07-01T13:11:35+00:00</issued>
    <id>tag:,2007:/10.697</id>
    <created>2007-07-01T13:11:35Z</created>
    <summary type="text/plain">James Lewis, David Peterson and others have asked if I&apos;m going to follow-up my programming in the small articles with some &quot;project management in the small&quot; articles, because these days I am mostly doing project management; so I&apos;ll have a...</summary>
    <author>
      <name>ivan</name>
      
      <email>ivanwl@tadmad.co.uk</email>
    </author>
    
    <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://ivan.truemesh.com/">
      <![CDATA[<p>James Lewis, <a href="http://www.davidpeterson.co.uk/">David Peterson</a> and others have asked if I'm going to follow-up my <a href="http://ivan.truemesh.com/archives/cat_programming_in_the_small.html">programming in the small</a> articles with some "project management in the small" articles, because these days I am mostly doing project management; so I'll have a go. I have less experience as a project manager than as a developer, so it might be presumptuous of me to write these articles, but I will anyway. My experience of project management is both on teams with someone else as the project manager, and being a project manager myself, over many years and dozens of projects.</p>

<h3>Project management doesn't have to be hard work.</h3>

<p>Some project managers seem to think that it's important that they work hard and their team works hard. However, (as <a href="http://stevef.truemesh.com/">Steve Freeman</a> has said recently in conversation too), what is really important are the outputs of the team not it's inputs. That is, what's important is the value the team is delivering (output) rather than how hard they are working (input). Note I say "the value that the team delivers" and not "the amount of software they deliver" - at times the difference can be huge.</p>

<h3>Sustainable pace</h3>

<p>In some cases, working longer hours increases the output, but not always. Don't confuse the two things. In my experience, a bit of <a href="http://www.amazon.com/Slack-Getting-Burnout-Busywork-Efficiency/dp/0767907698">slack*</a> actually increases output, and working too hard decreases output over the long term. You can increase output for a short time by working longer hours, but it doesn't last.</p>

<p>[* - I haven't read this book yet, I will do. From reading the reviews it sounds like the sort of stuff I'm talking about]</p>

<p>If developers get tired, they can also get sloppy, demotivated and stop thinking. Just grinding away hour after hour does nothing for productivity. Furthermore, the most mobile developers will eventually quit and get a job elsewhere if they are working long hours all the time.</p>

<h3>Less is more</h3>

<p>The biggest gains in productivity often come from doing a lot less work, rather than doing more work. That is, working out a creative way in which you can do something much simpler or using some software that someone has already written to solve the problem.</p>

<h3>More is more</h3>

<p>The biggest gains in value can come from developers having some slack time to think of new ways of using software for the benefit of the business. At connextra, we introduced <a href="http://www.xpuniverse.com/2001/pdfs/Method04.pdf">gold cards</a> which was a structured way of giving developers some time away from planned <a href="http://www.extremeprogramming.org/rules/userstories.html">stories</a>, and some of the work that came out of these "gold cards" turned out to be enourmously valuable to the company; much more than the cost.</p>

<h3>Why long hours are attractive to project managers</h3>

<p>It's much easier to work hard and to bully or bribe a team into working harder (or at least longer) than it is to help them work more effectively. Therefore, some project managers take this option - knowing that the hard work of themselves and their team will be noticed by the people they report to, and so they think they might look good. However, what looks better is actually delivering stuff.</p>

<h3>Why project managers can't be busy all the time</h3>

<p>As a project manager you will often have meetings scattered throughout the day, with half an hour here, and hour there, between meetings. You don't have to feel bad if you aren't busy all the time. You need to have unplanned time in your schedule to be available for helping with things that come up. Being available is more important than being busy. Some project managers seem to book and attend lots of pointless meetings and rush around the office being busy. It's more important to be available to sort out that seemingly minor issue there and then, because what seems like a minor issue can get in the way of the developers' flow. Remember it's the effectiveness of the whole team that matters rather than how busy any particular individual is.</p>

<h3>Put your feet up</h3>

<p>No. That's not what I'm saying. You shouldn't be a slacker, but a bit of slack is good.</p>]]>
      
    </content>
  </entry>

</feed>