<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Atlassian Developer Blog</title><link>http://blogs.atlassian.com/developer/</link><language>en</language><lastBuildDate>Thu, 02 Jul 2009 11:11:11 PDT</lastBuildDate><generator>Movable Type 4.1 http://www.sixapart.com/movabletype/</generator><description></description><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/AtlassianDeveloperBlog" type="application/rss+xml" /><feedburner:emailServiceId>AtlassianDeveloperBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><item><title>Agile With A Remote Product Owner</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/mY9aI4se2vA/agile_with_a_remote_product_ow.html</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Janusz Gorycki</dc:creator><pubDate>Thu, 02 Jul 2009 11:11:11 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2432</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="Agile software development at Atlassian" style="float: right; margin: 0 0 20px 20px;"></a></p>

<p><big><strong>We Are From Mars</strong></big></p>

<p>All agile methodologies stress the need of co-locating development with the customer's representative - the Product Owner - or at least, having them in close proximity - both in time and in space.</p>

<p>Our team seems to be the exact opposite of what "the agile way" requires - we are located on the other side of the planet (Gdansk, Poland) from the company headquarters (Sydney, Australia). This means we could just as well be located on Mars. Actually, one could argue that outside of the narrow communication windows (early morning and late at night), communication latency between Sydney and Mars is smaller than the one between us and our Product Owner. Yet - we still manage to work in an agile way.</p>

<p><strong><big>Talking To Each Other</big></strong></p>

<p>How do we do it? First of all, we utilise high-bandwidth communication opportunities as much as possible. We have a local Product Owner proxy (that would be me), who talks to the Product Owner regularly, at predefined times and at least once a week. Also, if the out-of-band need arises, ad-hoc meetings are scheduled. Skype (or some equivalent technology) is your friend here, as well as Instant Messaging systems, such as Jabber. I cannot stress this enough - talking to each other in real-time is the most important and most effective way to communicate. I do not think it would be possible for us to work without it.</p>

<p><big><strong>Technology Helps</strong></big></p>

<p>But that is not all. For us, the second most important communication method (and tool) is <a href="http://www.atlassian.com/agile/tools">JIRA</a> - and it does the job just fine in its plain, out-of-the-box form. We use pretty much a stock JIRA setup, with just two custom fields added (both reflecting standard Scrum practices) - one called "backlog order", used for prioritizing user stories (a fancy word for "new feature or improvement"), the other called "story points" - for estimating the "size" of the user story. That suffices all our needs for making sure both the PO and us know what has to be done and how much delivering the requested feature is going to cost (after all, the effort estimates translates almost linearly into monetary costs). We also use the <a href="http://www.atlassian.com/greenhopper/">GreenHopper</a> JIRA plugin to provide "virtual story cards", instead of maintaining our user stories on pieces of paper, to make our life easier when it comes to copying the outcomes of our planning sessions to JIRA. But for day-to-day ticket management, we just use JIRA. Or more properly: one of us (that would be me again) is using JIRA, everybody else is just moving around the actual paper story cards on the cork board, which is a central part of our workspace.</p>

<p>What about the communication in the other direction? What if a Product Owner needs to describe some story to us, and words are not enough? Sometimes the Product Owner will want to draw a mockup of the user interface he has in mind. Here is where the <a href="http://www.balsamiq.com/products/mockups">Balsamiq Mockups</a> tool is very helpful. It is a <a href="http://www.atlassian.com/agile/tools">Confluence</a> plugin, which lets you draw diagrams right there in the Confluence page, and share it with whoever you want to communicate with. We would typically edit the diagrams in-place and simply comment on them until everybody is satisfied with the resulting sketch of the user interface to be delivered.</p>

<p><strong><big>Delivery</big></strong></p>

<p>How does the Product Owner know we are doing what he wanted us to do?</p>

<p>First of all, just as the Scrum methodology requires, we have a proper (internal) release every two weeks. The official builds are published in an externally accessible place for everybody to download and try it out - this gives us a way to regularly demo our progress and provides a way for the Product Owner to correct our course (e.g. whenever we stray from what has been planned or whenever the plan needs to be changed due to external circumstances).</p>

<p>Second - our Bamboo-based <a href="http://www.atlassian.com/agile/tools/">automated build system</a> lets the Product Owner download and use the snapshot of our work every day - the snapshot may be barely usable, but typically it is in a good enough shape to let the Product Owner try new features as they are delivered.</p>

<p><big><strong>Remote Dogfooding</strong></big></p>

<p>We regularly practice <a href="http://en.wikipedia.org/wiki/Eat_your_own_dog_food">dogfooding</a>, using our product regularly on a daily basis. Almost every nightly snapshot is taken and used by each team member every morning. So if we happen to have a bug, we are quite likely to intercept it before the official version of the product is released. Also, if some user interface elements are awkward to use, or somebody has a bright idea for improving it, we are able to try multiple solutions and pick the best one - simply because we use our own software for our day-to-day work.</p>

<p>But in order to get even better feedback, developers in the Sydney office should also dogfood our software and give us feedback. Achieving sufficient levels of internal adoption requires a bit of advertising from the Product Owner, and regular presentations of cool new features that the product provides, so that folks in Sydney have a reason to bother installing our stuff.</p>

<p><big><strong>Separation Of Responsibilities</strong></big></p>

<p>The last thing that is important for a remote team and the Product Owner working with it is a separation of responsibilities. In our case, the Product Owner is only responsible for prioritizing features and controlling if they are delivered as promised. Planning and implementation are left to us. Also - all bug reports are handled locally by the team, as a part of the Quality Assurance process - we are solely responsible for keeping the product in good shape and the Product Owner does not interfere with that.</p>

<p>Another important rule we follow, is that the team's effort estimations are final. The Product Owner (or anybody else) does not even attempt to question them, trusting our judgement - in true agile spirit.</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=mY9aI4se2vA:upbq2bw6AC8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=mY9aI4se2vA:upbq2bw6AC8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=mY9aI4se2vA:upbq2bw6AC8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/mY9aI4se2vA" height="1" width="1"/>]]></content:encoded><description> We Are From Mars All agile methodologies stress the need of co-locating development with the customer's representative - the Product Owner - or at least, having them in close proximity -...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/07/agile_with_a_remote_product_ow.html</feedburner:origLink></item><item><title>Make Your Code Agile: Refactoring</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/lvT1ZdgyCyQ/make_your_code_agile_refactori.html</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Chris Mountford</dc:creator><pubDate>Thu, 02 Jul 2009 10:47:29 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2429</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="Agile software development at Atlassian" style="float: right; margin: 0 0 20px 20px;"></a></p>

<p>First, my definition of refactoring:</p>

<blockquote>Refactoring is improving code without changing the features it implements.</blockquote>

<p><em>That's all.</em></p>

<p>If you're refactoring, you're not fixing bugs, you're not improving performance and you not increasing robustness. Refactoring is simply improving the design of the code, while ensuring that it still works the same, warts and all.</p>

<p><a href="http://en.wikipedia.org/wiki/Pointy-Haired_Boss">Pointy haired bosses</a> the world over froth at the mouth to hear such things. Veins pop out in their temples. <em>You mean the business value of the software stays the same but the cost to the business goes up?</em> I didn't say that.</p>

<p>If you measure business value only in terms of features you have today, then you can end up deep in <a href="http://en.wikipedia.org/wiki/Technical_debt">technical debt</a>; you can add features today in such a way that features tomorrow cost more and more. The value of refactoring is wholly contained in the future ability of programmers to comprehend and modify the code. It's called maintainability, but that's a boring word, so let's call it Agility. Mmmm, sexy. Well-factored code is <em>agile</em> code because it's better able to change.</p>

<p>In economic terms, refactoring is an investment, or the repayment of a debt. It's only worth doing over a time frame when the interest payments or repayments (in the form of ongoing productivity gains) compound to exceed the time invested. Fingers crossed the business or project sponsor is also planning over such time frames.</p>

<p>The term refactoring comes from mathematics. You may remember your high school algebra:</p>

<blockquote><big>2x<sup>2</sup> + 10x</big></blockquote>

<p><strong>Stay with me! No glazing over!</strong> If you refactor the expression, extracting the common factor, 2x, you get:</p>

<blockquote><big>2x . (x+5)</big></blockquote>

<p>Sometimes it's hard to spot the common factors, in both mathematics and programming, and it can certainly be done poorly. More on that later.</p>

<p>Like most powerful techniques in software development, the purpose of refactoring is controlling complexity.</p>

<p>Complexity is bad, mmmkay. Complexity is evil.</p>

<p>I was fortunate enough to be chatting about project complexity with the legendary <a href="http://www.davethomas.net/">Dave Thomas</a> (OTI, Eclipse) at <a href="http://jaoo.com.au/sydney-2009/">JAOO Sydney</a> in May. He nailed it: "kLOC kills". Complexity and scale in codebases is a major contributor to schedule blowouts, poor velocity and excessive development cost. Complexity is a kitten killer from way back.</p>

<p><a href="http://en.wikipedia.org/wiki/No_Silver_Bullet">Fred Brooks discusses</a> two categories of complexity. <a href="http://en.wikipedia.org/wiki/Essential_complexity">Essential complexity</a> and <a href="http://en.wikipedia.org/wiki/Accidental_complexity">accidental complexity</a>.</p>

<p>Essential complexity is the complexity of the domain. In NASA software, there's no escaping rocket science. You can isolate and divide essential complexity but you can never remove it. Essential complexity belongs to the problem. By contrast, accidental complexity is an artefact of the systems, languages, frameworks you're using. In principle it can be reduced by changing the system. Accidental complexity belongs to the solution. Refactoring reduces accidental complexity.</p>

<p>If you don't have much experience with it and you're looking for some concrete tutorials on refactoring, I suggest you start with Martin Fowler's seminal book <a href="http://martinfowler.com/books.html#refactoring">Refactoring</a>. Fowler also maintains a <a href="http://www.refactoring.com/catalog/">catalog</a> of refactoring recipes with an Object Oriented flavour.</p>

<p>One of the most basic techniques is <a href="http://www.refactoring.com/catalog/extractMethod.html">Extract Method</a> which all decent IDEs can do automatically. You know you need Extract Method if you have a multi-page method with a sequence of of comment blocks which look like this: <em>Now that we have the InductionActuator, look up the FluxCapacitor...</em>. Doing it manually means snipping out a logical sequence of code and pasting it into a new, small, well-named method, stitching the local variables used from the originating context into parameters to the method. If this is hard due to sloppy scoping or too many variables, you may consider Introducing a Field from a local variable.</p>

<p>The inner loop of <a href="http://www.atlassian.com/agile">agile development</a> should go like this: Red, Green, Refactor. Red means you have a test which is not passing. Getting the test to pass is the next step. Green means you are passing all tests. Refactor means... refactor. </p>

<p>Even if you're a good <a href="http://www.atlassian.com/agile/people/developer.jsp">agile developer</a>, doing things as simply as possible, complexity and duplication of common factors creeps in while you're trying to pass tests. Everybody hacks. Everyone copies and pastes. This is fine as long as you go back and refactor when you've got the green bar. Sometimes you may need to avoid mentioning this to <a href="http://en.wikipedia.org/wiki/Pointy-Haired_Boss">PHBs</a>, for their own good. Shhh!</p>

<p>Unit tests are really important for refactoring. If you're not doing unit testing you've got a long way to go. A good unit test suite is a necessary precondition for confident, aggressive refactoring. And IMHO a good test system is a necessary precondition for confident, aggressive, <em>automated</em> refactoring. These preconditions can present a quandary for some developers. Legacy systems often have no effective automated tests. And since they're often composed entirely of <a href="http://www.retrologic.com/jargon/S/spaghetti-code.html">spaghetti</a>, they <em>need</em> to be refactored. It's a chicken and egg situation, where do you start? All I can say here is you start small.</p>

<h3>Refactoritis</h3>

<p>Can you have too much refactoring? Absolutely. If you're somewhere around middle-stage zealotry for this refactoring stuff, you may not be in danger of copy+pasting your way to a <a href="http://www.laputan.org/mud/">big ball of mud</a>, but you may fall prone to exceed the safe working abstraction load of your language or go too far beyond the idioms of your team's codebase or comfort.</p>

<p>Every language has limits imposed by its design and implementation. In Java and C#, for example, the limits are seen by many in the dynamic languages camps to be too much to bear. For example, say you're refactoring some Java or C# code. You might create a new interface with a few alternate concrete implementations and, whereas before you had two methods on a concrete class and a few big if-else blocks, after refactoring you might have three files and more actual lines of source code. It can be somewhat subjective but sometimes you may have more complexity even though you've removed duplication!</p>

<p>If this happens you have fallen asleep on the refactoring train and missed your station. Often you should just roll back the code and go write a feature. Some duplication is easy to see and cope with, especially if it can fit on one screen and any reader can see the pattern. In other languages, Lisp comes to mind, there are constructs (like macros) which allow you to encapsulate expressions that <a href="http://apocalisp.wordpress.com/2008/05/16/thrower-functor/">cannot be elegantly factored in, say, Java</a>. Disclaimer: IANALN; I Am Not A Lisp Nerd.</p>

<p>So the expressiveness of the language can constrain refactorability. Another way of saying this is that the language contains accidental complexity and only factoring out the language can remove that complexity. I should say here that I have recently found Groovy to be a great candidate for doing this on Java projects.</p>

