<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Carbon Five Community &#187; Rescue Mission</title>
	<atom:link href="http://blog.carbonfive.com/category/rescue-mission/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.carbonfive.com</link>
	<description></description>
	<lastBuildDate>Fri, 27 Aug 2010 00:24:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Custom Constraints for OCMock</title>
		<link>http://blog.carbonfive.com/2009/02/testing/custom-constraints-for-ocmock</link>
		<comments>http://blog.carbonfive.com/2009/02/testing/custom-constraints-for-ocmock#comments</comments>
		<pubDate>Tue, 17 Feb 2009 20:28:43 +0000</pubDate>
		<dc:creator>alon</dc:creator>
				<category><![CDATA[Rescue Mission]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=287</guid>
		<description><![CDATA[In my last post on unit testing iPhone development I introduced a couple helpful tools including OCMock, a mock objects implementation for Objective-C. I recently came across a scenario where I needed to make an assertion on an argument passed to an expected method invocation but OCMock did not provide the constraint I needed. I [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://blog.carbonfive.com/2009/02/testing/iphone-unit-testing-toolkit">last post on unit testing iPhone development</a> I introduced a couple helpful tools including <a href="http://www.mulle-kybernetik.com/software/OCMock/">OCMock</a>, a mock objects implementation for Objective-C.</p>
<p>I recently came across a scenario where I needed to make an assertion on an argument passed to an expected method invocation but OCMock did not provide the constraint I needed. I was testing a FileDetailsViewController that pushes a new FileContentsViewController on to the view stack when a button is pressed. I mocked the UINavigationController so that I could add an assertion that <code>pushViewController:animated:</code> is called with the right arguments.</p>
<p>The meat of my test is:</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #a61390;">@interface</span> FileDetailsViewControllerTest <span style="color: #002200;">:</span> SenTestCase <span style="color: #002200;">&#123;</span><span style="color: #002200;">&#125;</span>
    FileDetailsViewController <span style="color: #002200;">*</span>controller;
    <span style="color: #a61390;">id</span> navigationController;
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #a61390;">@implementation</span> FileDetailsViewControllerTest
&nbsp;
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> setUp <span style="color: #002200;">&#123;</span>
    controller <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>FileDetailsViewController alloc<span style="color: #002200;">&#93;</span> initWithNibName<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;FileDetailsView&quot;</span> 
                                                             bundle<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;
    navigationController <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>OCMockObject mockForClass<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>UINavigationController class<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;
    controller.navigationController <span style="color: #002200;">=</span> navigationController;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> testShowContentLoadsView <span style="color: #002200;">&#123;</span>
    <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>navigationController expect<span style="color: #002200;">&#93;</span> 
         pushViewController<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>OCMConstraint isKindOfClass<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>FileContentsViewController class<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>
                   animated<span style="color: #002200;">:</span><span style="color: #a61390;">YES</span><span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#91;</span>controller showContentsButtonWasPressed<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> tearDown <span style="color: #002200;">&#123;</span>
    <span style="color: #002200;">&#91;</span>navigationController verify<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<p>Note that in this example, I some fanciness is required to assign <code>controller.navigationController = navigationController</code> since it is a readonly property in the UIViewController API. I&#8217;ll explain what I did in a later post.</p>
<p>The OCMock feature I was missing is the <code>OCMock.isKindOfClass</code> constraint. So I wrote my own.</p>
<p>Following is the header file that declares the isKindOfClass assertion as a static method in an Objective-C category for OCMock. Categories are kind of like Ruby mixins for Objective-C. You can add static or instance methods to an existing class definition. With this approach I can follow the OCMock pattern of providing static factory methods on the OCMock class for the range of constraints available.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #11740a; font-style: italic;">//File: OCMockConstraint+Extensions.h</span>
<span style="color: #6e371a;">#import &lt;OCMock/OCMConstraint.h&gt;</span>
&nbsp;
<span style="color: #a61390;">@interface</span> OCMConstraint <span style="color: #002200;">&#40;</span>Extensions<span style="color: #002200;">&#41;</span>
&nbsp;
<span style="color: #002200;">+</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>isKindOfClass<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>value;
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<p>Following is the implementation, both of my new constraint and the factory method to create it.</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #11740a; font-style: italic;">//File: OCMockConstraint+Extensions.m</span>
<span style="color: #6e371a;">#import &quot;OCMockConstraint+Extensions.h&quot;</span>
&nbsp;
<span style="color: #a61390;">@interface</span> OCMKindOfClassConstraint <span style="color: #002200;">:</span> OCMConstraint
<span style="color: #002200;">&#123;</span>
    <span style="color: #a61390;">@public</span>
    <span style="color: #a61390;">id</span> testClass;
<span style="color: #002200;">&#125;</span>
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #a61390;">@implementation</span> OCMKindOfClassConstraint
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>evaluate<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>value
<span style="color: #002200;">&#123;</span>
	<span style="color: #a61390;">return</span> <span style="color: #002200;">&#91;</span>value isKindOfClass<span style="color: #002200;">:</span>testClass<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #11740a; font-style: italic;">// Static factory method</span>
<span style="color: #a61390;">@implementation</span> OCMConstraint <span style="color: #002200;">&#40;</span>Extensions<span style="color: #002200;">&#41;</span>
&nbsp;
<span style="color: #002200;">+</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>isKindOfClass<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>value <span style="color: #002200;">&#123;</span>
	OCMKindOfClassConstraint <span style="color: #002200;">*</span>constraint <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>OCMKindOfClassConstraint constraint<span style="color: #002200;">&#93;</span>;
	constraint<span style="color: #002200;">-</span>&gt;testClass <span style="color: #002200;">=</span> value;
	<span style="color: #a61390;">return</span> constraint;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<p>This implementation shows both how to write a custom constraint for OCMock and a use of Objective-C categories to provide a nice API for using it.</p>
<p>In many cases, you&#8217;ll want to write a custom constraint that is specific to your test scenario and not appropriate to be shared globally in this manner. In this case you&#8217;ll probably follow a similar strategy but implemented in your test class instead of mixed in to OCMockConstraint.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2009/02/testing/custom-constraints-for-ocmock/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>iPhone Unit Testing Toolkit</title>
		<link>http://blog.carbonfive.com/2009/02/testing/iphone-unit-testing-toolkit</link>
		<comments>http://blog.carbonfive.com/2009/02/testing/iphone-unit-testing-toolkit#comments</comments>
		<pubDate>Fri, 13 Feb 2009 21:36:20 +0000</pubDate>
		<dc:creator>alon</dc:creator>
				<category><![CDATA[Rescue Mission]]></category>
		<category><![CDATA[Testing]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=267</guid>
		<description><![CDATA[As we get in to developing iPhone/Touch applications at Carbon Five, we obviously want to bring over all our best agile practices including test driven development (TDD). I&#8217;ve been bootstrapping with the beta version of the Pragmatic iPhone SDK Development book but was disappointed to find no discussion of unit testing for the iPhone. So [...]]]></description>
			<content:encoded><![CDATA[<p>As we get in to developing iPhone/Touch applications at Carbon Five, we obviously want to bring over all our best agile practices including test driven development (TDD).</p>
<p>I&#8217;ve been bootstrapping with the beta version of the <a href="http://www.pragprog.com/titles/amiphd/iphone-sdk-development">Pragmatic iPhone SDK Development</a> book but was disappointed to find no discussion of unit testing for the iPhone. So I&#8217;ve been piecing it together as I go.</p>
<h2>GTM</h2>
<p>Unit testing in Objective-C is provided by the SenTestingKit framework that installs with XCode in /Developer/Library/Frameworks/. However it is not compatible with the iPhone SDK. The Google Toolbox for Mac provides <a href="http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting">an implementation of SenTestCase for the iPhone</a> that I am using instead. The instructions provided on that page worked for me with one exception that I included as a comment on that page:</p>
<blockquote><p>
I followed the instructions above but was unable to see any messages in the console. I did not see the behavior described above as:</p>
<p>Your target should now build cleanly, and if you check the build log you should see something like: &#8220;Executed 0 tests, with 0 failures (0 unexpected) in 0.001 (0.001) seconds&#8221; at the end.</p>
<p>It turned out that I needed to set the base SDK in the inspector for my target to an iPhone simulator even though the Overview showed I was building for simulator debugging.
</p></blockquote>
<p>I created a &#8216;Unit Tests&#8217; target per the GTM directions to run my tests. Now I am happily running my tests with command-B. One point that could be a problem when getting into continuous integration is that my Unit Tests target reports &#8216;Build succeeded (1 error)&#8217; when a unit test fails instead of having the build actually fail.</p>
<p>I have not gotten into the &#8220;Advanced Stuff&#8221; described on that page.</p>
<h2>Hamcrest</h2>
<p>The SenTestingKit assertion methods are pretty lame. They&#8217;re verbose and require that you provide a string message for your expectation (e.g. STAssertEqualStrings(a1, a2, description, &#8230;)). We&#8217;re using and like <a href="http://code.google.com/p/hamcrest/">Hamcrest</a> on many of our Java projects. I was psyched to see there is <a href="http://code.google.com/p/hamcrest/wiki/TutorialObjectiveC2">an Objective-C implementation</a>.</p>
<p>There is no download available for Hamcrest Objective-C so I <a href="http://code.google.com/p/hamcrest/source/browse/#svn/trunk/hamcrest-objectivec2/hamcrest">checked it out from SVN</a> and built the framework product in Xcode for OS X 10.5/i386.</p>
<p>I struggled for a while to get the dependency on the hamcrest.framework build product into my project. I ended up with this solution:</p>
<ol>
<li>Create a &#8216;Frameworks&#8217; folder in my project root
<li>Copy hamcrest.framework to Frameworks/
<li>In Xcode, right click on &#8216;Frameworks&#8217; and &#8216;Add existing framework&#8230;&#8221;
<li>Browse to the hamcrest.framework folder and add it, making sure to add it to my Unit Tests target only
<li>Right click on the Unit Tests target to add a new Copy Files Build Phase that copies to the Products directory and put that build phase early in the target steps
<li>Add hamcrest.framework to that build phase by dragging from Frameworks
<li>Include Hamcrest in your test class and you&#8217;re good to go
</ol>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #6e371a;">#import &quot;GTMSenTestCase.h&quot;</span>
<span style="color: #6e371a;">#define HC_SHORTHAND</span>
<span style="color: #6e371a;">#import &lt;hamcrest/hamcrest.h&gt;</span>
&nbsp;
<span style="color: #a61390;">@interface</span> ExampleWithAssertThat <span style="color: #002200;">:</span> SenTestCase
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #a61390;">@implementation</span> ExampleWithAssertThat
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> testUsingAssertThat
<span style="color: #002200;">&#123;</span>
    assertThat<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;xx&quot;</span>, is<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;xx&quot;</span><span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
    assertThat<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;yy&quot;</span>, isNot<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;xx&quot;</span><span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
    assertThat<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;i like cheese&quot;</span>, containsString<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;cheese&quot;</span><span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<h2>OCMock</h2>
<p>iPhone development requires a lot of mucking around with UI framework classes like UITableView, UIApplicationDelegate and so on. These classes have a lot of dependencies and are depended upon all over the place which can make it hard to get them under test. </p>
<p><a href="http://en.wikipedia.org/wiki/Mock_object">Mock objects</a> are a great solution to this problem and <a href="http://www.mulle-kybernetik.com/software/OCMock/">OCMock</a> provides an implementation for Objective-C. In this case, the OCMock.framework provided in the OCMock binary release worked for me when installed in the same manner as described for Hamcrest above. </p>
<p>This example of <a href="http://www.pathf.com/blogs/2009/01/testing-delegate-ocmock/">testing [UIApplicationDelegate applicationDidFinishLaunching]</a> illustrates its use pretty well. I have a simple test that creates a mock UITableView to asserts a cell is correctly created in [UITableViewController tableView:cellForRowAtIndexPath:]. It looks like:</p>

<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #6e371a;">#import &quot;GTMSenTestCase.h&quot;</span>
<span style="color: #6e371a;">#define HC_SHORTHAND</span>
<span style="color: #6e371a;">#import &lt;hamcrest/hamcrest.h&gt;</span>
<span style="color: #6e371a;">#import &lt;OCMock/OCMock.h&gt;</span>
&nbsp;
<span style="color: #6e371a;">#import &quot;SortedStateViewController.h&quot;</span>
<span style="color: #6e371a;">#import &quot;Country.h&quot;</span>
&nbsp;
<span style="color: #a61390;">@interface</span> SortedStateViewControllerTest <span style="color: #002200;">:</span> SenTestCase <span style="color: #002200;">&#123;</span><span style="color: #002200;">&#125;</span>
&nbsp;
SortedStateViewController <span style="color: #002200;">*</span>controller;
&nbsp;
<span style="color: #a61390;">@end</span>
&nbsp;
<span style="color: #a61390;">@implementation</span> SortedStateViewControllerTest
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> setUp <span style="color: #002200;">&#123;</span>
    controller <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>SortedStateViewController alloc<span style="color: #002200;">&#93;</span> init<span style="color: #002200;">&#93;</span>;
    Country <span style="color: #002200;">*</span>country <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>Country alloc<span style="color: #002200;">&#93;</span> init<span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#91;</span>country addState<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;New York&quot;</span> withPopulation<span style="color: #002200;">:</span><span style="color: #2400d9;">19306183</span> andArea<span style="color: #002200;">:</span><span style="color: #2400d9;">47213</span><span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#91;</span>country addState<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;California&quot;</span> withPopulation<span style="color: #002200;">:</span><span style="color: #2400d9;">36457549</span>     andArea<span style="color: #002200;">:</span><span style="color: #2400d9;">155959</span><span style="color: #002200;">&#93;</span>;
    controller.country <span style="color: #002200;">=</span> country;
    controller.key <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;population&quot;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span> testPopulatesCell <span style="color: #002200;">&#123;</span>
    <span style="color: #a61390;">id</span> tableView <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>OCMockObject mockForClass<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>UITableView class<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>tableView stub<span style="color: #002200;">&#93;</span> dequeueReusableCellWithIdentifier<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>OCMConstraint any<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;
    <span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span>indexPath <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSIndexPath</span> indexPathForRow<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span><span style="color: #2400d9;">0</span> 
                                                inSection<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span><span style="color: #2400d9;">0</span><span style="color: #002200;">&#93;</span>;
&nbsp;
    UITableViewCell <span style="color: #002200;">*</span>cell <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>controller tableView<span style="color: #002200;">:</span>tableView 
                            cellForRowAtIndexPath<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span>;
    assertThat<span style="color: #002200;">&#40;</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;color: blue;">&quot;California&quot;</span>, equalTo<span style="color: #002200;">&#40;</span>cell.text<span style="color: #002200;">&#41;</span><span style="color: #002200;">&#41;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #a61390;">@end</span></pre></div></div>

<h2>Continuous Integration</h2>
<p>So I&#8217;m pretty happy with where this is now. I&#8217;m working through the Pragmatic book TDD as I go. It&#8217;s also have an ObjectiveCTest that I add assertions to as I play with features of the language.</p>
<p>Missing from this toolkit is a good continuous integration solution. </p>
<p>There is now a <a href="http://blog.jeffreyfredrick.com/2008/11/27/continuous-integration-for-iphonexcode-projects/">recently released Xcode builder for CruiseControl</a>. However, we use <a href="http://www.atlassian.com/software/bamboo/">Bamboo</a> for CI for our Java and Ruby projects and are considering moving to <a href="http://www.jetbrains.com/teamcity/">TeamCity</a>. It looks like there is an <a href="http://github.com/orj/teamcity-xcode/tree/master">Xcode plugin for TeamCity</a> and some <a href="http://forums.atlassian.com/thread.jspa?messageID=257299176&#038;tstart=0">attempts with Bamboo</a>.</p>
<p>More on that later.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2009/02/testing/iphone-unit-testing-toolkit/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Make the things you do often fast and easy</title>
		<link>http://blog.carbonfive.com/2008/09/java/make-the-things-you-do-often-fast-and-easy</link>
		<comments>http://blog.carbonfive.com/2008/09/java/make-the-things-you-do-often-fast-and-easy#comments</comments>
		<pubDate>Wed, 10 Sep 2008 15:31:59 +0000</pubDate>
		<dc:creator>christian</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Rescue Mission]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=151</guid>
		<description><![CDATA[Many of our projects are &#8216;greenfield&#8217; and we have the opportunity to do things the way we like. By working on new projects every few months, as opposed to one project over the course of years, we have lots of opportunity to easily tweak and tune the way we do things. Not all of our [...]]]></description>
			<content:encoded><![CDATA[<p>Many of our projects are &#8216;greenfield&#8217; and we have the opportunity to do things the way we like.  By working on new projects every few months, as opposed to one project over the course of years, we have lots of opportunity to easily tweak and tune the way we do things.  Not all of our projects are from scratch though (see Alon&#8217;s post about <a href="http://blog.carbonfive.com/2008/09/testing/rewrite-or-rescue">Rewrite or Rescue</a>), so we sometimes end up dealing with years worth of history and crufty code.  It&#8217;s safe to say that each time we roll onto one of these projects, there&#8217;s going to be some level of bewilderment regarding what developers deal with on a daily basis.</p>
<p>Maybe it&#8217;s because we have a special opportunity to optimize the hell out of our development process, or the fact that we&#8217;re all productivity junkies; regardless of the reason, we religiously embrace the tenant &#8220;Make the things you do often fast and easy&#8221;.  It&#8217;s almost embarrassing to suggest that others don&#8217;t also subscribe to this simple notion, but &#8212; brace yourself &#8212; many do not.  On a project that has history, not everyone has been there for every decision.  In fact, many developers are at least relatively new and it&#8217;s somewhat customary to have a &#8220;it must be this way for a reason&#8221; attitude.  After all, who would deliberately make something cumbersome without good reason?</p>
<p>When we start working on one of these projects, we dedicate time to do some serious spring cleaning and tackle the things that will cost us the most in terms of pain and productivity.  The whole development team gets psyched about where we end up as it&#8217;s a significant improvement.  Projects with a history usually have a fair bit of low-hanging fruit.  Let&#8217;s discuss some of the things we see regularly.</p>
<h2>Build Systems</h2>
<p>Apparently few people like working on build scripts and when they do they have a habit of lowering their standards for quality of work.  That&#8217;s obviously not literally true, but sometimes it seems that way.  We&#8217;ve seen a number of beastly build systems that are slow because they&#8217;re doing things that aren&#8217;t necessary (extraneous jaring, copying, code generating, etc), they&#8217;re brittle and expensive to maintain, and full of dead code and duplicate target definitions&#8230; and they&#8217;re run many times every single work day.  It&#8217;s true that most developers may be compiling code from their IDE and thus bypassing the command-line build, but it&#8217;s still run on the build server, by ops folks, and even by developers when they&#8217;re debugging why something works from the IDE but busts on the build server.</p>
<h3>Guidelines for simplifying the build</h3>
<ul>
<li>Distill the build process down to the fastest, simplest steps that are necessary.</li>
<li>Eliminate duplicate and no-longer used dependencies; these files are being copied around and bundled for no reason (I&#8217;ve seen over 15 megs of unnecessary dependencies before).</li>
<li>When a project is split into multiple modules (and it should if it&#8217;s more than a few thousand lines), modules should be built in a consistent fashion using targets that are shared across modules.</li>
<li>Look for exceptions.  When you see something special happening for a particular file type, file name, or modules, ask yourself why.  Ask again.  Strive to eliminate these special cases when possible, even if they seem trivial.</li>
<li>Build a single deployable (or deployables) for all environments by eliminating environment-specific build code and externalizing application configuration. (Use Spring?  Check out this <a href="http://blog.carbonfive.com/2008/04/java/configuring-applications-with-spring">post on externalizing configuration with Spring</a>.)</li>
<li>Look for unnecessary code generation steps; if generated code changes once a year then check it in and make regenerating a manual step.</li>
<li>If you generate code coverage data, make sure that it&#8217;s only created when it&#8217;s needed (e.g. a nightly build on the build server), not on every build.</li>
</ul>
<p>So, we use <a href="http://maven.apache.org/">Maven 2</a> for all of our Java projects.  For sure, it has its share of rough edges (most of which are being fixed at a reasonable rate).  But it recommends some very sound conventions and doesn&#8217;t provide any scripting functionality, so it&#8217;s harder to hack it to do anything too unorthodox (please don&#8217;t use the antrun plugin unless as an incremental step when moving from Ant to Maven).  When you play ball by the Maven rules you&#8217;ll find your build much simpler and easier to maintain.  It&#8217;s likely you&#8217;ll notice other emergent benefits to boot.  For example, once you migrate to Maven you eliminate duplicate build configuration (both your command line build tool and IDE know how to compile your app &#8212; remember the <a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY principle</a>).  <a href="http://www.jetbrains.com/idea/features/ant_maven.html">IDEA</a>, Eclipse (via <a href="http://m2eclipse.codehaus.org/">m2eclipse</a>), and NetBeans all support importing from and synchronize with Maven.</p>
<p>Some people use <a href="http://incubator.apache.org/buildr/">Buildr</a> or Ant + <a>Ivy</a>, but either they don&#8217;t have the breadth of use (Buildr) or are more susceptible to writing nasty, unmaintainable build code (Ant).  That&#8217;s why we use Maven.</p>
<h2>Compile &gt; Deploy &gt; Make Changes &gt; Deploy Development Cycle</h2>
<p>Possibly more important than a simple and easy build, developers must be able to go through the compile, deploy, make changes, deploy cycle <em>FAST</em> (note that the compile, run tests, make changes, run tests cycle is also <em>very</em> important).</p>
<p>I remember working on an embedded system in 1998: a complex radio communications routing application written in C++ and deployed to custom hardware running the real-time operating system <a href="http://en.wikipedia.org/wiki/PSOS">PSOS</a>.  The build and deploy cycle took about 30 minutes and there were only 10 hardware instances for 60 engineers; you had to sign up for time slot on real hardware.  It was the epitome of unproductive as far as development environments go (and don&#8217;t even ask about debugging!).  You&#8217;d think such things were completely in the past (luckily they mostly are), but they&#8217;re not completely.  In the last 2 years I&#8217;ve seen applications that take 15 minutes to deploy.</p>
<p>It&#8217;s a drag when developers have to wait for these things to happen and it can totally destroy one&#8217;s rhythm, keeping developers from getting into the zone.  What&#8217;s worse, it&#8217;s completely unnecessary with modern tools.</p>
<h3>General Suggestions</h3>
<ul>
<li>Don&#8217;t drop down to the command-line; compile and deploy from your IDE (the IDE is your friend &#8211; master it).</li>
<li>Minimize steps for deploying changes to a running app:
<ul>
<li>Your IDE may support building on frame deactivation (IDEA does); check it out.</li>
<li>Look into the <a href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin">maven-jetty-plugin</a> if you use Maven.</li>
<li>Run in debug mode so that code can be hot-swapped or invest in <a href="http://www.zeroturnaround.com/javarebel/">JavaRebel</a>, which allows all sorts of code changes to take place without redeploying your application.</li>
<li>Deploy your application in exploded form; bundling a war or ear incurs unnecessary IO overhead.</li>
</ul>
</li>
<li>Host your database either locally on your workstation or on a beefy database server on the same LAN.  Remote databases are generally many times slower due to latency, even over fast connections.</li>
<li>Use JBoss?  Consider migrating to Jetty or Tomcat.  If that&#8217;s impossible, use the most stripped down profile (minimal, default, or all) that has what you need, or better yet, create a custom one which includes <em>only</em> what you need.</li>
<li>Minimize the amount of data needed in the database to run the application.  The same goes for running tests: do whatever it takes to run your tests against an empty (or very close to) schema. Check out the <a href="http://code.google.com/p/c5-db-migration/">Carbon Five DB Migration Project</a>.</li>
<li>Don&#8217;t skimp on developer hardware.  Buying the very fastest CPU isn&#8217;t going to be worth it, so aim for one or two models down from the fastest.  Buy the fastest hard disk you can since development is generally IO bound (WD Raptor and the new VelociRaptor are awesome, consider SSDs if your coffers run deep).  Avoid older CPU architectures (Pentium D), even when the clock speed (GHz) is faster.  Lastly, don&#8217;t be shy with memory; 4GB isn&#8217;t too much for a developer machine.</li>
</ul>
<p>In addition to these general recommendations, each individual application will have its own specific sources of inefficiency.  Many real world applications depend on services provided by application servers and/or a commercial products: message queues (JMS, ActiveMQ, etc), distributed caches (memcached, coherence, etc), enterprise service buses, job schedulers, work flow engines, etc.  It&#8217;s important that these services don&#8217;t get in the way of developing <em>fast</em>.  Some of them can be run in a light-weight development mode.  If you need to use one of these potentially heavyweight solutions, invest the time to minimize or eliminate any adverse effects to the development cycle.</p>
<h2>What Else?</h2>
<p>Some of the best improvements have nothing to do with the technical side of software development.  Take a step back and look at what else is happening (or not happening) each day.  There may be meetings which can be time boxed, consolidated, or eliminated all together.  Take a look at collaboration between engineers, product managers, testers, support and operations.  How long are developers waiting to have requirements-clarifying questions answered?  Ask your whole team where they think things can be improved.  Ask for feedback on a regular basis and allow it to help drive these improvements.</p>
<h3>Survey of other activities that should be fast and easy</h3>
<ul>
<li>Running the automated test suite &#8211; &lt; 10 minutes</li>
<li>Getting build results from continuous integration server &#8211; &lt; 10 minutes</li>
<li>Pushing a build to staging/acceptance server &#8211; One click build and deploy</li>
<li>Create a new instance of a minimal database instance &#8211; <a href="http://code.google.com/p/c5-db-migration/">Carbon Five DB Migration Project</a></li>
<li>Recreate production state in development for debugging</li>
<li>Story approval/acceptance &#8211; Continuous acceptance</li>
<li>Meetings &#8211; Timebox, Consolidate, Eliminate</li>
<li>Configure a new developer machine &#8211; Strive for zero configuration</li>
</ul>
<h2>Value Simplicity</h2>
<blockquote><p>Any intelligent fool can make things bigger, more complex and more violent.  It takes a touch of genius and a lot of courage to move in the opposite direction. -Albert Einstein</p></blockquote>
<p>There&#8217;s a theme underlying most of the solutions to these problems: simplicity.  Complex  systems don&#8217;t become complex and crufty overnight, they get that way one small step at a time.  With each change to a system it&#8217;s important to recognize that the change will either add complexity or remove it.  Complexity has a cost and it&#8217;s not to be taken lightly; make sure the benefit to each of the decisions that add complexity is worth that cost.</p>
<h2>Conclusion</h2>
<p>Making the things that people do often fast and easy can pay off geometrically as all developers benefit and regain a little more of their day (and sanity).  In the end, it&#8217;s not <em>just</em> about shaving off seconds or minutes, though that&#8217;s a huge part of it; it&#8217;s about creating a development environment that lets the team do what&#8217;s really important: write awesome code to solve real problems.  When the team dynamics, technical environment, and process are tuned just right, the overall benefit is greater than the sum of its parts.</p>
<p>Where have you seen changes in infrastructure, software, or process that&#8217;s resulted in a significant productivity bump?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2008/09/java/make-the-things-you-do-often-fast-and-easy/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Rewrite or Rescue</title>
		<link>http://blog.carbonfive.com/2008/09/java/rewrite-or-rescue</link>
		<comments>http://blog.carbonfive.com/2008/09/java/rewrite-or-rescue#comments</comments>
		<pubDate>Fri, 05 Sep 2008 20:31:14 +0000</pubDate>
		<dc:creator>alon</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Rescue Mission]]></category>

		<guid isPermaLink="false">http://blog.carbonfive.com/?p=164</guid>
		<description><![CDATA[Java has been the tool of choice for enterprise application development for many organizations for over 10 years. We are seeing more and more applications that are aging and suffering for it. We have worked with several recent clients to breathe new life in to these legacy applications so they can evolve and grow with [...]]]></description>
			<content:encoded><![CDATA[<p>Java has been the tool of choice for enterprise application development for many organizations for over 10 years. We are seeing more and more applications that are aging and suffering for it. We have worked with several recent clients to breathe new life in to these legacy applications so they can evolve and grow with the business they support.</p>
<p>This is the first of a series of articles about improving the quality of existing software and the processes that produce it.</p>
<h2>Why Not Rewrite</h2>
<p>We&#8217;ve all encountered software that we would rather just rewrite than try to fix. The problem may be poor design, poor implementation, lack of tests, bugs or any of a host of other software ills. We see ahead of us the pain of working with a poor quality system and the risks to schedule and quality of end product it creates.</p>
<p>At Carbon Five, we have participated in many successful projects to rewrite applications for our clients. Sometimes starting with a clean slate and all the lessons learned from a previous effort is exactly the right approach. Often it is not. </p>
<p>In our experience the greatest obstacle to a rewrite effort is that the existing system is the only accurate record of the requirements for the system. Applications evolve over years to meet the changing needs of a organization and its users. Many small decisions are made and captured in the functioning application only. Rewriting to reproduce the features of an existing system can be a very difficult effort to define and scope.</p>
<p>Usually the troubled application will continue to be use while a rewrite effort is underway. Often support, maintenance and feature development will need to continue on that application even while a rewrite is under way. A rewrite will compete for development resources and will be chasing new feature development on the existing application.</p>
<p>Not everything in the troubled application is rotten. There are often good pieces, or at least components that are reliable and well understood. It would be great to not have to rewrite them.</p>
<p>How can you be sure that a rewritten application will be so much better than the one it is replacing? All too often, the source of poor software quality is poor process and practices. Unless you fix those problems it is not worth embarking on a new software development effort. </p>
<h2>How to Rescue</h2>
<p>Sometimes a decision to rewrite is made because rescuing an application seems too daunting. Where do you start? How long will it take? Here are some high level thoughts from our experiences with our customers. We will get in to more detail on specifics in later articles.</p>
<h3>Set Short Term Goals</h3>
<p>Be sure to set demonstrable short term goals with meaningful names. When your job is to make the system &#8220;better&#8221; your job is never done. You need discrete goals with names that you can finish, demonstrate, release and feel good about before moving on. Easily communicated goals help to show management that the effort is progressing successfully and help your team build confidence in an often daunting task.</p>
<p>On one of our projects we set a goal of having a unit test in place for a component in our service layer. &#8220;Service Test&#8221; would be a fine name for that goal. In a system with no testing support, this was a big task that once completed opened the door for much easier testing.</p>
<h3>Don&#8217;t Let &#8220;Refactor&#8221; Become a Dirty Word</h3>
<p>A lot of the work you will do when rescuing an application can be characterized as refactoring:</p>
<blockquote cite="http://www.refactoring.com"><p>
Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.<br />
<em>Martin Fowler, http://www.refactoring.com</em>
</p></blockquote>
<p>Don&#8217;t let &#8220;refactor&#8221; become a dirty word. It&#8217;s too easy to say, &#8220;Yesterday I worked on refactoring our persistence layer. Today I&#8217;m going to keep working on that.&#8221; Again, come up with a name or description for the work you are doing that enables others to understand what you are doing and for you to describe progress through that task. Refactoring is one activity of many that you will engage in during this process. Avoid using &#8220;refactor&#8221; in the names of your short term goals.</p>
<h3>Introduce Testing</h3>
<p>A common attribute of many struggling applications is conspicuous lack of automated testing and the design and infrastructure to support it. We have found that the many changes required to support and write automated unit and integration tests are exactly the changes an application needs to get healthy. They include:</p>
<ul>
<li>Automating and streamlining build systems</li>
<li>Decoupling application components</li>
<li>Minimizing dependencies on application server features </li>
<li>Building test data sets</li>
<li>Continuous integration</li>
<li>Building a team interest in quality</li>
</ul>
<p>Each of these items is challenging to implement. You can&#8217;t do them all at once yet they are dependent on each other. Fortunately there are good open tools and practices available that provide a helpful bootstrap including Maven for convention-based builds, EasyMock to test code with lots of dependencies, Spring for component management outside of an application server, and CruiseControl for continuous integration (not to ignore the excellent almost-free options like TeamCity and Bamboo).</p>
<p>Introducing testing to a legacy application with no support for it is a daunting challenge and slow going at first. We&#8217;ve had good success in the past by first cleaning up the build and adding the ability to run tests, then finding a place in the application to break apart components and wedge a first test in. With a running test, you have something to automate so you can get continuous integration running. You also have a lot more insight into the architectural changes that will both improve the health of the application and make it easier to add more tests.</p>
<p>We have a lot more to say about this topic and specific experiences to share in coming articles.</p>
<h3>A Culture of Quality</h3>
<p>Another common attribute of a struggling application is that the team maintaining and developing the application is in continuous firefighting mode. They&#8217;ve lost hope of making fundamental improvements to their system and on a daily basis are fixing emergency bugs and hacking in features with their fingers crossed that they are not breaking something else.</p>
<p>Teams working in this mode collaborate poorly with product management. Product management feels that the developers are resistant to new features. Getting releases out is painful and unpredictable because defect rates are high.</p>
<p>These teams also collaborate poorly with each other. Developers tend to become isolated from each other as sole maintainers of one application component or another. They do not talk about how to make things better and become resigned to living with the status quo. </p>
<p>To be successful in a rescue mission or even a rewrite, you have to turn the firefighting culture into one where developers value quality and work for it daily. They should be excited to make things better and be engaging each other with ideas and practices to get there.</p>
<p>Again, this can be a very difficult effort. Sometimes it requires dramatic measures. In our experience this includes:</p>
<ul>
<li>Changing a workspace to remove barriers to casual conversation</li>
<li>Relocating developers to the same physical location</li>
<li>Hiring new blood and firing those resistant to change</li>
<li>Pair programming</li>
<li>Book clubs and study groups</li>
</ul>
<p>These changes are often the most painful to make. As a consulting company Carbon Five can advocate for these changes, teach pair programming and run a study group, but the real changes have to come from within. We have seen the most success in making these changes when there is an commitment to improve from the business and champions of this effort in management.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2008/09/java/rewrite-or-rescue/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Agile User Experience (UX) Resources</title>
		<link>http://blog.carbonfive.com/2007/09/flexflash/agile-user-experience-ux-resources</link>
		<comments>http://blog.carbonfive.com/2007/09/flexflash/agile-user-experience-ux-resources#comments</comments>
		<pubDate>Thu, 06 Sep 2007 23:12:19 +0000</pubDate>
		<dc:creator>alon</dc:creator>
				<category><![CDATA[Agile]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Flex/Flash]]></category>
		<category><![CDATA[Rescue Mission]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[sxsw]]></category>

		<guid isPermaLink="false">http://blog/?p=17</guid>
		<description><![CDATA[For the seven years that we have been evolving our Extreme Programming practice, we have repeatedly readdressed the question of how to best integrate user experience design (visual design, information architecture,&#8230;) with an agile process. As a consulting company, it has always been a requirement that we flex our process to accommodate our clients and [...]]]></description>
			<content:encoded><![CDATA[<p>For the seven years that we have been evolving our Extreme Programming practice, we have repeatedly readdressed the question of how to best integrate user experience design (visual design, information architecture,&#8230;) with an agile process. As a consulting company, it has always been a requirement that we flex our process to accommodate our clients and partners. Over the last year or two we have found our clients and partners increasingly interested in flexing their processes to work more agile and collaboratively with us.</p>
<p>The post is the first of a series on user experience (UX) design practices for agile software development teams. In an upcoming project with our partner Hot Studios we will be exploring new ways to integrate their user experience design process with our software development process. We&#8217;ll share some learnings from that, experiences from the past and new ideas for the future.</p>
<p>First we have a growing reading list of existing resources on the topic that I am posting here for you. If you have other resources for us, please share.</p>
<p><a href="http://timiti.blogspot.com/2008/02/user-research-as-commodity.html">User Research as a Commodity</a><br />
<i>Added 2008-02-14</i><br />
Alex at Pivotal Labs <a href="http://pivots.pivotallabs.com/users/alex/blog/articles/400-user-research-as-a-commodity">noted this post</a> from a user experience manager at Kaiser Permanente who describes a strategy for including regular user testing in an agile development process. The strategy is attractive because it fits with a key characteristic of agile where simple practices performed regularly produce high level complex results.</p>
<p><a href="http://web.archive.org/web/20070313205440/http://www.fawcette.com/interviews/beck_cooper/">Kent Beck v. Alan Cooper</a><br />
This is a 2002 article with the following abstract &#8220;Kent Beck is known as the father of &#8216;extreme programming,&#8217; a process created to help developers design and build software that effectively meets user expectations. Alan Cooper is the prime proponent of interaction design, a process with similar goals but different methodology. We brought these two visionaries together to compare philosophies, looking for points of consensus—and points of irreconcilable difference.&#8221; The difference in perspective between the two is very expressive of the conflicts we have seen between design and agile development processes. Many of the following resources reference this discussion.</p>
<p><i>Note: the original source for this great interview appears to have been taken over by another entity that is no longer making the article available. I am linking to it in the <a href="http://www.archive.org">Internet Archive</a>.</i></p>
<p><a href="http://www.disambiguity.com/waterfall-bad-washing-machine-good-ia-summit-07-slides/">Waterfall Bad, Washing Machine Good</a><br />
Leisa Reichelt&#8217;s IA Summit 07 presentation on the role of IA in the design process identifies the failures of a waterfall design process and argues that an agile, iterative design process is superior. Slide 14 is right on.</p>
<p><a href="http://www.agilemodeling.com/essays/agileUsability.htm">User Experience Activities on Agile Development Projects</a><br />
Recent article by Scott Ambler addresses the gap between Beck and Cooper by identifying a middle ground that requires UX and agile practitioners to learn a bit more about each others&#8217; practices and to adopt some of each.</p>
<p><a href="http://agileproductdesign.com/blog/index.html">Holistic Product Design &amp; Development</a><br />
A series of blog posts by Jeff Patton on agile software development and user centered design. Jeff moderates the <span class="nobr"><a href="http://tech.groups.yahoo.com/group/agile-usability/">agile-usability Yahoo group</a></span>.</p>
<p><a href="http://tech.groups.yahoo.com/group/agile-usability/">Agile Usability Yahoo! Group</a><br />
The description for this group provides a good summary of the issues we are tackling here. You are invited to jump into the fray.</p>
<p><a href="http://www.agilelogic.com/files/ExperiencesIntegratingUEDPractices.pdf">Experiences Integrating UED Practices</a><br />
In this experience report Paul Hodgetts presents his &#8220;teams&#8217; struggles to come to grips with the often programming-centric orientation of agile processes, and their ongoing efforts to integrate their UED (user experience design) best practices into the incremental, collaborative world of agile processes.&#8221; He provides an overview to the range of UX practices, identifies challenges and discusses practices that he found were particularly useful in integrating UX and and agile development.</p>
<p><a href="http://lists.interactiondesigners.com/htdig.cgi/discuss-interactiondesigners.com/2004-March/001083.html">Agile User-Centered Design</a><br />
A post to ID-discuss that presents one designer&#8217;s struggle to reconcile agile and user-centered design.</p>
<p><a href="http://www.foruse.com/articles/agiledesign.pdf">Process Agility and Software Usability: Toward Lightweight Usage-Centered Design</a><br />
2001 paper by Larry Constantine presents concrete practices for UX that map to agile practices. His approach is simple and pragmatic.</p>
<p><a href="http://www.slideshare.net/theinfonaut/are-agile-projects-doomed-to-halfbaked-design/">The Challenge of Agile Design</a><br />
These are slides from a talk given by our friends at Pivotal Labs and Satisfaction.</p>
<p><a href="http://agiletoolkit.libsyn.com/index.php?post_id=15584">User Centered Design Round Table</a><br />
Podcast of a discussion on user-centered design and agile development with Lynn Miller, Jeff Patton and Rebecca Wirfs-Brock.</p>
<p><i>Originally posted 2007-09-06</i><br />
<i>Last updated 2008-02-14</i></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.carbonfive.com/2007/09/flexflash/agile-user-experience-ux-resources/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