<p>As a more concrete example, lexical closures are a great way of implementing things like the new for loop (for each) introduced in Java 1.5 and functor frameworks that employ anonymous inner classes in Java for similar purposes (e.g. composable transformers, ad hoc iterator delegators instead of explicit looping) often feel too cumbersome compared to most closure implementations. So you just have to suffer the duplication and code bulk.</p>

<p>So in summary, Red, Green, Refactor, don't go overboard and be aware when your language makes capturing factors you see in your system worse. Kill complexity before it kills you.</p>

<p>If you're interested I'll be telling war stories and going into some side issues over on <a href="http://blog.chromosundrift.com/">my personal blog</a>.</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=lvT1ZdgyCyQ:JvXjrFZ1OBY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=lvT1ZdgyCyQ:JvXjrFZ1OBY:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=lvT1ZdgyCyQ:JvXjrFZ1OBY:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/lvT1ZdgyCyQ" height="1" width="1"/>]]></content:encoded><description>In this post I define and promote refactoring in productivity terms through controlling code complexity.</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/07/make_your_code_agile_refactori.html</feedburner:origLink></item><item><title>Help us Integrate Confluence with Alfresco</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/G9yT_GuMkn8/integrate_confluence_alfresco.html</link><category>Confluence</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bill Arconati</dc:creator><pubDate>Wed, 01 Jul 2009 18:10:58 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2453</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p>At Atlassian we're always looking for ways to expand the utility and functionality of our products.  Sometimes this means we develop that code ourselves, and sometimes it means our community fills in the blanks. Such is the case with a new <a href="http://code.google.com/p/confluence-alfresco/">integration</a> we've been working on with <a href="http://www.alfresco.com/">Alfresco</a>, an open-source content management company, and <a href="http://www.sourcesense.com/en/home">Sourcesense</a>, a mutual partner of Atlassian's and Alfresco's. In a nutshell, we're integrating Alfresco with Confluence to bring <a href="http://wiki.alfresco.com/wiki/OpenSearch">OpenSearch</a> to Confluence wiki pages and to embed Alfresco-stored documents into Confluence, among other things.</p>

<p>One of the best things about this integration is that it speaks to both the power of Alfresco's open-source code and Atlassian's open APIs: the integration was started without any formal communication between the three companies. <strong>All the more reason to add another party to the mix: YOU!</strong></p>

<p>While the integration is still a bit rough around the edges, we're inviting you to get involved and take the integration in the directions that you'd like to see. We're basing the initial integration on the new CMIS (<a href="http://en.wikipedia.org/wiki/Content_Management_Interoperability_Services">Content Management Interoperability Services</a>) standard, which means that not only can we build a range of new functionalities on top of the baseline integration, but we can also extend the integration to a diverse range of different technologies.<br />
<br><br />
<big><strong>We've Accomplished So Much Already</strong></big></p>

<p>The current Alfresco Plugin for Confluence provides a set of custom macros that enable Confluence wiki pages to display or embed Alfresco documents or document metadata through ID reference, Path reference or CMIS Search Query.  The expectation is that we can provide a seamless experience to users of both platforms:<br />
<ul><br />
	<li><u>Search integration within Alfresco</u>: By adding OpenSearch capabilities to Confluence, Alfresco is now able to aggregate search results from Confluence wiki pages and the Alfresco repository. So, for example, a user logged into Alfresco will be able to retrieve data from documents hosted in an Alfresco repository and any Confluence page she's got access to. This is a good example of what open standards and extensible systems can offer.</li><br />
	<li><u>New macros for Confluence</u>, providing wiki users with the ability to interact with an Alfresco repository in a number of ways, such as:<br />
<ul><br />
	<li>Browse an Alfresco repository</li><br />
	<li>Reference (link) and embed (display directly) documents stored in Alfresco</li><br />
	<li>Build custom reports (such as listing documents that match specified criteria) by running queries against the Alfresco repositories</li></ul><br />
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://blogs.atlassian.com/developer/2009/06/30/Confluence%20Alfresco%20Thumbnail.png"><img alt="Confluence Alfresco Thumbnail.png" src="http://blogs.atlassian.com/developer/2009/06/30/Confluence Alfresco Thumbnail-thumb-400x199.png" width="400" height="199" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></span><br />
</li><br />
<li><u>Use Web Scripts or CMIS:</u> As an added bonus, macros are implemented using either Alfresco-specific technologies such as Web Scripts or a pure standard-based approach (CMIS).  The <a href="http://wiki.alfresco.com/wiki/Web_Scripts">Web Scripts</a> technology is very, very cool and opens the door to do some interesting new capabilities for Confluence.</li></ul></p>

<p><br />
<big><strong>This is only the beginning</strong></big></p>

<p>Even as I type this, an email has hit my inbox noting that new features, like the ability to pull Alfresco Share (Alfresco's collaboration application) site activities into Confluence via an Atom feed, have been added. We'd love to have you help us figure out where to take this integration, and to fill in pieces you think are missing. That's the benefit of open standards and open source: Confluence can be even more than Atlassian envisions. </p>

<p>We're hosting the project on Google Code: <a href="http://code.google.com/p/confluence-alfresco/">http://code.google.com/p/confluence-alfresco/</a>.  <strong>Please stop by and get involved.</strong><br />
<br><br />
<big><strong>UPDATE</strong></big><br />
You can also get SSO w/ Crowd for your Alfresco install:<br />
<a href="http://code.google.com/p/alfresco-crowd-security/">http://code.google.com/p/alfresco-crowd-security/</a></p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=G9yT_GuMkn8:irjiMZV_aMs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=G9yT_GuMkn8:irjiMZV_aMs:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=G9yT_GuMkn8:irjiMZV_aMs:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/G9yT_GuMkn8" height="1" width="1"/>]]></content:encoded><description>At Atlassian we're always looking for ways to expand the utility and functionality of our products. Sometimes this means we develop that code ourselves, and sometimes it means our community fills in...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/integrate_confluence_alfresco.html</feedburner:origLink></item><item><title>Hamcrest saves your soul - Now with less suffering!</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/dbEHvaHd50g/hamcrest_saves_your_soul_now_w.html</link><category>20 Percent Time</category><category>Java</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Wallace</dc:creator><pubDate>Mon, 29 Jun 2009 11:31:45 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2450</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p>In a previous post I described <a href="http://blogs.atlassian.com/developer/2009/06/how_hamcrest_can_save_your_sou.html" rel="nofollow">how Hamcrest can save your soul</a>.  After writing that, it was pointed out that you probably don't need to suffer so much boiler-plate to save your soul.</p>

<p>With that thought I set out to write the <tt>deeplyIsEqual</tt> matcher.  The result is the <a href="http://labs.atlassian.com/source/browse/AHAM/trunk/src/main/java/com/atlassian/hamcrest/DeepIsEqual.java?r=2624" rel="nofollow">DeepIsEqual</a> Matcher.  Using it is pretty easy.  In that previous post I described the Matcher for comparing lightsabers, LightsaberIsEqual.  We no longer need that.  Now we can simply do</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    assertThat(anakinsLightsaber, is(deeplyEqualTo(lukesFirstLightsaber));
</pre>
</div></div>

<p>No extra boilerplate is needed and we still get all the benefits of only seeing the fields that didn't match in the error messages.  Oh, and the reported expected value is built using reflection too, so you don't need to worry about whether the type of objects you're comparing implement <tt>toString</tt> or how they implement it.</p>

<p>After going through the initial implementation I started switching over the OAuth tests to use this new matcher.  Everything was going great until I hit a case where <tt>PublicKey</tt> or <tt>PrivateKey</tt> objects were being compared.  The problem was, one was a generated key and the other was converted from an encoded string value.  Apparently, depending on which way you create <tt>Key</tt> objects, the internal fields can be slightly different.  So, comparing them recursively was failing.  I struggled with what to do - I could add the <tt>Key</tt> to the <a href="http://labs.atlassian.com/source/browse/AHAM/trunk/src/main/java/com/atlassian/hamcrest/DeepIsEqual.java?r=2624#l144" rel="nofollow">internal, hardcoded types</a> that are compared by simply using <tt>equals()</tt>, or I could add the ability for testers to specify how they wanted certain types to be compared using a <tt>MatcherFactory</tt>.  This being 20% time and me seeing other places where specifying custom matchers for certain types would be very useful, I went with the second option.</p>

<p>So, if we wanted to match <tt>Hilt</tt>s in a very specific way we could that pretty simply.  </p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    <span class="code-keyword">public</span> <span class="code-keyword">static</span> Matcher&lt;? <span class="code-keyword">super</span> Lightsaber&gt; equalTo(Lightsaber lightsaber)
    {
        <span class="code-keyword">return</span> deeplyEqualTo(lightsaber, extraTypeMatchers);
    }

</pre>
</div></div>

<p>I wrapped the <tt>deeplyEqualTo</tt> call in a convience method, because the creation of the extraTypeMatchers can be a bit gnarly and makes the tests a bit harder to read.  As a simple example, let's say we want to compare {{Hilt}}s using their <tt>equals()</tt> and don't care if the subtypes are different.  To accomplish that we could use the following <tt>extraTypeMatchers</tt>.

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">import</span> <span class="code-keyword">static</span> com.atlassian.hamcrest.ClassMatchers.isAssignableTo;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> com.atlassian.hamcrest.DeepIsEqual.deeplyEqualTo;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> com.atlassian.hamcrest.MatcherFactories.isEqual;
    Map&lt;Matcher&lt;<span class="code-object">Class</span>&lt;?&gt;&gt;, MatcherFactory&gt; extraTypeMatchers = <span class="code-keyword">new</span> HashMap&lt;Matcher&lt;<span class="code-object">Class</span>&lt;?&gt;&gt;, MatcherFactory&gt;()
    {{
        put(isAssignableTo(Hilt.class), isEqual());
    }};
</pre>
</div></div>

<p>That's not very pretty, but by wrapping it up our test can go back to simply being</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    assertThat(anakinsLightsaber, is(equalTo(lukesFirstLightsaber));
</pre>
</div></div>

<p>and we get all the benefits we had before, plus we are matching <tt>Hilt</tt>s in the desired way.</p></p>

<p>The biggest thing left to tackle is how to handle object graphs that have cycles in them.  We have some ideas about how to do it, such as tracking the objects visited and if we find one that we've already visited just stop recursion and assume it will be true, relying on all the other matching to prove otherwise.  I'm not 100% sure this will work.  The main problem being, what happens if a field in expected value has a reference to, for example, the first object in the graph.  But, the actual value doesn't have a cyclic reference but does have a reference to an object that should be considered "equal" to the same object as the expected value (I know, it's probably not entirely clear what I mean but I'm having a problem figuring out how to phrase it more better as my brain keeps breaking when I think too hard about it).  I'm thinking for now, being able to specify custom matchers for types should be enough.  If there is some part of the object graph that we expect to have a cycle, then just specify a custom matcher for it and move on.</p>

<p>To play with this you can checkout the code from <a href="http://labs.atlassian.com/svn/AHAM/trunk" rel="nofollow">the labs project</a>, or you can get from our <a href="http://maven.atlassian.com/public/com/atlassian/hamcrest/atlassian-hamcrest/1.0.beta1/" rel="nofollow">public maven repository</a>.  If you find any problems with it report them in <a href="http://labs.atlassian.com/browse/AHAM/" rel="nofollow">JIRA</a>.</p>

<p>Thanks, and enjoy testing again!</p>

        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=dbEHvaHd50g:V156DBSC8LM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=dbEHvaHd50g:V156DBSC8LM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=dbEHvaHd50g:V156DBSC8LM:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/dbEHvaHd50g" height="1" width="1"/>]]></content:encoded><description>In a previous post I described how Hamcrest can save your soul. After writing that, it was pointed out that you probably don't need to suffer so much boiler-plate to save your...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/hamcrest_saves_your_soul_now_w.html</feedburner:origLink></item><item><title>How to determine the context your macro is being rendered in</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/5Dm0jmygoJE/how_to_determine_the_context_y.html</link><category>Confluence</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Cheryl Jerozal</dc:creator><pubDate>Thu, 25 Jun 2009 13:16:32 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2447</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p>For Confluence 2.10 (remember that?), we converted the display of the <a href="http://confluence.atlassian.com/display/DOC/JIRA+Issues+Macro">Jira Issues Macro</a>  from using a static HTML table to using a table infused with jQuery goodness. Now we could add features that wouldn't have been possible without JavaScript, like the ability to sort issues in the page without even reloading. That was pretty cool, but it also meant we had a new problem to deal with: macros can be rendered in places that can't render JavaScript, such as in a feed reader or an email notification. In those cases, our beautifully redesigned macro would look something like a puddle of goo. </p><br/>

<p>We thought about how to get around this new problem, and decided the best approach would be to make it possible for macros to find out if they are being rendered in an email or a feed, so they can display themselves appropriately. It was already possible for macros to find out if they are being rendered in a PDF document or several other contexts. In Confluence 2.10, we made it possible for macros to find out that they were being displayed in an email or feed, an addition to the previously defined contexts. Previously, macros being viewed in an email or feed reader would have just had the render type "display", which is the default.</p><br/>

<p>Now the Jira Issues Macro is able to render itself differently in display versus feed modes:<br/>
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://blogs.atlassian.com/developer/jiraissues_in_page_cropped.html" onclick="window.open('http://blogs.atlassian.com/developer/jiraissues_in_page_cropped.html','popup','width=1247,height=543,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://blogs.atlassian.com/developer/jiraissues_in_page_cropped-thumb-200x87.png" width="200" height="87" alt="jiraissues_in_page_cropped.png" class="mt-image-none" style="" /></a></span>
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://blogs.atlassian.com/developer/jiraissues_in_feed_cropped1.html" onclick="window.open('http://blogs.atlassian.com/developer/jiraissues_in_feed_cropped1.html','popup','width=1102,height=388,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://blogs.atlassian.com/developer/assets_c/2009/06/jiraissues_in_feed_cropped-thumb-200x70.png" width="200" height="70" alt="jiraissues_in_feed_cropped.png" class="mt-image-none" style="" /></a></span></p><br/>

<p>Okay, so how can you find out the current render context from within your macro? When creating a plugin that includes a <a href="http://confluence.atlassian.com/display/DOC/Macro+Plugins">macro module</a>, you return the HTML that the macro will display from the execute() method of the macro class. One of the parameters to the execute() method, the one with type RenderContext, can be used to determine how the macro is being rendered. </p><br/>

<p>Here's a sample execute method from a macro that prints out the current render context type:<br/>
<pre><span class="category1">public</span> <span class="category2">String</span> execute(Map parameters, <span class="category2">String</span> body, RenderContext renderContext)
{
 <span class="category1">if</span>(RenderContext.FEED.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">FEED render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.EMAIL.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">EMAIL render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.HTML_EXPORT.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">HTML_EXPORT render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.PREVIEW.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">PREVIEW render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.DISPLAY.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">DISPLAY render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.PDF.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">PDF render type</span>";
 <span class="category1">else</span> <span class="category1">if</span>(RenderContext.WORD.equals(renderContext.getOutputType()))
 <span class="category1">return</span> "<span class="quote">WORD render type</span>";
 <span class="category1">else</span>
 <span class="category1">return</span> "<span class="quote">some other render type</span>";
}</pre><p><br />
</p></p>

<p>If you used this sample macro on a page you were editing (by first installing the plugin that contains it), you could visit the preview tab to see it output "PREVIEW render type". In the case of a more complex macro, you could, say, disable some UI elements when the RenderContext.PREVIEW.equals(renderContext.getOutputType()) check is true. Using these checks is exactly how the Jira Issues Macro decides whether to render itself using JavaScript or just stick with a basic HTML version.</p>

        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=5Dm0jmygoJE:vfxEMSfzjx4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=5Dm0jmygoJE:vfxEMSfzjx4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=5Dm0jmygoJE:vfxEMSfzjx4:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/5Dm0jmygoJE" height="1" width="1"/>]]></content:encoded><description>For Confluence 2.10 (remember that?), we converted the display of the Jira Issues Macro from using a static HTML table to using a table infused with jQuery goodness. Now we could add...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/how_to_determine_the_context_y.html</feedburner:origLink></item><item><title>Pair Programming is Kryptonite!</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/U70J8dOWfRU/pair_programming_is_kryptonite.html</link><category>Agile</category><category>Crucible</category><category>JIRA</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Chris Mountford</dc:creator><pubDate>Thu, 25 Jun 2009 09:59:17 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2375</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="Agile software development at Atlassian" style="float: right; margin: 0 0 20px 20px;"></a></p>

<p>Pair programming is easily the most controversial <a href="http://www.atlassian.com/agile">agile development</a> practice and in my experience the most frequently avoided. It makes some people pretty uncomfortable.</p>

<p>Just so we're all on the same page, <a href="http://www.atlassian.com/agile/practices/code-quality.jsp">pair programming</a> means two programmers working together on one computer. <strong>Yes really</strong>. Normally one codes while the other thinks ahead and around. Or, one drives and the other navigates, swapping regularly. The motivation is higher quality output from the outset.</p>

<p>Pair programming requires working much closer with team mates than most <a href="http://www.atlassian.com/agile/people/developer.jsp">developers</a> are accustomed to. This is both its power and it's greatest weakness.</p>

<p>Working so closely with others can bring otherwise hidden personal issues to the surface. My experience in previous jobs has shown me that pair programming can be so personally challenging for some people that the practice is infeasible for that team. Doing it takes courage and maturity, it requires a healthy environment where mutual respect, humour and humility are all expected. Not to mention minimum standards of personal hygiene! This is quite different from the stand-offish formalism characteristic of many alternatives and this humanistic style sets the bar too high for some. Cowboys, incompetent introverts, control freaks and superhero programmers all resist pair programming. It's like their <a href="http://en.wikipedia.org/wiki/Kryptonite">kryptonite</a>.</p>

<p>Like culture shocked westerners first visiting Tokyo, unused to the dramatic reduction in personal space one can expect, say, on a commuter train, for some, getting used to the personal invasion that is pair programming is a self-imposed barrier they just need to break through.</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://www.cenqua.com/pairon/index.html"><img alt="pairon.jpg" src="http://blogs.atlassian.com/developer/pairon.jpg" width="460" height="264" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></a></span></p>

<p>Pairing up with different people makes this personal contact a dynamic challenge. There are several axes of individual difference which demand different behaviour. For example there's the novice-expert axis and the introvert-extrovert axis and people have different natural working styles and favoured problem solving strategies. For example I can often be willing to explore unconventional solution paths which make methodical people a little unsettled. Being aware of that helps me communicate appropriately and change my behaviour where necessary.</p>

<p>Pairing requires bilateral adaptability and the art of compromise.</p>

<p>There are many common anti-patterns that lead to ineffective pair programming. These things are fairly well documented, for example see <a href="http://books.google.com.au/books?id=LRQhdlrKNE8C&dq=pair+programming+illuminated&printsec=frontcover&source=bn&hl=en&ei=FhAMSpyOFcKGkAXymJmzBA&sa=X&oi=book_result&ct=result&resnum=4">Pair Programming Illuminated</a> by Laurie Williams and Robert Kessler. It's worth tackling these problems actively because ineffective pair programming is worse than no pair programming and it's obvious even to would-be starry-eyed XP fans.</p>

<p align="center"><br><br><object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ildf0ropwo%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ildf0ropwo%2F2%2Fconfig.xml&autoplay=false"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ildf0ropwo%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/></object></p><br>

<p>One of the reasons I've been thinking a lot about pair programming recently is because I haven't been doing it for a change. (gasp!)</p>

<p>Apart from some time on support and fixing a pile of trivial bugs (not pairing), I've been working solo on a front-end feature for JIRA 4.0. Solo because our team has an odd number of people right now (<a href="http://www.atlassian.com/about/careers/">wanna job?</a>). It has actually put me in a great position to evaluate the practice of pair programming somewhat objectively.</p>

<p>First the good stuff about not pairing. I have some great new headphones and with the right music I can get into the zone and stay there for hours without needing to coordinate with anyone. I do not have to continually exert effort listening or thinking of ways to express my questions or ideas. I just think and do. That's refreshing. </p>

<p>But I'm not ashamed to admit it's a little isolating. Hey, I'm a people person.</p>

<p>The negatives of not pair programming are especially visible when you are accustomed to doing it. While not pairing recently I have become curiously suspicious of the quality of my work. </p>

<p><em>I think it's good, Is it really good? I'm not gold-plating am I? Maybe this is absolutely the wrong approach. Is there something obvious that I'm missing? I'd better get someone to review this. <br />
</em></p>

<p>All those thoughts are plaguing me in my "step back moments" when the tests pass and I get to wonder about the next move. This wouldn't happen if I was pairing. I would be in dialogue with my partner; we would have <em>common mind</em> (just imagine Yoda saying that last bit).</p>

<p>Pair programming can really help in <a href="http://blog.chromosundrift.com/2009/05/when-team-gels-motorcycle-powerband.html">making a team gel</a>. Spending time together - not just in pair programming but in other activities like sharing a meal or social event <strong>during work time</strong> help teams to gel.</p>

<p>So while there are many reasons to practice pair programming, it's not the only way to get these benefits. <a href="http://www.atlassian.com/software/crucible/">Code review</a> is an obvious alternative for many of the same benefits. Marketing may want to break my arms if I don't mention <a href="http://www.atlassian.com/software/crucible/">Crucible</a> at this point.</p>

<p>So if you're not improving the quality of your code with pair programming, then I'd hope you are doing code reviews because there really isn't much disagreement in the industry that this is a very well known and effective method of ensuring <a href="http://www.atlassian.com/agile/practices/code-quality.jsp">quality software development</a>.</p>

<p>Atlassian development teams do either one or the other, or sometimes, like in JIRA some of both.</p>

<p>Using a tool like <a href="http://www.atlassian.com/software/crucible/">Crucible for code reviews</a> has some advantages over pair programming, such as record-keeping and working across distributed teams and time zones. Pair programming has some advantages over code review such as swapping keyboard shortcuts and shell one-liners and helping a team to gel.</p>

<p>So if you have a business case for better quality software then remember: two heads are better than one!<br />
<p align="center"><br><object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ikjtk2v38n%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ikjtk2v38n%2F2%2Fconfig.xml&autoplay=false"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6ikjtk2v38n%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/></object><br><br><a href="http://www.atlassian.com/agile/"><strong>Learn about agile software development inside Atlassian</strong></a><br />
</p><br />
</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=U70J8dOWfRU:uehZIvOfiC4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=U70J8dOWfRU:uehZIvOfiC4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=U70J8dOWfRU:uehZIvOfiC4:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/U70J8dOWfRU" height="1" width="1"/>]]></content:encoded><description>Heroes, cowboys, incompetent introverts and egomanic wizards all resist pair programming. It's like their kryptonite.</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/pair_programming_is_kryptonite.html</feedburner:origLink></item><item><title>Agile Project Management With Virtual Index Cards</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/XRYvYRVYWw0/jira_studio_and_greenhoppers_v.html</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Ted Tencza</dc:creator><pubDate>Tue, 30 Jun 2009 09:26:23 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2291</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="Agile software development at Atlassian" style="float: right; margin: 0 0 20px 20px;"></a></p>

<p><a href="http://www.atlassian.com/agile/practices/planning.jsp">Index Cards</a> have become an indispensable tool for a software development team practicing <a href="http://www.atlassian.com/agile">agile</a>. There are a <a href="http://blog.mountaingoatsoftware.com/the-ideal-agile-workspace" rel="nofollow">host</a> of <a href="http://www.mountaingoatsoftware.com/task-boards" rel="nofollow">good</a> <a href="http://www.think-box.co.uk/blog/2005/12/use-index-cards-or-planning-tool-to.html" rel="nofollow">sites</a> with information on strategies for and benefits of utilizing index cards.</p>
<p><a href="htttp://www.atlassian.com/agile">Agile teams</a> use Index Cards for a variety of reasons, including:</p>

<ul>
	<li>helping with iteration planning</li>
	<li>assigning tasks and then tracking developer assignments</li>
	<li>recording task estimates</li>
	<li>as one component of the team's <a href="http://alistair.cockburn.us/Information+radiator" rel="nofollow">Information Radiator</a>.</li>
	<li>reminders of the conversations between customers and developers around the definition of user stories.</li>
</ul>

<p>The cards are an excellent visual representation of the health of the iteration. A majority of cards still in the "to do" with the end of an iteration pending usually means the team is behind schedule. A scarcity of cards means the developers will soon run out of tasks and so the backlog had better be up to date. Cards also work well as a great tool for facilitating the assigning of work (more specifically, allowing developers to assign themselves individual tasks). On the JIRA Studio team at Atlassian, back in "Ye Olden Days" (circa 2008) we used to use actual physical index cards, stuck to the team white board with blu tack. The use of physical index cards does have drawbacks:</p>
<ul>
	<li>Duplication of effort</li>
	<li>Data from cards and issue tracker being out of synch</li>
	<li>Bad handwriting leading to incorrect implementations</li>
	<li>Tons of old index cards lying around</li>
</ul>

<p>We now use <a href="http://www.atlassian.com/greenhopper/features/">virtual index cards</a>, thanks to the magic of <a href="http://www.atlassian.com/greenhopper">Greenhopper</a>, an agile planning tool that is now bundled free with JIRA Studio:&nbsp;</p>
<br>
<p align="center"><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://blogs.atlassian.com/developer/PlanningBoardBig1.html" onclick="window.open('http://blogs.atlassian.com/developer/PlanningBoardBig1.html','popup','width=1216,height=603,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://blogs.atlassian.com/developer/assets_c/2009/03/PlanningBoardBig-thumb-400x198.jpg" width="400" height="198" alt="Agile software development in action - GreenHopper for JIRA - Screenshot 1" class="mt-image-none" style="" /></a></span></p>
<br>
<p>Greenhopper is key to being able to do <a href="www.atlassian.com/agile">agile project management</a>.  We use the <a href="http://www.atlassian.com/greenhopper/features/default.jsp">virtual cards</a> for the traditional tasks of iteration planning, capturing requirements, and capturing acceptance criteria. The use of virtual cards allows for all of the functionality and advantages of physical cards, with the added advantage of better communication, and instant updates. Developers still "pick" their cards off the task board, track work on the cards, and place them in the "done" column when complete.&nbsp;</p>

<p>The JIRA Studio team was not a distributed team when we made the change to using the Greenhopper plugin exclusively. But now that one member is on another continent, the benefit of virtual cards to help with communication has grown even greater. Our Information Radiator is created with the virtual card wall displayed on a large monitor which also shows the current build status. Everyone on the local team can glance at the monitor for a visual representation of the health of the build and of the iteration, while the remote team member can view the exact same information in his web browser.</p>

<p>The synchronization of data is by far and away the biggest advantage of virtual cards, as an instant update of all information occurs whenever a card is updated. No more does a task get ignored due to a stray mark on the card. No longer does a developer waste time entering the same data onto the card and into JIRA. And the Team Lead no longer spends 30 minutes transcribing cards into JIRA issues or copying JIRA tasks onto cards in his chicken-scratch handwriting.</p>

<p><a href="http://www.atlassian.com/greenhopper">Greenhopper</a> is integrated with <a href="http://www.atlassian.com/studio">JIRA Studio</a> (and <a href="http://www.atlassian.com/greenhopper/pricing.jsp">available</a> as a plug-in for <a href="http://www.atlassian.com/software/jira/">JIRA</a>), so changes to a card are reflected in the JIRA issue, and vice versa. Moving the card to a new iteration, or new column on the board is reflected in the JIRA Issue. Changing the JIRA issue status to Assigned or Resolved immediately moves the card into the correct column on the Information Radiator. Almost as valuable is the ability to have multiple views of the information, without having to continually move physical cards. The cards can be viewed as a task board or a planning board, and sorted by priority, assignee, or a host of metrics.</p>

<p align="center"><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://blogs.atlassian.com/developer/TaskBoardjpg1.html" onclick="window.open('http://blogs.atlassian.com/developer/TaskBoardjpg1.html','popup','width=1627,height=462,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false"><img src="http://blogs.atlassian.com/developer/assets_c/2009/03/TaskBoardjpg-thumb-400x113.jpg" width="400" height="113" alt="Agile software development in action - GreenHopper for JIRA - Screenshot 2" class="mt-image-none" style="" /></a></span></p><br><p>Last, but definitely not least, the use of virtual cards mitigates a few of the <a href="http://agileconsulting.blogspot.com/2009/03/dangers-of-agile.html" rel="nofollow">dangers of agile</a>.</p>
<br>
<p align="center">
<big>Watch Ted talk about Virtual Index Cards with GreenHopper</big>
<br>
<br>
<object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imd5gx4jdn%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imd5gx4jdn%2F2%2Fconfig.xml&autoplay=false"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imd5gx4jdn%2F2%2Fconfig.xml&autoplay=false" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/></object>
<br>
<a href="http://www.atlassian.com/agile/"><strong>Watch more videos on agile software development inside Atlassian</strong></a>
</p>
<div class="clearer">
</div>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=XRYvYRVYWw0:HN6QU9Iyx0Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=XRYvYRVYWw0:HN6QU9Iyx0Y:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=XRYvYRVYWw0:HN6QU9Iyx0Y:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/XRYvYRVYWw0" height="1" width="1"/>]]></content:encoded><description> Index Cards have become an indispensable tool for a software development team practicing agile. There are a host of good sites with information on strategies for and benefits of utilizing index...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/jira_studio_and_greenhoppers_v.html</feedburner:origLink></item><item><title>Getting Started With Agile - Daily Standup Meetings</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/Yhe2n2qHQmw/getting_started_with_agile_daily_standup_meetings.html</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Per Fragemann</dc:creator><pubDate>Wed, 17 Jun 2009 09:10:13 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2424</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <h3>Becoming agile. The first baby-step</h3>

<p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="agile" style="float: right; margin: 0 0 20px 20px;" /></a></p>

<p>There are many ways to become <a href="http://www.atlassian.com/agile">agile</a>. And many ways to fail horribly while trying! Purists say that you are not really agile until you do <em>everything</em> in a truly agile fashion. That might be true. But beware - don't try to start everything <em>at the same time</em>. That's a recipe for disaster. To make the transition to agile go smoothly, this blog post focuses on a simple way to get started with agile: perform daily standup-meetings.</p>

<p>Why start with stand-ups? Because they work perfectly in any environment, including traditional waterfall environments. So you can get started without even telling your boss about your agile intentions, and achieve good results even if you ultimately decide to remain non-agile. Daily stand-ups will still save you money and keep you focussed.</p>

<h3>How does it work?</h3>

<p>Daily standup-meetings are as simple as they sound.</p>

<p>You gather the team in the same spot each day, stand in a circle, and then everyone takes turns in answering three simple questions: What did I work on yesterday, what will I work on today, and is there is anything that is blocking me? Each person gets one minute on average (sometimes more, sometimes less), and overall it shouldn't take more than 10 minutes. That's it. In a way this is so old-fashioned that I wouldn't be surprised if ancient Greeks did this...</p>

<p>But beware! Daily standup-meetings are also a bit harder than they sound. After the "new" effect wears off, and before the long-term gains start to be fully understood, people might find it tempting to slack. So let's have a look at the long term benefits outside of having a nice team chat...</p>

<h3>How does it help?</h3>

<p>Introducing a quick daily standup-meeting is the cheapest most astonishing improvement I can think of for any project and one of the fundamental aspects of <a href="http://www.atlassian.com/agile/practices/communication.jsp">communication</a> with an agile team.</p>


<ul>
<li><strong>It helps your team avoid duplication:</strong> if Steve plans to work on a problem that was already solved two months ago by Sam, then you can bet that Sam (or someone else) will tell Steve before he even starts wasting time;</li>
<li><strong>Clear roadblocks:</strong> if someone is blocked by something particular, this is the perfect time to check who could help: "I have been working on bug X, with little progress. Has anyone seen X happening? Let's chat <em>after</em> the standup meeting";</li>
<li><strong>Detect roadblocks:</strong> if John tells the same story for the fifth time in a row, everyone else will realise that he is severely stuck and needs a hand. Catch him right after the meeting and get him some help;</li>
<li><strong>Boost team coherence and commitment:</strong> team coherence and commitment is increased when everyone speaks to everyone else on a regular basis. Especially when you don't all work in the same room, or when different members of the team work on different aspects of the same project;</li>
<li><strong>Increase project manager accountability:</strong> daily standup-meetings even make the project manager more accountable. When your manager comes to ask your status, would you dare ask him <em>his</em>? Probably not. But in a daily standup-meeting the manager is reporting his status to everyone else;</li>
<li><strong>Increase team member accountability:</strong> daily standup-meetings make each team member more accountable as well. A manager might not notice that Sal is over-engineering her solution just for the fun of it - but the rest of the team will;</li>
<li><strong>Spread transparency:</strong> if you are the project manager, then the project is probably transparent to you already. But there is immense value in team-wide transparency too. People are not machines, and love knowing what's happening. This is the place\!</li>
</ul>



<h3>What are the costs and risks?</h3>

<p>Admittedly, the daily standup-meeting does take time. And some people don't like speaking in front of others. And at times the meeting can distract people from doing important work. These are all valid points. But you need to compare them to the costs that arise by <em>not</em> having this meeting. How much work would be lost by double work, or by people not realising they are stuck? How important is staff retention to you - and what better ways of open communication can you get at a price tag this small? As long as you apply common sense and accept that some people <em>occasionally</em> may miss a meeting, and that others may not be great at speaking (but <em>do</em> attend and listen), there is little to worry about.</p>

<p>A bigger risk are people who like to hear themselves talking. The stand-up meeting is not for story-telling. You need to be strict about the time-limit. If someone exceeds the standard 60 seconds, usually a smile combined with the "Time-out sign" will help. A common problem is that the boss feels inclined to turn his 60 seconds of fame into a 5 minute speech, to ensure he looks more important than everyone else. Someone needs to have the guts and speak up - in a friendly way, and depending on the kind of boss it may make sense to do this after the meeting, to come across less confronting. The same applies to discussions. Stand-up meetings are <em>not</em> for discussions. They should <em>encourage</em> discussions, but after the standup-meeting, and with just the people who really care. If someone frequently starts discussing what someone else said, take them aside and explain that the discussion is great, but it needs to happen after the meeting.</p>

<p>It will help to nominate one person to become the 'Standup-Champion' who will make sure that the meeting remains focused and finishes quickly and on-time. This person also makes sure the meeting happens in the first place, so the person should not be too shy to shout out "It is <span class="caps">STANDUP </span>time now, guys and girls!")</p>

<h3>So what's agile about it?</h3>

<p>You can start stand-ups without turning agile. But the impact is bigger than "just" efficiency, transparency and team building. The standup-meeting quickly turns into the catalyst meeting of the day. It regularly spawns discussions about things that were not considered in the "big plan". It helps you address problems earlier. It helps you reshuffle your road-map because you learn about good and bad news early. It encourages people to wrap up tasks more frequently, and to tackle tasks in smaller increments, because no one likes to tell (or hear) the same report every day. Standup-meetings don't <em>make</em> you agile, but it would be harder to become agile if you didn't have them. So they are a great first step on your long winding road to agility.</p>

<h3><span class="caps">Q&amp;A</span></h3>

<p><strong>When is a good time to get started?</strong></p>

<p>As with any change, you need to be careful. People are often hesitant about change, especially if they have been subjected to frequent random change before that didn't help. So don't introduce this when the tension is high already, or when you are new and don't have any credibility yet. In software development terms, how about waiting until the current version of your software has been shipped, and everyone had a great time at the post-launch party. As with every change, first make the proposal, discuss it with the team and with relevant stakeholders, and take on their feedback before you actually get started. You may even want to try it out with a subset of people who are more keen on trying new things out. Just make sure to keep everyone in the informed.</p>

<p><strong>Is it necessary</strong> <strong><em>all</em></strong> <strong>the time? Can we just have standup-meetings in times of crisis?</strong></p>

<p>Standup-meetings are awesome during times of stress and crisis. They keep you focused and help avoid unnecessary work. But ask yourself: How can you avoid stress and crisis in the first place? How about avoiding straying, and how about avoiding unnecessary work in the first place? Standup-meetings are no silver bullets, but they can help avert crisis by catching problems early.</p>

<p><strong>How about just doing it once a week then? That should save us some time?</strong></p>

<p>The opposite will be true: People will talk for longer. People will prepare more (if they only get to talk once a week, they will want to shine\!). There will be less focus on the week ahead. If someone did double work, it will be too late to save time. If someone was stuck, she will have been stuck for a while now. And so on. Weekly meetings also have their place, but they should be dealing with larger issues, like planning and discussing the work ahead, and they should only have the people involved who are actually needed for that discussion.</p>

<p><strong>Can we sit down? Standing is uncomfortable...</strong></p>

<p>You are missing the point. It <em>should</em> be uncomfortable! You have to keep the meeting short. If everyone sits in a comfy chair and sips on great coffee, what are the chances of the meeting being short? Don't even dare to lean on that table - <a href="http://www.youtube.com/watch?v=mh6pZQX22CQ"><span class="caps">HTFU</span></a> mate!</p>

<p><strong>Is my team big enough to justify this?</strong></p>

<p>At my previous job, I thought stand-ups would not be necessary for a team of just five. After all, we were sitting in the same room already, had plenty of discussions, and I talked to the individual developers quite frequently already. We started only when we ramped up to 8 and then 10 developers. Only when we did so, I realised how much I had missed what the original 5 developers had been doing (and what they had not been doing..). And of course the transparency improvement for my colleagues was even bigger than for me. Stand-up meetings even with just three people make sense, after all double work needs to be avoided all the time, and with just three people on the team you should be done in 3 to 5 minutes anyway.</p>

<p><strong>Is my team too big to justify the costs?</strong></p>

<p>If 16 people spend 15 minutes a day, that adds up to 4h per day, and a whopping 20h per week. This is of course still just 3% of each person's time, and a team will need to communicate anyway, so 3% is not too bad. But indeed, if your team is so large that people start complaining about the duration, or you see people dozing off, then you need to reconsider. On the Confluence team, we decided to split the big standup into two meetings when we reached 18 people. Our team consists of four sub-teams, and we do a rotation now. During one fortnight the sub-teams A and B meet at 9:45, and sub-teams <span class="caps">C&amp;D </span>meet at 10:00. The next fortnight sub-teams A and C meet at 9:45, and subteams B and D meet at 10:00. And so on. Obviously you will have a little less knowledge-sharing because some people will only meet every two weeks. But of course there are so many other ways to share information too. Wiki-pages, wiki-blog posts, wiki-discussions and (sometimes) good old email don't get replaced by stand-ups after all.</p>

<p><strong>When is a good meeting-time?</strong></p>

It really depends on your team. If you all start working at the same time in the morning, then that time is ideal. But if people trickle in between 8:00 AM and 10:30AM, then any time is a bad time, because it will distract the people who came in early, or exclude the people who come in late. You may want to consider a time just before everyone goes to lunch (this can actually help you get everyone to go for lunch at the same time). Or you just go with 10:30. Just make sure that you let the team decide, and make sure to review your decision frequently. Maybe 8:00AM seemed like a good idea at first, but once people realise that their transport is less reliable than they thought, you may want to reschedule. Tell everyone that the time will be reviewed after, say, 4 weeks.<br />
<br /><br />
<p align="center"><object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imxz8nxa1w%2F2%2Fconfig.xml&amp;autoplay=false" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imxz8nxa1w%2F2%2Fconfig.xml&amp;autoplay=false"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imxz8nxa1w%2F2%2Fconfig.xml&amp;autoplay=false" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/></object>
<br /><br />
<a href="http://www.atlassian.com/agile/practices/"><b>Learn about other agile practices</b></a><br />
</p>

<p><br /><br />
<strong>That was an interesting introduction - where can I learn more?</strong></p>

<p>Try <a href="http://www.martinfowler.com/articles/itsNotJustStandingUp.html">Martin Fowler</a> for plenty of stand-up patterns and anti-patterns. I also recommend seeing how Atlassian practices <a href="http://www.atlassian.com/agile">agile development</a>.</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=Yhe2n2qHQmw:pAKFGJyZv6w:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=Yhe2n2qHQmw:pAKFGJyZv6w:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=Yhe2n2qHQmw:pAKFGJyZv6w:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/Yhe2n2qHQmw" height="1" width="1"/>]]></content:encoded><description>Becoming agile. The first baby-step There are many ways to become agile. And many ways to fail horribly while trying! Purists say that you are not really agile until you do everything...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/getting_started_with_agile_daily_standup_meetings.html</feedburner:origLink></item><item><title>Agile Context Switching with "The Disturbed"</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/rwPySi94e_A/the_disturbed.html</link><category>Agile</category><category>Software engineering</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Charles Miller</dc:creator><pubDate>Mon, 15 Jun 2009 19:27:15 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2311</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder" alt="agile" /></a>Possibly the best way to cripple the productivity of any development team is to bombard them with distractions. This was best brought home to me by DeMarco and Lister's seminal book <a href="http://www.amazon.com/Peopleware-Productive-Projects-Teams-Second/dp/0932633439">Peopleware: Productive Projects and Teams</a>.</p>

<p>Programmers, the authors proposed, are at their most efficient when they reach a state of 'flow' in which they are able to concentrate fully on a single problem. Any developer will tell you how satisfying it is to be in this state, how productive it is... and how easy it is to be pulled out of it by the smallest interruption. What's worse, since it takes at least fifteen minutes to reach this level of concentration, any interruption no matter how small can cost you a quarter hour's productivity. A constant stream of small interruptions, say a phone near your desk that rings just twice an hour, can make a developer unproductive (and miserable) for most of the day.<sup>1</sup></p>

<p>Still, distractions are inevitable, and problems that need a developer to help solve them will come up every day. A production system may run into trouble, another developer may need help with a hairy problem, a member of another team may want to know how your product solves a particular problem, and so on. At Atlassian, we try to manage these interruptions in two ways:</p>

<h3>1. Asynchronous Communication</h3>

<p>Atlassian runs a <a href="http://www.igniterealtime.org/">Jabber server</a> for staff, and during the day all developers are logged on to instant messaging. The advantage of instant messaging over other forms of intra-office communication is that it is so easily ignored. A developer who doesn't mind being interrupted can monitor their messages and respond as they come in. A developer who is busy working on some tricky problem can minimise the window and respond to messages at his or her leisure.</p>

<p>For this reason, developers will often use IM to talk to a colleague, even when the other person is sitting only a few feet away.</p>

<p>The other form of asynchronous communication popular in Atlassian is blogging: writing a blog post on our internal <a href="http://www.atlassian.com/software/confluence">wiki</a> is a great way to get feedback from the rest of the company about an idea without walking around interrupting people to ask them their opinion.</p>

<h3>2. The Disturbed</h3>

<p>Asynchronous communication introduces its own inefficiencies. It turns out there are still enough problems that need to be solved <em>now</em>, not at the convenience of the developer, to constitute a steady stream of interruptions to the development teams. Our solution, pioneered by the <a href="http://www.atlassian.com/software/bamboo/">Bamboo</a> team, was to designate a single developer to be "The Disturbed"; taking a two week stint to act as a magnet for all the questions, requests and distractions that would otherwise be distributed across the whole team.</p>

<p>For Confluence, the name of the current Disturbed is prominently displayed on a whiteboard just outside the team's office space. The Bamboo Disturbed has a flag on his or her monitor.</p>

<p>The downside of this approach, of course, is that for two weeks the Disturbed gets very little work done on whatever feature track they are nominally committed to. On the other hand, because the Disturbed isn't <em>expecting</em> (or expected) to get much of that work done while being disturbed, it is far less frustrating for the developer and far more predictable in terms of scheduling and estimation for the team as a whole.</p>

<p><i>To learn more about how we develop software at, visit the <a href="http://www.atlassian.com/agile/">Agile at Atlassian</a> site.</i></p>


<br /><br />
<p align="center"><object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imklt4fw2r%2F2%2Fconfig.xml&amp;autoplay=false" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imklt4fw2r%2F2%2Fconfig.xml&amp;autoplay=false"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2Fl6ica3s712x1%2Fl6imklt4fw2r%2F2%2Fconfig.xml&amp;autoplay=false" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/></object></p>

<p><br /><br />
<sup>1</sup> DeMarco and Lister use this to argue that each developer should be given a private office with a door, a practice enthusiastically embraced by Microsoft in the 1980s and more recently by New York software firm <a href="http://www.fogcreek.com/">Fog Creek</a>. Atlassian remains defiantly open plan: an office layout that encourages collaboration and communication at the expense of pure efficiency.</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=rwPySi94e_A:_RoPBVHZ2Fc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=rwPySi94e_A:_RoPBVHZ2Fc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=rwPySi94e_A:_RoPBVHZ2Fc:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/rwPySi94e_A" height="1" width="1"/>]]></content:encoded><description>Still, distractions are inevitable, and problems that need a developer to help solve them will come up every day. A production system may run into trouble, another developer may need help with a hairy problem, a member of another team may want to know how your product solves a particular problem, and so on. At Atlassian, we try to manage these interruptions in two ways.</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/the_disturbed.html</feedburner:origLink></item><item><title>Atlassian Agile Process Revisited</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/w0wRiH7Qoj4/atlassian_agile_process_revisi.html</link><category>Agile</category><category>Bamboo</category><category>Crucible</category><category>JIRA</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Chris Mountford</dc:creator><pubDate>Wed, 10 Jun 2009 11:04:42 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2297</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" align="right" class="noborder"></a><br />
In 2007 I did a series of surprisingly popular blog posts on <a href="http://blogs.atlassian.com/developer/2007/09/atlassian_agile_process_part_1.html">Atlassian's Agile Process</a>. As part of our renewed push to share our own <a href="http://www.atlassian.com/agile">agile story</a>, I wanted to post  an update to my earlier posts. Last time I enumerated the XP practices and mapped them to Atlassian's. I won't be repeating myself (<a href="http://c2.com/cgi/wiki?DontRepeatYourself">D.R.Y.</a>), so if you're interested in the origins of Atlassian's Agile Process, here are parts <a href="http://blogs.atlassian.com/developer/2007/09/atlassian_agile_process_part_1.html">one</a>, <a href="http://blogs.atlassian.com/developer/2007/10/atlassian_agile_process_part_2.html">two</a>, <a href="http://blogs.atlassian.com/developer/2007/10/atlassian_agile_process_part_3.html">three</a> and <a href="http://blogs.atlassian.com/developer/2007/11/atlassian_agile_process_part_4.html">four</a>.</p>

<p>Today in the Engineering division, we still have an agile process in each of the development teams defined for and by that team, that is as true now as it was in 2007.</p>

<p>The things that have changed are mostly in the category of <em>second order effects</em>.</p>

<p><big>Proliferation</big></p>

<p>The biggest difference in Atlassian's agile process in the last two years is that now it's not just a process for engineering work. Every department is getting agility. Top books on Agile practices are handed around, stacked available on common bookshelves and regularly seen on the tops of busy desks, scruffy yellow sticky notes protruding.</p>

<p>And now I'm even beginning to think that people are reading them.</p>

<p>Another sign of mass agile process adoption was about a year ago when middle management (which was at that time brand-new at Atlassian) started doing stand-ups. After that it seemed as though a critical mass had been reached and the practice of stand-up meetings became ubiquitous. It seems there is a culture of minimalism in meetings, not just at Atlassian, but amongst all smart people who want to get things done.</p>

<p>It's not just <a href="http://www.atlassian.com/agile/practices/communication.jsp">Stand-up Meetings</a> either. <a href="http://en.wikipedia.org/wiki/User_story">User stories</a>, <a href="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It">You Aren't Going to Need It</a>, <a href="http://c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.html">Do The Simplest Thing That Could Possibly Work</a>, <a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">Don't Repeat Yourself</a> as well as some of the foundation thinking such as <a href="http://en.wikipedia.org/wiki/Kaizen">kaizen</a> are all now part of the Atlassian lexicon. </p>

<p>I attribute this adoption to three things:</p>

<p>1. People have seen it work.<br />
2. Leaders like it.<br />
3. This stuff isn't new-fangled, it fits in with much established wisdom.<br />
<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="agilebooks.jpg" src="http://blogs.atlassian.com/developer/agilebooks.jpg" width="500" height="375" class="mt-image-right" style="float: right; margin: 0 0 20px 20px;" /></span><br />
<big>Scaling</big></p>

<p>Atlassian has grown quickly over its six year history, both in staff and in the number of offices. Processes have had to scale and over long distances and time zones. A good example of the growing pains is JIRA's automated test suite. <a href="http://web.mac.com/carlfishy/iWeb/TUAW/Blog/6F6C594B-BC75-4CE6-B1FB-B8D936D77A79.html">It's an awesome beast.</a> There are a growing number of unit tests (<a href="http://www.junit.org/">junit</a>), functional tests (<a href="http://jwebunit.sourceforge.net/">jwebunit</a>) , web browser driver tests (<a href="http://seleniumhq.org/">selenium</a>) and there is now a QA department who reviews and improves all aspects of our quality assurance regime which includes testing.</p>

<p>JIRA supports multiple operating systems, JDKs, application servers, databases, and versions thereof. And it comes in three editions (Standard, Professional and Enterprise) with different feature sets. Each edition comes in different distributions including EAR/WAR, Standalone which includes Tomcat as an application server and there is a Windows installer executable. As if that wasn't enough, it works in multiple versions of different web browsers.</p>

<p>So that's a lot of combinations to test! We really wish we could automatically test every combination! Currently our full functional test suite takes more than two hours to run. Multiply that by every combination and it would probably take several days of CPU time to complete. Of course Unit Tests run comparatively quickly and their green bar gives fairly good confidence, but if we want to be sure we haven't broken something in the JIRA user interface when we refactor the code, we have to wait at least two hours. </p>

<p>Running many thousands of functional tests is what is known as an <a href="http://en.wikipedia.org/wiki/Embarrassingly_parallel">embarrasingly parallel workload</a> and accordingly, I am embarrassed to say we have not completed the work to properly "parallelify" it. </p>

<p>But now that <a href="http://www.atlassian.com/software/bamboo/features/elastic.jsp">Bamboo supports agents running in the EC2 cloud</a>, we can crank up the compute resources available for <a href="http://martinfowler.com/articles/continuousIntegration.html">continuous integration</a> and we should be able to bring our functional test latency down under 20 mins without too much trouble. Fingers crossed.</p>

<p><big>Code Review</big></p>

<p>"<a href="http://www.atlassian.com/agile/practices/code-quality.jsp">Pair programming is the ultimate code review</a>" goes the "agilist" line. I can see this. I can see how it works and I have felt code-review benefits from pairing. I'll be blogging about pair programming specifically in the coming weeks, so I won't exhaust this side of the argument here, however I have to say that code review is the single most effective quality assurance measure on top of a traditional agile process that I know.</p>

<p>There are many advantages of <a href="http://www.atlassian.com/agile/practices/code-quality.jsp">code review</a> over pairing as well. Of course, these are not reasons to abandon pairing, because pairing provides much more than code review. If you are using a web-based code review tool like, ahem, oh look here, it seems we have a product I can pimp here... <a href="http://www.atlassian.com/software/crucible/">Crucible</a>... the main advantages are the most obvious ones. You can scale up the number of reviewers and they can act from different locations and time zones. You also get a permanent record.</p>

<p>The JIRA team is probably an average adopter of code review as a practice where it remains a discretionary thing. This suits us because it lets us always use the Agile advised dosage: Just Enough.</p>

<p><big>Virtual Index Cards with GreenHopper</big></p>

<p>While we really like the simplicity of paper cards for tracking tasks in the small, the electronic version can be better in some respects. Not only for distributed teams. Of course we use JIRA for tracking large numbers of bugs and feature requests, but it was not until the <a href="http://www.atlassian.com/greenhopper/">GreenHopper</a> plug-in that we have felt that JIRA was fully capable of giving us the immediate feedback and visibility that cards can give a co-located team. It also makes <a href="http://www.atlassian.com/greenhopper/features/chart-board.jsp">burndown charts</a> which are increasingly to be found on big LCD screens around the offices.</p>

<p>So that's it. We like our agile process. It suits Atlassian.</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=w0wRiH7Qoj4:LcbrBhFCpho:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=w0wRiH7Qoj4:LcbrBhFCpho:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=w0wRiH7Qoj4:LcbrBhFCpho:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/w0wRiH7Qoj4" height="1" width="1"/>]]></content:encoded><description>This is an update to a series on Atlassian's Agile Process I did back in 2007.</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/atlassian_agile_process_revisi.html</feedburner:origLink></item><item><title>Selenium Testing with Windows Integrated Auth</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/TIkN2_vlMMs/selenium_testing_with_windows.html</link><category>Software engineering</category><category>User Interface</category><category>Web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Jonathan Gilbert</dc:creator><pubDate>Wed, 10 Jun 2009 18:29:25 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2410</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p></p>

<p>Automated web testing generally has problems testing sites using Windows Integrated Authentication. This is because the nature of integrated auth is to supply the credentials of the logged-on user - which in the general case is going to be the user running the tests. This may work in some cases, but at the very least it's likely you want to test using both an admin and non-admin user. This can be done with Selenium, but requires some work to get it set up (I'm assuming we're using Selenium Server here).</p>

<p>The goal here is to get the browser running as a specific user (I'm not aware of a way to make the browser authenticate using other credentials). The problem is that your browser is spawned by the Selenium Server, so naturally inherits the logon credentials which started the server. Therefore there are two possible paths to take here - fool Selenium into starting the browser under another user or start Selenium itself under another user. I picked the latter option, although I expect it would be possible to do the former (possibly by replacing the browser executable with a custom script before each test?).</p>

<p>Selenium is fairly quick to start up - certainly in comparison to the speed at which it executes tests, so can be started at the beginning of each test without too much impact on overall testing time. (It would also be possible to maintain state and only respawn if the user changes - but this may not be simple to close on the final test - is there a hook for this?) For our testing (which tests using integrated auth), all Selenium tests create their own Selenium Server instance in a SetUp() method from a base test class (note that the tests are C#/NUnit), and close it in the TearDown() method. The C# code is below which uses the .NET API to start the process under another user - this would be marginally more complex in Java, but could be achieved by invoking the <em>runas</em> command to spawn Selenium.<br />
<p/><br />
<div style="border: 1px dotted blue;background-color: #F6F6F6"><br />
</p><pre><span class="category1">public</span> <span class="category1">class</span> SeleniumContainer
{
 <span class="category1">private</span> readonly <span class="category2">Process</span> process = <span class="category1">new</span> <span class="category2">Process</span>();
 <span class="category1">private</span> bool started;
 
 <span class="category1">public</span> SeleniumContainer(bool asAdmin)
 {
  process.StartInfo.FileName = "<span class="quote">java</span>";
  process.StartInfo.Arguments = "<span class="quote">-jar lib/selenium-server.jar</span>";
  process.StartInfo.CreateNoWindow = <span class="category1">false</span>;
  process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
  process.StartInfo.UseShellExecute = <span class="category1">false</span>;
  string[] domainAndUser = Config[asAdmin ? ConfigProps.AdminUser : ConfigProps.NonAdminUser].Split('\\');
  string password = Config[asAdmin ? ConfigProps.AdminPassword : ConfigProps.NonAdminPassword];
  
  process.StartInfo.UserName = domainAndUser[1];
  
  SecureString pw = <span class="category1">new</span> SecureString();
  
  foreach (<span class="category1">char</span> c in password)
  pw.AppendChar(c);
  
  process.StartInfo.Password = pw;
  
  DirectoryInfo currentDir = <span class="category1">new</span> DirectoryInfo(Environment.CurrentDirectory);
  
  process.StartInfo.WorkingDirectory = currentDir.Parent.Parent.Parent.Parent.FullName;
  
  <span class="category1">if</span> (Config.Exists(ConfigProps.SeleniumFirefoxProfile))
  {
   process.StartInfo.Arguments += ("<span class="quote"> -firefoxProfileTemplate </span>" + Config[ConfigProps.SeleniumFirefoxProfile]);
  }
 }
 
 <span class="category1">public</span> <span class="category1">void</span> Start()
 {
  lock (<span class="category1">this</span>)
  {
   <span class="category1">if</span> (started)
   <span class="category1">throw</span> <span class="category1">new</span> InvalidOperationException("<span class="quote">Selenium already started</span>");
   <span class="category1">if</span> (!process.Start())
   <span class="category1">throw</span> <span class="category1">new</span> <span class="category2">Exception</span>("<span class="quote">Failed to start Selenium process</span>");
   
   started = <span class="category1">true</span>;
  }
  
  <span class="linecomment">//give it half a second to start up</span>
  Thread.Sleep(500);
 }
 
 <span class="category1">public</span> <span class="category1">void</span> Stop()
 {
  lock (<span class="category1">this</span>)
  {
   <span class="category1">if</span> (!started)
   <span class="category1">throw</span> <span class="category1">new</span> InvalidOperationException("<span class="quote">Selenium not running</span>");
   
   <span class="category1">try</span>
   {
    process.CloseMainWindow();
   }
   <span class="category1">catch</span>
   {
    <span class="linecomment">//Ignore</span>
   }
   
   <span class="linecomment">//give Selenium 5sec to close before we kill it</span>
   <span class="category1">if</span> (!process.WaitForExit(5000))
   process.Kill();
   
   started = <span class="category1">false</span>;
  }
 }
}</pre><p><br />
</div><br />
<p/><br />
<big><big>Testing using Firefox</big></big></p>

<p>Firefox in general does not send integrated auth credentials, but can be told to. To do this go to the firefox about:config screen and search for <em>ntlm</em> - there should be three options, two of which need changing.<br />
<strong>network.ntlm.send-lm-response</strong>: enable this (set to <em>true</em>)<br />
<strong>network.automatic-ntlm-auth.trusted-uris</strong>: set this to a string representing your host (it's a URL pattern)<br />
Once this is working for firefox you will need to save the profile directory and pass it to Selenium when it starts so that it can use your modified firefox profile (by default it uses it's own). This is done using the <em>-firefoxProfileTemplate</em> parameter, as you can see in the above code. Oh, and you'll need at least version 1.0-beta-2 of Selenium if you want to use firefox 3.<br />
</p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=TIkN2_vlMMs:hySUNtEZ8kA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=TIkN2_vlMMs:hySUNtEZ8kA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=TIkN2_vlMMs:hySUNtEZ8kA:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/TIkN2_vlMMs" height="1" width="1"/>]]></content:encoded><description>Automated web testing generally has problems testing sites using Windows Integrated Authentication. This is because the nature of integrated auth is to supply the credentials of the logged-on user - which in the general case is going to be the user running the tests. This may work in some cases, but at the very least it's likely you want to test using both an admin and non-admin user. This can be done with Selenium, but requires some work to get it set up (I'm assuming we're using Selenium Server here).</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/selenium_testing_with_windows.html</feedburner:origLink></item><item><title>Atlassian does the Agile Enterprise Acid Test</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/JY2Oq50LhRg/atlassian_does_the_agile_enter.html</link><category>Agile</category><category>Software engineering</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Melanie Carasso</dc:creator><pubDate>Tue, 09 Jun 2009 17:28:53 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2252</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><a href="http://www.atlassian.com/agile/"><img src="http://blogs.atlassian.com/news/agile_development_blog_badge-thumb-185x99.png" alt="Agile development at Atlassian" align="right" class="noborder"></a>As you may know, we are getting a little more serious about sharing our <a href="http://www.atlassian.com/agile/">agile development</a> stories with the world. Well, a little while back, at our Dev team leads' weekly Agile Book Club meeting, we decided to assess ourselves against Dean Leffingwell's <a href="http://scalingsoftwareagility.wordpress.com/2008/10/13/the-agile-enterprise-acid-test-updated/">Agile Enterprise Acid Test</a>. The test consists of 9 questions, grouped into 3 elements - see the full definitions below.</p>

<p>The purpose of the discussion was not to compare teams; it was a subjective self-assessment of the different products' development processes, to help identify areas for improvement within teams.  We also discussed some key characteristics of each team, like team size, codebase age, and something we politely refer to as "Founder involvement", to see if there was any correlation with Acid Test results (there wasn't, so I've left these out).  </p>

<h2>The results</h2>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="ATL Acid Test Dec08.png" src="http://blogs.atlassian.com/developer/ATL%20Acid%20Test%20Dec08.png" width="792" height="396" class="mt-image-none" style="" /></span></p>

<h2>The discussion</h2>
<ul>
	<li>The Confluence team has monthly process meetings which provide a means of "empowerment" and allows them to "reflect & adapt".  Issues from the retrospectives are voted on by the team, and high-ranking issues are added to the project backlog.  Per (<a href="http://www.atlassian.com/agile/people/teamlead.jsp">team lead</a>) "smoke tests" the user stories from Adnan (product manager) for completeness and clarity, before passing them on to the developers.</li>
	<li>The JIRA team conducts laptop demos for founders at monthly reviews.  Feedback is not as continuous as other teams - milestones are less frequent but release cycles are longer.  Process champions and tech leads are in place.</li>
	<li>The Studio team has not been tested on "accountable for results", as they have not stuffed up a release (yet). The team hasn't asked for many process changes after "reflecting" (apart from requesting the use of real hours for estimating, rather than story points).</li>
	<li>Generally the team leads felt frustrated at their ability to remove all impediments, particularly IT infrastructure issues. Luckily we now have an unsuspecting new IT Manager on board to help out with these.</li>
	<li>The FishEye/Crucible and Clover teams have had slippages.  They are starting to reflect and adapt.</li>
	<li>It's difficult to update our internal Crowd instance for dogfooding without disrupting IT.  Need to work on a better system for this.</li>
	<li>The Poland team holds structured retrospectives and follows up.  They work well around the remote location issues.</li>
	<li>UI Design - need to feed this into our agile processes more effectively, and in parallel with the Product Managers' conceptual phase for the next release.</li>
	<li>Need to include the San Francisco team next time (they were busy actually working when we did this test).</li>
</ul>

<h2><a href="http://scalingsoftwareagility.wordpress.com/2008/10/13/the-agile-enterprise-acid-test-updated">Dean's definitions</a></h2>

<p><strong>Variable scope. Fixed quality.</strong><br />
1. Can the teams vary scope? - Does the team have the authority to vary scope even as the release deadline draws closer?<br />
2. Is quality acceptable and fixed? - You can't go fast building on crappy code. Agile accomplishes little without the requisite code quality which must be built into the process through TTD, continuous integration, test automation, coding standards and the like. If you see your teams iterating or sprinting with a primary objective of working down code-level defects, then you are not truly agile.</p>

<p><strong>Incremental Value Delivery</strong><br />
3. Is software delivered incrementally? - If your teams are sprinting but there is no feedback until the final delivery (one form of "waterscrumming") then you are not achieving agility as there is no meaningful feedback to drive the solution to an acceptable outcome<br />
4. Is it valued and evaluated? - Demos are great, but you need real value delivery to assure fitness for intended use and early and continuous ROI. If the incremental code is not being actually used, you are not very likely to get the results you seek.<br />
5. Is feedback continuous? - In addition to customer feedback, product owners, product managers and other stakeholders have a responsibility to continually assess the current result. This is achieved through story-level acceptance and iteration-level demos.</p>

<p><strong>Empowerment and Accountability</strong><br />
6. Are the teams empowered? - Are the teams truly empowered and able to modify and improve their own software processes? Do they self organize and self-manage? Are resources routinely moved to the most critical bottlenecks?<br />
7. Are they accountable for results? Empowerment and accountability are two sides of the same agile coin. Are the teams delivering reliable quality code in incremental fashion? Do you they commit to iteration and release objectives, subject to responsible scope management?<br />
8. Do they regularly reflect and adapt? - Do the teams adhere to Agile Manifesto Principle #12? - At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly.<br />
9. Does management eliminate impediments? - Is management also engaged in continuous improvement? Are impediments routinely surfaced, addressed and eliminated.</p>

<h2>The follow-up</h2>
We came up with a few actions, including more dogfooding of pre-release milestones, and following up more on the issues identified in team retrospectives.  In a couple of months we'll be conducting the Agile Enterprise Acid Test again, to see how we're progressing.  This time we'll survey developers, product managers, and the Founders (in addition to the team leads), just to check we're not making stuff up :-).
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=JY2Oq50LhRg:jdkeCCjXf28:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=JY2Oq50LhRg:jdkeCCjXf28:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=JY2Oq50LhRg:jdkeCCjXf28:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/JY2Oq50LhRg" height="1" width="1"/>]]></content:encoded><description>How does Atlassian score on Dean Leffingwell's "Agile Enterprise Acid Test"?</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/atlassian_does_the_agile_enter.html</feedburner:origLink></item><item><title>How Hamcrest can save your soul</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/QGLs6it_emc/how_hamcrest_can_save_your_sou.html</link><category>Java</category><category>Software engineering</category><category>programming tools</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Richard Wallace</dc:creator><pubDate>Fri, 05 Jun 2009 15:26:47 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2402</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p>I considered calling this post, "assertEquals considered harmful" but thought that it might be a bit too harsh.  After all, <tt>assertEquals</tt> is still good for asserting that the primitive result of a calculation is accurate.  But I will never again use it for complex objects and I hope to convince as many people as possible that there is a better way.</p>

<p>First off, let's start with some assumptions:</p>
<ol>
	<li>We want our tests to be clear to the reader and convey what we are trying to test without a lot of extra baggage.</li>

	<li>We want our test failures to be clear about what the problem was and not have to spend copious amounts of time figuring out what the problem in the result is.</li>
	<li>We want to follow the "One Assert Per Test" TDD rule - or get as close as we can to it.</li>
	<li>Implementing the <tt>equals</tt> method purely for comparisons in testing is a dubious, potentially wrong and harmful, practice.</li>
</ol>


<p>That last one probably merits a blog all of its own.  The problem is that for some objects, domain entity objects as an example, implementing <tt>equals</tt> and <tt>hashCode</tt> in terms of the complete state of the object is not usually what you want.  Often, for these types of objects, there is an identifier member such that, if 2 objects had the same value for the identifier member, you would consider them to represent the same entity.  So you need to think long and hard about why you're implementing <tt>equals</tt> and what 2 objects of that type being "equal" really means.</p>

<p>If all that seems a bit abstract for the end of the week after long week of Atlassian Summit and JavaOne, we can consider another case that I ran into recently -  the objects that you want to compare are of types from a 3rd party library and the developers didn't implement <tt>equals</tt>, <tt>hashCode</tt> or <tt>toString</tt>!  So, you can't use <tt>assertEquals</tt> directly and even if you could any output you get is completely useless!</p>

<h3><a name="HowHamcrestcansaveyoursoul-Thepainof%7B%7BassertEquals%7D%7D"></a>The pain of <tt>assertEquals</tt></h3>

<p>So, lets say we've got a type called Lightsaber that doesn't implement <tt>equals</tt>, <tt>hashCode</tt>, or <tt>toString</tt>.  How can we compare 2 instances to be sure they're the same?  The plain old JUnit with <tt>assertEquals</tt> way would be</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
assertEquals(thisLightsaber.isSingleBladed(), thatLightsaber.isSingleBladed());
assertEquals(thisLightsaber.getColor(), thatLightsaber.getColor());
assertEquals(thisLightsaber.getHilt(), thatLightsaber.getHilt());
</pre>
</div></div>

<p>That's all well and good, but there are a few problems.  If there is a failure, it's hard to tell exactly which statement failed.  In this trivial example it will be easier because each of the things we're comparing is of a different type.  But if we had multiple assertions on <tt>Color</tt> or <tt>boolean</tt> this wouldn't be so readily apparent.  Another problem is that the <tt>Hilt</tt> object returned from <tt>Lightsaber.getHilt</tt> is another object, and if it doesn't implement <tt>equals</tt> then our test is suddenly going to blow up as we need separate asserts for each of its attributes.  So we can do that, and let's say we even pull these asserts out of our test into a separate <tt>assertEquals(Lightsaber, Lightsaber)</tt> utility method so we can reuse it all.  We're still left with a problem: the tests only run until the first mismatched field.  Instead of showing us all the failures, we only see the first one that is detected which leads to a long test-fix-test-fix-test cycle.</p>

<h3><a name="HowHamcrestcansaveyoursoul-EnterHamcrest"></a>Enter Hamcrest</h3>
<p>So, what's a guy to do?  We've been using <a href="http://code.google.com/p/hamcrest/" rel="nofollow">Hamcrest</a> for a while on the SF team.  What first attracted me to Hamcrest is its really nice, fluent language for making assertions</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
assertThat(anakin, is(equalTo(vader));
assertThat(<span class="code-quote">"May the asserts be with you"</span>, containsString(<span class="code-quote">"<span class="code-keyword">assert</span>"</span>));
assertThat(weaponsOfGalacticDestruction, hasItems(deathStar, sunCrusher));

</pre>
</div></div>

<p>but what kept me coming back for more is the way you can define custom Matchers to do the comparisons.  Each of those statements above uses the same <tt>assertThat</tt> method, but a different <a href="http://code.google.com/p/hamcrest/source/browse/tags/hamcrest-java-1.2/hamcrest-core/src/main/java/org/hamcrest/Matcher.java" rel="nofollow"><tt>Matcher</tt></a> is used for the actual comparison.  There are all sorts of <a href="http://code.google.com/p/hamcrest/wiki/Tutorial#A_tour_of_common_matchers" rel="nofollow"><tt>Matchers</tt></a> built into the core library, but it is trivially easy to implement our own as well.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-comment">// factory methods <span class="code-keyword">for</span> fluent language
</span><span class="code-keyword">static</span> Matcher&lt;? <span class="code-keyword">super</span> Lightsaber&gt; equalTo(Lightsaber lightsaber) {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span> LightsaberIsEqual(lightsaber);
}


<span class="code-keyword">static</span> Matcher&lt;? <span class="code-keyword">super</span> Hilt&gt; equalTo(Hilt hilt) {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span> HiltIsEqual(hilt);
}

<span class="code-keyword">private</span> class LightsaberIsEqual <span class="code-keyword">extends</span> BaseMatcher&lt;Lightsaber&gt; {
    <span class="code-keyword">private</span> <span class="code-keyword">final</span> Lightsaber lightsaber;
    <span class="code-keyword">private</span> <span class="code-keyword">final</span> Matcher&lt;? <span class="code-keyword">super</span> Hilt&gt; hilt;

    LightsaberIsEqual(Lightsaber lightsaber) {
        <span class="code-keyword">this</span>.lightsaber = lightsaber;
        hilt = is(equalTo(lightsaber.getHilt()));
    }

    <span class="code-keyword">public</span> <span class="code-object">boolean</span> matches(<span class="code-object">Object</span> o) {
        Lightsaber l = (Lightsaber) o;
        <span class="code-keyword">return</span> lightsaber.isSingleBladed() == l.isSingleBladed()
            &amp;&amp; lightsaber.getColor().equals(l.getColor())
            &amp;&amp; hilt.matches(l.getHilt());
    }

    <span class="code-keyword">public</span> void describeTo(Description desc) {
        desc.appendText(<span class="code-quote">"{singleBladed is "</span>)
            .appendValue(lightsaber.isSingleBladed())
            .appendText(<span class="code-quote">", color is "</span>)
            .appendValue(lightsaber.getColor())
            .appendText(<span class="code-quote">", hilt "</span>)
            .appendDescriptionOf(hilt);
            .appendText(<span class="code-quote">"}"</span>);
    }
}


<span class="code-comment">// HiltIsEqual would look much the same, comparing the attributes in the matches() method and </span>
</pre>
</div></div>

<p>Now our assertions in our test look like</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
assertThat(anakinsLightsaber, is(equalTo(lukesFirstLightsaber));
</pre>
</div></div>

<p>If this were to fail the output would look like </p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>  Expected: is {singleBladed is true, color is GREEN, hilt is {...}}
  but: Lightsaber@35324
</pre>
</div></div>

<p>That's better, at least it shows us what we should have gotten.  But we're still not seeing what failures there were.</p>

<h3><a name="HowHamcrestcansaveyoursoul-Describingmismatches"></a>Describing mismatches</h3>
<p>If that's all there was to the story, it wouldn't be much of a story and wouldn't be that much more compelling than having an <tt>assertEquals(Lightsaber, Lightsaber)</tt> method.  But the story doesn't end there, it continues with the release of <a href="http://groups.google.com/group/hamcrest-java/browse_thread/thread/37dc31a74bc34992" rel="nofollow">Hamcrest 1.2</a>.  The most important new bit for us is "added mismatch reporting".  Using this we can take care of that second part of the output, with an added benefit.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">private</span> class LightsaberIsEqual <span class="code-keyword">extends</span> TypeSafeDiagnosingMatcher&lt;Lightsaber&gt; {
    <span class="code-keyword">private</span> <span class="code-keyword">final</span> Matcher&lt;? <span class="code-keyword">super</span> <span class="code-object">Boolean</span>&gt; singleBladed;
    <span class="code-keyword">private</span> <span class="code-keyword">final</span> Matcher&lt;? <span class="code-keyword">super</span> Color&gt; color;
    <span class="code-keyword">private</span> <span class="code-keyword">final</span> Matcher&lt;? <span class="code-keyword">super</span> Hilt&gt; hilt;

    LightsaberIsEqual(Lightsaber lightsaber) {
        singleBladed = is(equalTo(lightsaber.isSingleBladed()));
        color = is(equalTo(lightsaber.getColor()));
        hilt = is(equalTo(lightsaber.getHilt()));
    }

    <span class="code-keyword">public</span> <span class="code-object">boolean</span> matchesSafely(Lightsaber lightsaber, Description mismatchDescription)
        <span class="code-object">boolean</span> matches = <span class="code-keyword">true</span>;
        mismatchDescription.appendText(<span class="code-quote">"{"</span>);
        <span class="code-keyword">if</span> (!singleBladed.matches(lightsaber.isSingleBladed())) {
            reportMismatch(<span class="code-quote">"singleBladed"</span>, singleBladed, lightsaber.isSingleBladed(), mismatchDescription, matches);
            matches = <span class="code-keyword">false</span>;
        }
        <span class="code-keyword">if</span> (!color.matches(lightsaber.getColor())) {
            reportMismatch(<span class="code-quote">"color"</span>, color, lightsaber.getColor(), mismatchDescription, matches);
            matches = <span class="code-keyword">false</span>;
        }
        <span class="code-keyword">if</span> (!hilt.matches(lightsaber.getHilt())) {
            reportMismatch(<span class="code-quote">"hilt"</span>, hilt, lightsaber.getHilt(), mismatchDescription, matches);
            matches = <span class="code-keyword">false</span>;
        }
        mismatchDescription.appendText(<span class="code-quote">"}"</span>);
        <span class="code-keyword">return</span> matches;
    }

    <span class="code-keyword">public</span> void describeTo(Description desc) {
        desc.appendText(<span class="code-quote">"{singleBladed "</span>)
            .appendDescriptionOf(singleBladed)
            .appendText(<span class="code-quote">", color "</span>)
            .appendDescriptionOf(color)
            .appendText(<span class="code-quote">", hilt "</span>)
            .appendDescriptionOf(hilt);
            .appendText(<span class="code-quote">"}"</span>);
    }
}
    

<span class="code-keyword">static</span> void reportMismatch(<span class="code-object">String</span> name, Matcher&lt;?&gt; matcher, <span class="code-object">Object</span> item, Description mismatchDescription, <span class="code-object">boolean</span> firstMismatch)
{
    <span class="code-keyword">if</span> (!firstMismatch)
    {
        mismatchDescription.appendText(<span class="code-quote">", "</span>);
    }
    mismatchDescription.appendText(name).appendText(<span class="code-quote">" "</span>);
    matcher.describeMismatch(item, mismatchDescription);
}

</pre>
</div></div>

<p>Now, let's say I try and do this assertion</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
assertThat(lukesFirstLightsaber, is(equalTo(maceWindusLightsaber)));
</pre>
</div></div>

<p>That should clearly fail, and when it does we get the following output (assuming the hilts are the same)</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>  Expected: is {singleBladed is true, color is PURPLE, hilt is {...}}
  but: is {color is GREEN}

</pre>
</div></div>

<p>But wait, what's this?&#33;? In the output it only shows us the color of Lukes first lightsaber.  Is that a bug?  Lets see, they're both single-bladed, we assumed the hilts were the same, but we know Mace Windu had a purple blade and Lukes had a green one.  Why, it's only showing us the field that didn't match!  How cool is that?&#33;?  For this example it would've been pretty trivial to determine where the mismatch was, but suppose we didn't make the assumption that the hilts were the same.  Let's say they're the same, except for the size.  Then the output would have been</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>  Expected: is {singleBladed is true, color is PURPLE, hilt is {size is LARGE, ...}}
  but: is {color is GREEN, hilt is {size is MEDIUM}}
</pre>
</div></div>

<p>Even with composite objects we can see at a glance what the differences are!</p>

<p>A question you are probably asking is whether this extra effort is really worth it.  After all, this is Java and there is a bunch of boiler plate that wouldn't be necessary if we went the simple route of the <tt>assertEquals(Lightsaber, Lightsaber)</tt>.  I can only speak from my personal experience, but doing it this way has vastly improved my ability to write new test and functionality for the OAuth plugins that I have been working on.  It was a major pain to go through and write all the matchers for our own OAuth objects and the OAuth.net objects, but it was a write them once and enjoy the benefits for a long time to come.  Every new test I write where I see the abbreviated output that tells me the exact fields that were wrong makes me thankful I put the extra effort in because it saves at least 5-10 minutes everytime there is a test failure.  And if it helps me, right now, as I'm writing the tests, I feel confident that it will be a tremendous help to whoever might pick up the code after me and be expected to make changes and add new stuff.  Because I know from experience what a pain it is to make a change, run the tests and see</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
<pre>  Expected: &lt;BIG GLOB OF OBJECT AND FIELD ATTRIBUTES GENERATED WITH ToStringBuilder&gt;
  but was: &lt;OTHER BIG GLOB OF OBJECT AND FIELD ATTRIBUTES WITH MINOR DIFFERENCES GENERATED WITH ToStringBuilder&gt;
</pre>
</div></div>

<p>What I would typically wind up doing was copying and pasting to a text editor and lining everything up and trying to scan for what changed.  But no more!  Now my unit tests actually tell me what is wrong!</p>

<p>There's another huge benefit of creating these equal matchers, you can use these them with Mockito using <a href="http://mockito.googlecode.com/svn/branches/1.7/javadoc/org/mockito/Matchers.html#argThat(org.hamcrest.Matcher)" rel="nofollow"><tt>argThat</tt></a>, try that with <tt>assertEquals(Lightsaber, Lightsaber)</tt>!</p>

<p>Additionally, after showing this to some of my colleagues here at Atlassian, it was suggested that I should be able to write a <tt>Matcher</tt> that does a deep comparison of two objects of the same type using reflection.  It's a good idea and is something that I am working on in my 20% time.  I don't expect it to be something that will take away the need to write some custom equal <tt>Matcher</tt>s, but if I can get it to a point where it works in 80% of the cases, then I'll be very happy as much of the above boiler plate can be eliminated.  More on that to come in the future</p>

        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=QGLs6it_emc:9jXy6zVeOSE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=QGLs6it_emc:9jXy6zVeOSE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=QGLs6it_emc:9jXy6zVeOSE:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/QGLs6it_emc" height="1" width="1"/>]]></content:encoded><description>I considered calling this post, "assertEquals considered harmful" but thought that it might be a bit too harsh. After all, assertEquals is still good for asserting that the primitive result of a...</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/06/how_hamcrest_can_save_your_sou.html</feedburner:origLink></item><item><title>Introducing the Atlassian Plugin Exchange</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/RviqVARFI3Q/introducing_the_atlassian_plug.html</link><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Michael Knighten</dc:creator><pubDate>Tue, 26 May 2009 18:26:47 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2388</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p>I'm excited today to announce the availability of Atlassian's newest website, the <a href="http://plugins.atlassian.com" rel="nofollow">Atlassian Plugin Exchange</a>. The Plugin Exchange provides an easy way to find, download, and review plugins for all Atlassian products.</p>

<p>The <b>Plugin Exchange</b> was built out of a need to consolidate and scale our burgeoning plugin libraries. Over the last couple of years, interest in developing and using plugins with Atlassian's products, particularly Confluence and JIRA, has skyrocketed. But we've started to become a victim of our own success: between our plugin spaces at confluence.atlassian.com, our public subversion, partner sites, etc., its become difficult to reliably find and evaluate new plugins.</p>

<p>Our goals then with the Plugin Exchange have been two-fold: one, make it easier for users to find and install plugins, and two, make it easier for developers to add new plugins. Towards the first goal, we have built a new application from scratch (using Grails) to warehouse all the plugins and make them discoverable by product, by category, by search, by popularity, or any combination of these. Towards the second goal, we have made the process of adding a plugin as simple as filling in a form with information about your plugin, including links to download jar, issue tracker, etc., adding version info, screenshots, etc. and submitting to Atlassian for approval. No more maintaining Confluence pages, editing XML files in your source, etc., we've made the process of adding and maintaining a plugin as easy as possible.</p>

<p>But it doesn't end with the Plugin Exchange. Having a site that makes it easy for you to find and add new plugins is great, but what we really wanted was a way to get at this information from the product itself.  To that end, we've built a new plugin manager, dubbed the <b>Plugin Exchange Manager</b>, to handle installation and management of plugins in our products. We've built this using Atlassian's <a href="http://blogs.atlassian.com/developer/2008/08/plugins_20_a_headsup.html" rel="nofollow">Plugins 2.0</a>, meaning that it will work in any product that supports Plugins 2, such as Confluence and JIRA. For a sneak preview, check out the video below. The Plugin Exchange Manager isn't ready for release quite yet, but we'll get an alpha release out there as soon as we can.</p>

<p>Please head over to the <a href="http://plugins.atlassian.com" rel="nofollow">Plugin Exchange</a> and have a look. Better yet, find a new plugin, check it out, and add a review. For more about the Exchange, as well as the plugin manager, please check out the video below.</p>

<p>If you're a plugin developer and have any questions, please contact developer-relations@atlassian.com.</p>


<object id="ep_player" name="ep_player" height="391" width="640" data="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2F13%2Fm3e0owspwavc%2F2%2Fconfig.xml" type="application/x-shockwave-flash"><param name="movie" value="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2F13%2Fm3e0owspwavc%2F2%2Fconfig.xml"/><param name="AllowScriptAccess" value="always"/><param name="allowfullscreen" value="true"/><embed src="http://cdn.episodic.com/player/EpisodicPlayer.swf?config=http%3A%2F%2Fcdn.episodic.com%2Fshows%2F13%2Fm3e0owspwavc%2F2%2Fconfig.xml" type="application/x-shockwave-flash" allowfullscreen="true" AllowScriptAccess="always" width="640" height="391" id="ep_player" name="ep_player"/><a href="http://analytics.episodic.com/download/em3e0owspwavc/f20/atlassian-plugin-exchange.mp4"><img src="http://cdn.episodic.com/shows/assets/2/a6009.jpg" height="360" width="640" border="0"/></a></object>


        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=RviqVARFI3Q:jMeMjX4fKrw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=RviqVARFI3Q:jMeMjX4fKrw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=RviqVARFI3Q:jMeMjX4fKrw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/RviqVARFI3Q" height="1" width="1"/>]]></content:encoded><description>I'm excited today to announce the availability of Atlassian's newest website, the Atlassian Plugin Exchange. The Plugin Exchange provides an easy way to find, download, and review plugins for all Atlassian products....</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/05/introducing_the_atlassian_plug.html</feedburner:origLink></item><item><title>40 to 7: How We Sped Up Confluence's Build by a Factor of 5</title><link>http://feedproxy.google.com/~r/AtlassianDeveloperBlog/~3/2GO21O-FE60/40_7_how_we_sped_up_confluences_build_by_5.html</link><category>Agile</category><category>Bamboo</category><category>Clover</category><category>programming tools</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Nick Pellow</dc:creator><pubDate>Tue, 26 May 2009 17:55:57 PDT</pubDate><guid isPermaLink="false">tag:blogs.atlassian.com,2009:/developer//14.2385</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[
        <p><br />
Don't you hate committing code and then waiting hours to find out you broke the build? Even worse is when other people commit code at a similar time to you, and you get dragged into the 'who broke the build' witch-hunt by pure circumstance.</p>

<p>If your build times are blowing out because of long test runs (greater than ten minutes), then you are most likely suffering from CI (Continuous Integration) latency and the above problems are real problems for you and your team.</p>

<p>Clover can help alleviate theses problems, by optimizing both unit and acceptance tests, drastically reducing the feedback time for each commit. Below is a case study of how Clover's Test Optimization is run on the Confluence project.</p>

<h1> Serious CI</h1>

<p>With about 55 different CI Plans setup in Bamboo, the Confluence team are very serious about CI. So serious in fact, that if each build were to run end-to-end, a single commit would take over two days to be tested by each of those plans. Fortunately, Bamboo provides a pretty impressive CI-Cloud, that entails 20 different agents that run build plans in parallel. This makes it possible to get feedback in a couple of hours, as opposed to days.</p>

<p>Often, those few hours can mean the difference between one changeset being included in the build, or many. The main build can run for up to 40 minutes before a failure is detected. In that time, possibly multiple other commits have been made, making it more difficult to track down the root cause of the failure. 40 minutes is also long enough for a developer to be tempted by the ultimate SCM sin; commit-and-run.</p>

<p>For the past month, the Clover team have run a shadow plan of the Confluence trunk build which only runs the acceptance tests that cover code which was modified since the previous build. This is made possible by Clover's per-test coverage data, that reports which tests hit which lines of code during a test run.</p>

<h2> Results</h2>

<p>The optimized build (charted on the left) is configured to do a complete test run every 10 builds to refresh the per-test coverage data.</p>

<table><tr>
<td>
<img alt="optimized-build-results-1.png" src="http://blogs.atlassian.com/developer/clover/optimized-build-results-1.png" width="300px"  class="mt-image-none" style="" />
</td><td>
<img alt="main-build-results.png" src="http://blogs.atlassian.com/developer/clover/main-build-results.png" width="300px" class="mt-image-none" style="" />
</td></tr></table>

<p>The optimized build provides faster feedback on average compared with the main build, and because it completes on average a lot faster than the main build, more builds get run in the same amount of time.</p>

<h2> Faster Feedback</h2>

<p>A specific case of where the Clover Optimized build failed before the full Confluence build can be seen in CCD-CONFDF-164, where it took 7 minutes to detect the Acceptance test failure, as opposed to 38 minutes in CONFFUNC-MAIN-5247. This was one case where the changeset that triggered the Optimized build was identical to the changeset that triggered the main build. Quite often, the Optimized build was started on another agent while the main build was still churning through each and every JWebUnit acceptance test.</p>

<p><img alt="test-optimized.png" src="http://blogs.atlassian.com/developer/clover/test-optimized.png" width="960" height="253" class="mt-image-none" style="" /><br />
<em>Clover Optimized build failing in 7 minutes</em></p>

<p><img alt="full-main-build.png" src="http://blogs.atlassian.com/developer/clover/full-main-build.png" width="977" height="246" class="mt-image-none" style="" /><br />
<em>The full build took 38 minutes to fail</em></p>

<p>On average the Clover Optimized Build takes just 7 minutes. This currently runs&nbsp;<em>all</em> unit and integration tests, and optimizes the long running Acceptance Tests. The main Confluence build takes 40 minutes on average to complete.</p>

<p>The Clover optimized build is a 'gateway' build for the Confluence CI-pipeline. It is the canary down the CI-mineshaft, if you will. If the optimized build smells danger, then it fails; preventing other builds from being triggered and hogging valuable CI cycles.</p>

<h2> Greater CI Throughput == Clearer CI Results</h2>

<p>The faster a build can run, the greater its throughput will be. What does this mean for a build that typically takes 40 minutes to run? This means that you get a much clearer picture of exactly which changeset has caused a build failure.</p>

<p>These next two screenshots show the Bamboo build history page for the full build plan and the Optimized build plan:</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="non-optimized-2.png" src="http://blogs.atlassian.com/developer/clover/non-optimized-2.png" width="933" height="156" class="mt-image-none" style="" /></span><br />
<em>Full build results for the past two hours</em></p>

<p>You can see that build 5285 failed fairly spectacularly. However, who do we blame for this failure? Three developers made changes that triggered the build which causes a possible disturbance for all three devs as each tries to clear their name.</p>

<p>The Clover Optimized build paints a clearer picture of the situation:</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="optimized-high-fidel-1.png" src="http://blogs.atlassian.com/developer/clover/optimized-high-fidel-1.png" width="931" height="217" class="mt-image-none" style="" /></span><br />
<em>Optimized build results for the past two hours</em></p>

<p>Over the past seven days there were 74 Full Confluence builds triggered. For the same time period there were 116 optimized builds, which represents an approximate 56% increase in build throughput.</p>

<h2> Where's the catch?</h2>

<p>Of course, Test Optimization of acceptance tests is not a silver bullet.</p>

<p>A full build, that runs all tests regularly should still play an important role in the CI stack. This is because there are still cases where an optimized build may pass, but a full build will fail. Since Clover tracks per-test coverage for Java source files only, it will not detect which tests to run when a non-Java source file is modified. This means that if the only modification for a changeset is for a non-Java file, such as web.xml; velocity macro; pom.xml; build.xml; .jsp (and so on) then possibly no tests will be run, causing the build to pass &#8212; when it should have failed!</p>

<p>The aim of an optimized build plan is for it to fail faster than a full build, and also to run more often than a full build does. An optimized build should be about quantity, whereas the main build is there for quality.</p>

<h2> What is per-test Coverage?</h2>

<p>Per-test coverage, is a mapping of each line of code that was covered by one or more tests, back to the tests that covered the line. As an example here is a screenshot of the Clover report for a test run of Confluence's Acceptance Tests showing all the tests that covered the "AllQuery" class:</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="per-test-class-coverage.png" src="http://blogs.atlassian.com/developer/clover/per-test-class-coverage.png" width="1060" height="386" class="mt-image-none" style="" /></span><br />
<em>Per-test Coverage for the AllQuery class</em></p>

<p>This shows us that "AllQuery" was covered by 16 test methods and 6 TestCases. If the class containing this code is modified in any way Clover will ensure only those 6 TestCases get run. This means the Confluence Acceptance tests can complete in just a few minutes as opposed to 40.</p>

<p><a href="http://blogs.atlassian.com/developer/2007/10/my_tests_touched_what.html">Per-test coverage data</a> is also excellent for answering the question: "Is there already a test for this Class and if so, which one?".</p>

<h2> The Benefits of Test Optimization</h2>

<p>The Confluence team benefit by having a Clover optimized in the following ways:<br />
<ul><br />
	<li> On average, they are alerted earlier to build breakages.</li><br />
	<li> When a build does break, fewer committers are involved with the breakage, making it easier to discern who broke the build.</li><br />
	<li> Fewer CI resources on our build server are consumed by Confluence's long running MAIN build, thereby reducing latency of other builds.</li><br />
</ul></p>
        
    <div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=2GO21O-FE60:_XX_a7noYzk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?a=2GO21O-FE60:_XX_a7noYzk:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/AtlassianDeveloperBlog?i=2GO21O-FE60:_XX_a7noYzk:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/AtlassianDeveloperBlog/~4/2GO21O-FE60" height="1" width="1"/>]]></content:encoded><description>Don't you hate committing code and then waiting hours to find out you broke the build? Even worse is when other people commit code at a similar time to you, and you get dragged into the 'who broke the build' witch-hunt by pure circumstance.

If your build times are blowing out because of long test runs (greater than ten minutes), then you are most likely suffering from CI (Continuous Integration) latency and the above problems are real problems for you and your team.

Clover can help alleviate theses problems, by optimizing both unit and acceptance tests, drastically reducing the feedback time for each commit. What follows is a case study of how Clover's Test Optimization is run on the Confluence project.</description><feedburner:origLink>http://blogs.atlassian.com/developer/2009/05/40_7_how_we_sped_up_confluences_build_by_5.html</feedburner:origLink></item></channel></rss>
