<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-35609063</id><updated>2012-04-16T04:39:24.234+01:00</updated><category term='bcs'/><category term='lean'/><category term='disruptive'/><category term='geotagging'/><category term='geocoding'/><category term='web2.0'/><category term='photos'/><category term='gps'/><title type='text'>what i still don't know</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default?start-index=26&amp;max-results=25'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>66</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-35609063.post-445730490615202128</id><published>2012-03-08T21:09:00.000Z</published><updated>2012-03-08T21:09:23.915Z</updated><title type='text'>Is programming language choice a good thing?</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;a href="http://bovon.org/"&gt;James Lewis&lt;/a&gt; and I did a ThoughtWorks QTB on programming language choice. You can watch the video &lt;a href="http://www.thoughtworks.com/events/programming-language-choice-good-thing"&gt;here&lt;/a&gt;. I'm working on getting the scala code I mention in the talk online, it shows how you can use GPUs from within scala code via OpenCL.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-445730490615202128?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/445730490615202128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=445730490615202128' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/445730490615202128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/445730490615202128'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2012/03/is-programming-language-choice-good.html' title='Is programming language choice a good thing?'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-8294910829857563191</id><published>2011-05-09T23:22:00.000+01:00</published><updated>2011-05-09T23:22:02.725+01:00</updated><title type='text'>Environment Cost: Are licensing costs damaging software quality?</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Are you unable to test your solution fully except in production? This could well be because your business can't afford to buy licenses for the same software as is in production to be installed on UAT, QA and Development environments. I've seen clients where things like web servers,&amp;nbsp;application&amp;nbsp;servers, messaging middleware and storage solutions just can't be the same as production because it's too darn expensive.&lt;br /&gt;
One reason this happens is because these kind of software purchases tend to be costed based only on production needs, most often this occurs when procurement sits off to one side away from the rest of the business. In fact in one organisation I saw a few years ago the development part of the organisation had to negotiate&amp;nbsp;separately to buy the same software since procurement dealt&amp;nbsp;solely&amp;nbsp;with "operational" systems.&lt;br /&gt;
Frankly I also think it happens because some software&amp;nbsp;vendors&amp;nbsp;are hoping clients will have to come back and ask for lots more licences once they realise they need to start testing things. I've seen this happen too, although to be fair I also know of vendors who throw in test and dev licences for free. (of course some vendors will gladly sell you expensive test tools to test their expensive complex software, but thats a whole other story....)&lt;br /&gt;
The lack of a representative "production like" environment causes numerous problems, in fact most outages I've seen could have been prevented had something more representative of production been available. When I say representative I mean environments where the performance delta's between them are relatively easy to measure and model, that gets much harder when you are running different software or radically different hardware.&amp;nbsp;&amp;nbsp;I'm now of the view that if you can't afford to buy the licences for all of production, UAT, QA and development then you can't afford the software at all.&lt;br /&gt;
Once you go down the road of relying on some expensive software (or hardware) to scale things on production you are running increasing risks: an outage becomes more and more likely and you are letting a software or hardware vendor control the cost of scaling your business. I think it's much better to go with commodity hardware, and software where you choose what to pay for and when - this seems to be what works for the best known "internet scale" companies.&lt;br /&gt;
I think&amp;nbsp;software licensing and hardware costs can damage software quality as they make scaling the ability to test software too expensive. It's just not enough to scale production, you have to have a cost effective way to scale the means of getting high quality software into producution as well.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-8294910829857563191?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/8294910829857563191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=8294910829857563191' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8294910829857563191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8294910829857563191'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2011/05/environment-cost-are-licensing-costs.html' title='Environment Cost: Are licensing costs damaging software quality?'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-2122441057441382845</id><published>2010-12-15T16:06:00.000Z</published><updated>2010-12-15T16:06:53.507Z</updated><title type='text'>Signal to Noise ratio in software testing</title><content type='html'>When using techniques like automated functional testing we want a high signal to noise ratio, we want a failing test to tell us something has really broken and not just that we changed something.&lt;br /&gt;
Tracking this ratio of 'useful failures' to 'wasteful failures' gives us a signal to noise ratio for our tests. Ideally we want something like 10:1, say, so only every 10th failure is spurious and just due to a software change and/or fragility in our tests. Unfortunately for many teams this is more like 1:10, so most test failures are due to test fragility and not to something really being broken.&lt;br /&gt;
When we get too much noise and not enough signal we tend to start ignoring problems and disabling tests, while this might make the ratio better we do so at the cost of losing some of the signal.&amp;nbsp;You might want to try tracking this ratio for a few weeks, tracking the trend over time can give a way to focus attention on eliminating areas of high noise.&lt;br /&gt;
My experience is the sources of noise are often&amp;nbsp;related and are&amp;nbsp;often due to things like timeout issues, hard coding id's or creating&amp;nbsp;unnecessary&amp;nbsp;dependencies on the order things get displayed or happen. Another common source of noise is teams working with very large backlogs of 'low impact' bugs, the issue here is more complex and probably worth a post on it's own - but&amp;nbsp;when prioritizing bugs&amp;nbsp;it is worth considering the impact they have on team productivity and not just the production impact.&lt;br /&gt;
Whatever the cause a relatively small effort can sometimes dramatically improve the signal to noise ratio.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-2122441057441382845?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/2122441057441382845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=2122441057441382845' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/2122441057441382845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/2122441057441382845'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2010/12/signal-to-noise-ratio-in-software.html' title='Signal to Noise ratio in software testing'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-6838344083795513251</id><published>2010-03-10T17:04:00.001Z</published><updated>2010-03-10T17:09:26.406Z</updated><title type='text'>Software that requires unnessecary constant internet connectivity</title><content type='html'>I bought a logitech harmony remote a while back and last night needed to reconfigure it, so I fire up the config tool and sit watching connection errors. I finally followed a link to the support web site only to see "routine maintenance" was in progress. I'm left frustrated and wondering why a connection is always required to just reconfigure a couple of buttons on the remote. Why not cache some data locally and have an offline mode?&lt;br /&gt;
&lt;br /&gt;
I had the same issue with my slingbox which I use to stream tv to another room, when I moved house I had no broadband for a week or so - I could not retune the slingbox without internet connectivity as this is required for authentication, again puzzlement and frustration.&lt;br /&gt;
&lt;br /&gt;
Now in the last few days I've been reading about Ubisoft and issues people have had with their "always need to be connected" DRM solution, I'm very glad I've not bought the product involved.&lt;br /&gt;
&lt;br /&gt;
Until the internet achieves utility status (like electricity or water) it seems a huge assumption and big risk to release products that rely on "always on" connections where an offline mode seems entirely possible. I think some packaging that says "Needs an internet connection" needs to be revised to say "Needs a constant internet connection to function at all"&lt;br /&gt;
&lt;br /&gt;
To introduce an unnecessary single point of failure is in my book poor design or poor decision making process, especially where most of the impact of the associated risk ends up with the consumer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-6838344083795513251?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/6838344083795513251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=6838344083795513251' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6838344083795513251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6838344083795513251'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2010/03/software-that-requires-unnessecary.html' title='Software that requires unnessecary constant internet connectivity'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-8477129913498842960</id><published>2010-01-27T15:21:00.000Z</published><updated>2010-01-27T15:21:04.713Z</updated><title type='text'>Using Retlang for multi-threaded windows form code</title><content type='html'>&lt;p class="p2"&gt;I've been a fan of Retlang for a long time and have been meaning to write up some of the ways I've used it. This is the first of 3 blog entries and in this one I'll describe a way of using Retlang to avoid the dreaded InvalidOperationException "Control accessed from a thread other than the thread it was created on".&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;It avoids lots of nasty boiler plate calls around InvokeRequired(), this isn't new and has been described before but what I want to show how it can also make testing easier and work with existing MVC patterns.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;We are going to use Retlang to allow controller code and view code to communicate over message channels in a thread safe way.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Lets imagine a very simple implementation of MVC to illustrate the approach.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Here's the test, we are just adding numbers together and then displaying the answer in the view.&lt;span class="Apple-converted-space"&gt; &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;/span&gt;TestFixture&lt;span class="s1"&gt;]&lt;/span&gt;&lt;/p&gt;&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;TestSimpleController&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s3"&gt;Model&lt;/span&gt; model;&lt;/p&gt;&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;SimpleView&lt;span class="s1"&gt; view;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s3"&gt;MockRepository&lt;/span&gt; repository;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;/span&gt;SetUp&lt;span class="s1"&gt;]&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; SetUp()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;MockRepository&lt;/span&gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model = repository.StrictMock&amp;lt;&lt;span class="s3"&gt;Model&lt;/span&gt;&amp;gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view = repository.StrictMock&amp;lt;&lt;span class="s3"&gt;SimpleView&lt;/span&gt;&amp;gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;span class="s3"&gt;Test&lt;/span&gt;]&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; testShouldDoSimpleAdd()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(10);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(5);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.SumStack();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;LastCall&lt;/span&gt;.Return(15);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view.DisplayCurrentTotal(15);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.ReplayAll();&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; simpleController = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt;(model,view);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(10);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(5);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.Sum();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.VerifyAll();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;I'm using RhinoMock for mocking the view and the model. The controller implementation looks like&lt;/p&gt;&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;SimpleController&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p4"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;private&lt;span class="s1"&gt; &lt;/span&gt;readonly&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s3"&gt;Model&lt;/span&gt;&lt;span class="s1"&gt; model;&lt;/span&gt;&lt;/p&gt;&lt;p class="p4"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;private&lt;span class="s1"&gt; &lt;/span&gt;readonly&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s3"&gt;SimpleView&lt;/span&gt;&lt;span class="s1"&gt; view;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; SimpleController(&lt;span class="s3"&gt;Model&lt;/span&gt; model, &lt;span class="s3"&gt;SimpleView&lt;/span&gt; view)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;this&lt;/span&gt;.model = model;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;this&lt;/span&gt;.view = view;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; SendNumber(&lt;span class="s2"&gt;int&lt;/span&gt; i)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(i);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p4"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;public&lt;span class="s1"&gt; &lt;/span&gt;void&lt;span class="s1"&gt; Sum()&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; result = model.SumStack();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view.DisplayCurrentTotal(result);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Now suppose we have to extend the implementation to deal with a long running task, we don't want to block the view thread as that is bad for user experience so we create a new thread to call the model from. Here is the controller code for that:&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; DoPrediction()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; thread = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;Thread&lt;/span&gt;(InvokeModelWork);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;thread.Start();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; InvokeModelWork()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; result = model.LongRunningCalculation();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view.DisplayCurrentTotal(result);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;It's worth noting here that a test for this code written in the same way as for our SimpleAdd will quite likely start failing at this point as asserts will be made on the calling thread before the "worker" thread has called the model and the view. Here is that naive and incorrect version of the test&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;span class="s3"&gt;Test&lt;/span&gt;]&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; testShouldInvokeLongRunningCalc()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(33);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(44);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(11);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.LongRunningCalculation();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;LastCall&lt;/span&gt;.Return(88);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view.DisplayCurrentTotal(88);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.ReplayAll();&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; simpleController = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt;(model, view);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(33);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(44);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(11);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.DoPrediction();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.VerifyAll();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;This sort of test is a common problem in many code bases where the threading is not added until after complaints from the users about performance problems - this can create a lot of problems with the testing and is a very common source of bugs in MVC code. It is easy to tie yourself in knots using the mock framework to signal threads that a method was called or, even worse, using Thread.Sleep() to pause inside of the test. My experience has been that both of these are a source of "mysterious" test/build failures and are very hard to maintain.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Anyway even if we make our test pass we'll see the following when we try to run the code for real in a Windows Forms implementation of our view interface:&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;{"Cross-thread operation not valid: Control 'textBoxResult' accessed from a thread other than the thread it was created on."}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;For completeness here is the very simple user control that implements the view.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s2"&gt;partial&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;SimpleControl&lt;span class="s1"&gt; : &lt;/span&gt;UserControl&lt;span class="s1"&gt;, &lt;/span&gt;SimpleView&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;readonly&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt; controller;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; SimpleControl()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;controller = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt;(&lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;DoesMath&lt;/span&gt;(),&lt;span class="s2"&gt;this&lt;/span&gt;);&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;InitializeComponent();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; DisplayCurrentTotal(&lt;span class="s2"&gt;int&lt;/span&gt; i)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;textBoxResult.Text = i.ToString();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; buttonSum_Click(&lt;span class="s2"&gt;object&lt;/span&gt; sender, &lt;span class="s3"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;controller.Sum();&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; buttonPredict_Click(&lt;span class="s2"&gt;object&lt;/span&gt; sender, &lt;span class="s3"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;controller.DoPrediction();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; buttonSubmit_Click(&lt;span class="s2"&gt;object&lt;/span&gt; sender, &lt;span class="s3"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; input = &lt;span class="s2"&gt;int&lt;/span&gt;.Parse(textBoxInput.Text);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;controller.SendNumber(input);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;So how can Retlang help?&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Retlang lets us create channels which different threads can use to communicate. We can create one of these channels from the dispatch thread of a windows form class.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Here is the new version of the constructor for the controller:&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s3"&gt;Channel&lt;/span&gt;&amp;lt;&lt;span class="s2"&gt;int&lt;/span&gt;&amp;gt; viewChannel;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; SimpleController(&lt;span class="s3"&gt;Model&lt;/span&gt; model, &lt;span class="s3"&gt;SimpleView&lt;/span&gt; view)&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;this&lt;/span&gt;.model = model;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;viewChannel = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;Channel&lt;/span&gt;&amp;lt;&lt;span class="s2"&gt;int&lt;/span&gt;&amp;gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;viewChannel.Subscribe(view.Fibre, view.DisplayCurrentTotal);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;In this simple case we have one channel over which we will send int's and we also have have just one subscriber which is the view.DisplayCurrentTotal() method. We also ask the view to provide use with the Fiber to use, in this case this allows the view to provide a fiber that it knows will be safe to execute the subscriber method(s) on.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;In more advanced cases you might want to have many channels or multi-plex different message types over the same channel and use the Dispatcher pattern to route those to the right subscribers on the view. I've done just that in a real example and used reflection to create mappings from each view method to the the correct message type being received from the channel.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;On with our simple example; so what do Sum() and DoPrediction() look like now?&lt;/p&gt;&lt;br /&gt;
&lt;p class="p4"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;public&lt;span class="s1"&gt; &lt;/span&gt;void&lt;span class="s1"&gt; Sum()&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; result = model.SumStack();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;viewChannel.Publish(result);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; DoPrediction()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; thread = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;Thread&lt;/span&gt;(InvokeModelWork);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;thread.Start();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; InvokeModelWork()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; result = model.LongRunningCalculation();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;viewChannel.Publish(result);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;So instead of calling methods on the view we publish messages instead, in this case just an int.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;What about the test? We'll need a few changes to SetUp as well, Retlang gives us a nice stubbed implementation of fiber we can use for testing.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;/span&gt;SetUp&lt;span class="s1"&gt;]&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; SetUp()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;fiber = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;StubFiber&lt;/span&gt;();&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;MockRepository&lt;/span&gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model = repository.StrictMock&amp;lt;&lt;span class="s3"&gt;Model&lt;/span&gt;&amp;gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view = repository.StrictMock&amp;lt;&lt;span class="s3"&gt;SimpleView&lt;/span&gt;&amp;gt;();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;SetupResult&lt;/span&gt;.For(view.Fibre).Return(fiber);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;fiber.Start();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;[&lt;span class="s3"&gt;Test&lt;/span&gt;]&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; &lt;span class="s2"&gt;void&lt;/span&gt; testShouldInvokeLongRunningCalc()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; finished = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;ManualResetEvent&lt;/span&gt;(&lt;span class="s2"&gt;false&lt;/span&gt;);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(33);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(44);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.PushNumber(11);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;model.LongRunningCalculation();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;LastCall&lt;/span&gt;.Return(88);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;view.DisplayCurrentTotal(88);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;// note the type of m below must match the parameter type of the above method&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;LastCall&lt;/span&gt;.Callback((&lt;span class="s2"&gt;int&lt;/span&gt; m) =&amp;gt; finished.Set());&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.ReplayAll();&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;var&lt;/span&gt; simpleController = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt;(model, view);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(33);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(44);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.SendNumber(11);&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;simpleController.DoPrediction();&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s3"&gt;Assert&lt;/span&gt;.IsTrue(finished.WaitOne(1000),&lt;span class="s4"&gt;"Timed out"&lt;/span&gt;);&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;repository.VerifyAll();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;We use a manual reset event to allow us to wait for a particular event to occur, in this case the call to view.DisplayCurrentTotal() is followed by the use of a callback to allow this to happen. We then wait for the event (or a timeout) before calling VerifyAll(). So we can't avoid the fact that things happen asynchronously, but by using a message channel we make things explicitly so and importantly we have consistency across all the controller/view interactions. If we treat our test code as just code we can take advantage of this consistency to extract methods etc and this helps keep the code readable.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;Here are the required changes for the view:&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt; &lt;span class="s2"&gt;readonly&lt;/span&gt; &lt;span class="s3"&gt;FormFiber&lt;/span&gt; formFiber;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt; SimpleControl()&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;formFiber = &lt;/span&gt;&lt;span class="s2"&gt;new&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;FormFiber&lt;span class="s1"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;this&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="s2"&gt;new&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;BatchAndSingleExecutor&lt;span class="s1"&gt;());&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;controller = &lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;SimpleController&lt;/span&gt;(&lt;span class="s2"&gt;new&lt;/span&gt; &lt;span class="s3"&gt;DoesMath&lt;/span&gt;(),&lt;span class="s2"&gt;this&lt;/span&gt;);&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;InitializeComponent();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;formFiber.Start();&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p3"&gt;&lt;span class="s1"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;IDisposingExecutor&lt;span class="s1"&gt; Fibre&lt;/span&gt;&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;{&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt; { &lt;span class="s2"&gt;return&lt;/span&gt; formFiber; }&lt;/p&gt;&lt;p class="p2"&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;&lt;span class="Apple-tab-span"&gt; &lt;/span&gt;}&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;We no longer need to worry about surrounding things with DispatchRequired() calls, our implementation of the view interface stays nice and clean. We also avoid having to create a view Proxy, this is another solution to the DispatchRequired() that requires a lot of repetitive boiler plate code.&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;I've tried to keep things simple for this example, some things to think about for a real world implementation are&lt;/p&gt;&lt;ul class="ul1"&gt;  &lt;li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;That Methods on the view interface will be of the form void Method(Message msg)&lt;/li&gt;
  &lt;li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;Any methods on the view that do need to return something must be called from the same thread as created the form (but to be thread safe this must be done anyway).&lt;/li&gt;
  &lt;li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;Whether to multiplex multiple message types over the same channel and then use a Dispatcher to call the correct method on the view or to have a channel per message type&lt;/li&gt;
  &lt;li style="margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica"&gt;Whether to create different message channels for different styles of message flow and quality of service&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;p class="p2"&gt;I wonder if this is more Model Channel Controller as opposed to Model View Controller?&lt;span class="Apple-converted-space"&gt; &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;
&lt;p class="p2"&gt;In the next entry I'll describe using a similar idea to allow high performance low latency updates of a view direct from a domain model by sidestepping the controller but without seriously compromising encapsulation. In the final entry of the 3 I'll describe how to use Retlang channels to efficiently share work across multiple worker threads and hence CPU core. The Retlang developers have done a great job and created a simple to use but highly useful library.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-8477129913498842960?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/8477129913498842960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=8477129913498842960' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8477129913498842960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8477129913498842960'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2010/01/using-retlang-for-multi-threaded_5958.html' title='Using Retlang for multi-threaded windows form code'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-5907208074939323619</id><published>2009-11-24T15:27:00.001Z</published><updated>2009-11-24T15:30:44.551Z</updated><title type='text'>Ban The Debugger</title><content type='html'>&lt;span style="font-weight: bold;"&gt;How much logging do you really need in your application?&lt;/span&gt;

I was visiting  a client recently to help diagnose some problems with one of their applications. I asked for their support people to send me the log file, seemed like a good place to start. I got a file of about 2.5K, that seemed kinda small to me. I opened it up and found just one exception and it's stack trace so I got back to the support people to check. Yep, that was it, logging cranked up to max and the only output was one stack trace. Not even the date and time of the problem. Wow!

A technique I find very useful prior to go live is to Ban the Debugger. Developers get very used to just firing up the debugger when fixing issues or diagnosing problems. This is fine but it means that no one looks at the log files from the point of view of someone who only has those available to find out what is going on. For our colleagues in support and operations it is only the log files that they can use to find and fix issues.

So during development prior to go live I stop the developers from using the debugger, instead I ask them to spend at least some time trying to fix the issue based solely on the log files and whatever else our friends in support will have available once we have gone live. This usually leads to a big upswing in the amount of logging and it's logging we know helps to fix issues.

Of course sometimes you do need the debugger, but hopefully after we've used the logging to narrow down the problem area and to understand what the users were trying to do at the time.

So back to the client above - it turned out the technical lead had never worked in support and that the support team had not really been represented during development. We owe our colleagues better than this, perhaps Banning the Debugger for a time during development might help.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-5907208074939323619?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/5907208074939323619/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=5907208074939323619' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5907208074939323619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5907208074939323619'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/11/ban-debugger.html' title='Ban The Debugger'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-6041632081941360494</id><published>2009-11-04T09:34:00.002Z</published><updated>2009-11-04T09:40:57.618Z</updated><title type='text'>Technology Lightning Talks in Chicago</title><content type='html'>Some of my colleagues from the ThoughtsWorks Technology Advisory Board members will be delivering Lightning Talks next week in Chicago, I'm not speaking myself but will be attending. If you are in Chicago and would like to come along follow this &lt;a href="http://connect.thoughtworks.com/chicagotab/"&gt;link&lt;/a&gt; for registration information. I think it's a pretty great list of speakers and am looking forwards to hearing them myself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-6041632081941360494?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/6041632081941360494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=6041632081941360494' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6041632081941360494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6041632081941360494'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/11/technology-lightning-talks-in-chicago.html' title='Technology Lightning Talks in Chicago'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-5199673749781347837</id><published>2009-11-02T16:57:00.002Z</published><updated>2009-11-02T20:55:24.609Z</updated><title type='text'>Databases and Separation of Concerns</title><content type='html'>A continuing source of pain on projects is Object Relational Mappings and databases. A contributor to this pain is the mixture of two concerns, this mixture seems to occur on nearly every project that uses a database. I think spending a moment to think about these two different kinds of usage is worthwhile. So what are these two concerns?

A. Persist State Needed for Recovery
This is just working state saved so that when the application restarts we can continue processing. For example perhaps the current state of a customer order or work in progress on a very long running calculation.

B. Data saved for Reporting and Querying
This is data saved so it can be queried later on, perhaps to allow end of month reports to be generated or tracking of user trends. It is not needed to recover the working state of an application.

Many teams try to overload (A) to achieve (B), the sorts of problems this can cause are

i) Data Volume - the volumes of data needed for (A) tend to be smaller, it's current working data as opposed to historical data. This can show itself as performance issues for the application as queries become slow over time.

ii) The object design needed for (A) and (B) is not necessarily the same. This often shows itself as fields or object relationships being created with names like "history" or "recordOf", so an object design created for (A) becomes overloaded with things needed for (B). This again causes performance issues as the number of objects and data getting pulled into memory by the ORM can increase. It also means a simple state update can touch a lot of tables as we try to achieve (B) at the same time, chances are the indexes needed for historical queries can start to impact the update speed for these.

iii) Confusing Code
As with any other area where we fail to achieve separation of concerns the code can become confusing, for example state change operations become implicitly overload to create and persist data needed for historical reasons.

iv) Archive of historical data becomes problematic, so you can't cleanly identify what data in the DB can be safely moved out to a historical database or deleted without impacting the functionality of the application itself.

It wont always help but separation of these two concerns can provide clarity and address some kinds of performance issues. I certainly think it's worth calling out these concerns as different kinds of requirement even if you end up using the same implementation for both.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-5199673749781347837?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/5199673749781347837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=5199673749781347837' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5199673749781347837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5199673749781347837'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/11/databases-and-separation-of-concerns.html' title='Databases and Separation of Concerns'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-8727077235246336552</id><published>2009-09-30T19:53:00.001+01:00</published><updated>2009-09-30T19:54:42.882+01:00</updated><title type='text'>See you at JAOO?</title><content type='html'>I'm very excited to be attending JAOO this year, I'll be hanging out at the ThoughtWorks stand so why not head over and say hello.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-8727077235246336552?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/8727077235246336552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=8727077235246336552' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8727077235246336552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8727077235246336552'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/09/see-you-at-jaoo.html' title='See you at JAOO?'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-3902638817129835602</id><published>2009-09-30T19:50:00.003+01:00</published><updated>2009-09-30T19:53:28.101+01:00</updated><title type='text'>Great OS X app for those who change location a lot</title><content type='html'>Finding this extremely useful &lt;a href="http://www.symonds.id.au/marcopolo/"&gt;http://www.symonds.id.au/marcopolo/&lt;/a&gt;

Lets you define rules to work out where you currently located based on all sorts of criteria and then define actions for that location: enable screen saver password, default printer, mount a drive, etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-3902638817129835602?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/3902638817129835602/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=3902638817129835602' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3902638817129835602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3902638817129835602'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/09/great-os-x-app-for-those-who-change.html' title='Great OS X app for those who change location a lot'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-8568296181771161278</id><published>2009-09-02T13:09:00.002+01:00</published><updated>2009-09-02T13:23:25.865+01:00</updated><title type='text'>Incremental Internet Product Launch - responding to feedback over predicting needs</title><content type='html'>I've spent a lot of time working with companies looking to launch offerings on the internet, I've been asked to get involved at various stages of the life cycle including starting over after the previous launch failed.

There seem to be two main approaches to product development for the internet and interestingly these approaches often correlate with whether the company is a "traditional" company or not. By traditional I mean a company who previously sold and marketed products via non-internet means, the internet is something they've had to react to. So by implication the other kind of company is one that exists solely because of the internet, an "internet company" if you will, they are built around the web and have none of the more traditional retail sales channels.

&lt;span style="font-weight: bold;"&gt;Traditional Companies&lt;/span&gt;
The more traditional companies often seem to have a very fixed approach to product development, perhaps as a legacy of selling through offline means such as the high street? They go through a series of (often costly) exercises to try and fully describe a product offering, so to anticipate needs and to fully design a web product. There are usually a series of gates to pass through to get approval for budget, not least because the whole product has been designed and needs to be built so there is a lot of money involved.

Product launches are costly infrequent events for this kind of company, usually with a lot riding on them. As the product offering has been designed upfront traditional methodologies are often used in delivery, no need for a more Agile approach when we already know what the users want. As such a ability to react quickly to change is rarely valued in this kind of product development with the real users often not involved at all.

There is a high degree of risk around adoption with this sort of approach; will people actually want to use it? One technique often used to try to  mitigate this risk is a big expensive marketing campaign and lots of publicity around the launch, although this in turn increases risks around performance problems and scaling at launch time. It also tends to push companies towards big upfront expenditure on fixed infrastructure sized for the peak demand. I'm sure we can all think of examples of products launched in a blaze of publicity only then to very publicly fail due to performance issues. Finally even if things go well and you do manage a good launch you've got a much bigger problem: non-traditional companies seem able to react extremely quickly and update or create competing offerings, often in weeks and not the months or years it took the traditional company.

&lt;span style="font-weight: bold;"&gt;Internet Companies&lt;/span&gt;
Internet companies seem to do things quite differently, perhaps because they've never had to sell products that had to be 100% fully formed at launch via traditional retails channels. They do something few traditional companies seem willing to do - they create and launch a product with an absolute bare minimum of functionality, just enough to hit the core proposition and nothing more. They then put that out into the wild and see what happens; often with minimum publicity but always with good feedback mechanisms in place.

If no one picks up on the offering it can be just as quietly be canned, while money has been lost it was a small amount compared to a fully fledged product launch. In fact as this is a relatively inexpensive approach there is a chance to try out a whole series of ideas and do so quickly and often.

If people do start to use it then the feedback mechanisms come into play and you can respond to that feedback to start incrementally improving the product. There is a very direct relationship with the users and their feedback here. You'll probably see a gradual ramp up in users as things pick up and can start to scale up numbers of machines etc. If you've used "cloud" like technologies from day one this is probably pretty easy to do. By necessity your delivery mechanisms have to be fast and quality has to be a fundamental part of the process: no time for a big 1 month long test period at the end, users need rapid response to their feedback for them to stay engaged with the product.

&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;
If you are a traditional company looking to go head to head with an internet company you may need to change how you approach internet product development. You'll be going up against companies that constantly and rapidly respond to user feedback. The key seems to be understanding the bare minimum you can launch with and then not being afraid of having a more direct relationship with end users.

If someone is telling you that the bare minimum product definition "has to be" very large you need to be very sure some other company can't see a way to launch something simpler (faster) and  just incrementally respond to their end user feedback.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-8568296181771161278?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/8568296181771161278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=8568296181771161278' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8568296181771161278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/8568296181771161278'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/09/incremental-internet-product-launch.html' title='Incremental Internet Product Launch - responding to feedback over predicting needs'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-7883773794613159687</id><published>2009-04-22T17:58:00.003+01:00</published><updated>2009-04-22T18:13:10.622+01:00</updated><title type='text'>Test Code Is Just Code</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Test Code is Just Code&lt;/span&gt;
Some Anti-Patterns to avoid and some techniques for making sure Test Code doesn't slow you down

&lt;span style="font-weight: bold;"&gt;Maintaining Test Code can become Costly&lt;/span&gt;
One of the key objections to Test Driven Development is that the tests will become a barrier to change; that the maintenance and reshaping of the tests needed as new business requirements come into play will start to become prohibitively expensive. In fact there is evidence that this indeed happens, over time many Agile teams spend more and more time fixing tests that have been broken by new requirements as opposed to working on those new requirements. I've seen too many teams in exactly this situation and it usually due to difficult to understand test code that is hard and costly to maintain.

&lt;span style="font-weight: bold;"&gt;We already know how to keep code in good shape&lt;/span&gt;
This problem has, however, been solved and many high performing teams are able to introduce new functionality or make rapid changes to existing code without needing to start &lt;a href="http://iancartwright.com/blog/2008/08/dont-delete-unit-tests-even-if-you-have.html"&gt;deleting tests&lt;/a&gt; or abandon the XP principle of TDD (Test Driven Development). The key is that they behave towards test code as they do any other code, Test Code is Just Code. All the things we have learnt about keeping production code in shape through high discipline, techniques like refactoring, and the use of patterns all apply to test code. And yes, sometimes this even means writing tests to make sure our test code behaves as expected.

&lt;span style="font-weight: bold;"&gt;Common Traps&lt;/span&gt;
I want to say more on why people seem to treat test code differently later but first I want to describe some of the common traps that I've seen people fall into and some ways of avoiding them. Here are five I see again and again, they can cause a lot of pain and I've met developers who have been put off TDD by falling into them.

&lt;span style="font-weight: bold;"&gt;Cut &amp;amp; Paste&lt;/span&gt;
The first one is cut &amp;amp; paste, for some reason when it comes to unit tests people suddenly start cutting and pasting all over the place. Suddenly you find a file with 20 tests each of which repeats exactly the same few lines of code. I don't think I need to describe why this is bad and I expect we've all seen the outcome: at some point later those tests all start breaking at the same time, if we are unlucky a few tweaks have happened to the cut &amp;amp; pasted code in each so we spend a lot of effort figuring out how to make each one pass again. There is no rule that says we can't have methods in test fixtures so ExtractMethod still applies, and using SetUp sensibly often helps.  The same rational for avoiding cut &amp;amp; paste and the same solutions we know from production code apply to test code.

&lt;span style="font-weight: bold;"&gt;Poor Encapsulation&lt;/span&gt;
The classic example of a failure of encapsulation in test code is when you start to see database specific code creeping into every test, perhaps you have "Naked SQL" in your tests? I'm going to focus on the database here but the same ideas apply to other areas as well, for instance security, auditing and messaging.

The pain this trap causes often shows itself at the worse time when you are optimising the database or trying sort out a referential integrity issues, suddenly the tests become a millstone around your neck and start breaking on a very frequent basis — because of Cut &amp;amp; Paste this can often be a very large number of tests. I've seen teams avoid addressing things like foreign keys in the database because it has become too difficult to get the test code to create and delete things in an appropriate order. 

The solution is to introduce proper encapsulation around the test code that looks after the database. The &lt;a href="http://sarahtaraporewalla.com/thoughts/testing/using-builders-in-tests/"&gt;builder&lt;/a&gt; and fluent interface patterns are very useful here, if we can hide the implementation and separate concerns we can also often open to door to more advanced testing techniques. Of course you'll want to make sure that builder code is properly tested, that it really puts things into the database in the order you expect and deletes them properly as well, but that should be work you only need to do once.

&lt;span style="font-weight: bold;"&gt;Bloated SetUp&lt;/span&gt;
The next example is Bloated Setup, hopefully the name is self evident, you end up with a SetUp method in your test fixture that becomes huge. As with any bloated method it becomes code that is hard to understand and error prone. Perhaps changing the order of calls in SetUp fixes one test but causes 5 others to fail and the reasons are just not obvious? If you've seen this then you are probably suffering from bloated SetUp.

This pain point has two main causes, the first is poor coding practice such as described above, and the second is the structure of the production code itself. It is often dependencies within the production code that force us to do too much work in SetUp, we have to create all the things each class under test requires and in turn their dependencies as well. The pain grows as SetUp code then gets Cut &amp;amp; Pasted into other test fixtures.

As opposed to the first two examples the solution here lies not just into our approach to the test code, we also need to look at the way the production code is structured. The first step is to make sure we use a well understood pattern such as DI (Dependency Injection) to express and understand the dependencies in the production code. The second step is to make use of stubbing or mocking techniques in our test code so that we can isolate the class under test and exercise it without needing to instantiate the whole tree of dependencies. A lot has been written on mocking and DI already, some of the best articles are &lt;a href="http://www.martinfowler.com/articles/mocksArentStubs.html"&gt;here&lt;/a&gt; &amp;amp; &lt;a href="http://martinfowler.com/articles/injection.html"&gt;here&lt;/a&gt;. Mocking has its own traps not least of which is only having the real wire up of all the components done for the first time in production (more later) but used well it is an invaluable technique for keeping test code easy to maintain and understand.

&lt;span style="font-weight: bold;"&gt;Too hard to write the Test&lt;/span&gt;
The previous pain point described a situation where the solution lay in looking at both our test code and the production code, this one is about testing pain that comes entirely from the production code. Finding a test hard to write can tell us a lot about the production code and a pain point people often refer to is "it's just too hard to write a test for that". My view on this is that code that is hard to test is poorly written code. To write a new unit test, and hence make a change to make it pass, the code we are testing needs to be easy to read and understand. If we can't work out which class to write the test against that means we have poor separation of concerns or a lack of coherence in our production code. If at first you can't write the new test then refactor the production code until you can, it's the production code that is at fault and not the TDD technique.

&lt;span style="font-weight: bold;"&gt;Code Integration Test Pain&lt;/span&gt;
The last example I want to talk about is integration testing. As this is such an overloaded term I first need to describe by what I mean by code level integration testing. In essence it is making sure that when we wire up all the components in our solution they behave as expected together. If we've used patterns like DI, a DI framework such as Spring and a test technique such as Mocking then we need to make sure we are fully testing the wired up solution as well, unfortunately this can get forgotten. Often we end up with a special test wire-up class or a Bloated SetUp doing the wire-up job, either way there is a bug waiting to happen if the production wire-up code never gets called until actual deployment.

In essence SetUp for our code integration tests ought to be calling the production wire-up code. I often hear that this is impossible because the production wire-up code is hard wired to a specific environment. If that is the case the team is probably heading for trouble, too many teams don't spend enough time thinking about how to handle configuration for multiple environments such as Dev, QA and Production. If you can solve that problem first then exercising the full production wire-up code should not be an issue, just point it at the appropriate environmental configuration.

&lt;span style="font-weight: bold;"&gt;Why do people treat test code differently?&lt;/span&gt;
Test Code is only throw away code if the production code is throw away.

I think people often think of test code as throw away, that once in production it becomes superfluous and so there is little use devoting time to keeping it in good shape. When people say this to me I like to ask if they plan on ever changing the production code or doing another release, the answer is always yes. Test Code is an intrinsic part of the solution, like the launch tower for a rocket it provides an essential stepping stone in the solution and one that it's worth expending time and effort to keep in good shape.

There are very few engineering disciplines where regular monitoring and testing do not form part of ongoing maintenance activities, in fact I can't think of a single one. We are lucky with software because if we look after our tests they are a very low cost solution to making sure things can be evolved with minimal risk. We ought to treat testing as an intrinsic part of software engineering instead of a one off 'gate keeper' activity or a problem for the QA team to solve . Test Code is as much a part of a modern software solutions as the production code itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-7883773794613159687?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/7883773794613159687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=7883773794613159687' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/7883773794613159687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/7883773794613159687'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/04/test-code-is-just-code.html' title='Test Code Is Just Code'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-6886566889178225704</id><published>2009-02-25T12:25:00.002Z</published><updated>2009-02-25T12:30:30.998Z</updated><title type='text'>Words of wisdom from Ron Jeffries</title><content type='html'>Via &lt;a href="http://patricklogan.blogspot.com/2009/02/grade-incomplete.html"&gt;Patrick Logan&lt;/a&gt;

Think you are doing Scrum and/or XP? Then read &lt;a href="http://xprogramming.com/blog/2009/01/30/context-my-foot/"&gt;this&lt;/a&gt;.

My take away is that yet again people want to latch on to a silver bullet instead of working out what is really wrong with their organisation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-6886566889178225704?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/6886566889178225704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=6886566889178225704' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6886566889178225704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6886566889178225704'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/02/words-of-wisdom-from-ron-jeffries.html' title='Words of wisdom from Ron Jeffries'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-6459247927404721087</id><published>2009-02-25T09:25:00.004Z</published><updated>2009-02-25T12:11:11.021Z</updated><title type='text'>Acceptance Testing Silverlight with white</title><content type='html'>I've used &lt;a href="http://www.codeplex.com/white"&gt;White&lt;/a&gt; to acceptance test WPF applications a few times so was pleased to discover it just works with Silverlight. It seems that IE passes the usability api calls into the contained Silverlight app so to an outside caller the app just looks like extra controls within IE.

There were a few things I had to make sure were in place though

1. You need to make sure you start IE in a way White can 'find', so if you just pass the URL as the executable name IE (or your default browser) will start but White will not then be able to find that application and window. Obviously you'll need to alter the values to match your install and set up.

&lt;span style="font-family:courier new;"&gt;const string internetExplorer = @"C:\Program Files\Internet Explorer\IEXPLORE.EXE";&lt;/span&gt;
&lt;span style="font-family:courier new;"&gt;const string url = "http://localhost:1138/";&lt;/span&gt;
&lt;span style="font-family:courier new;"&gt;const string windowTitle = "silverlightApp - Microsoft Internet Explorer";&lt;/span&gt;

&lt;span style="font-family:courier new;"&gt;var ie = new ProcessStartInfo(internetExplorer, url);&lt;/span&gt;
&lt;span style="font-family:courier new;"&gt;var application = Core.Application.Launch(ie);&lt;/span&gt;
&lt;span style="font-family:courier new;"&gt;var window = application.GetWindow(windowTitle, Core.Factory.InitializeOption.NoCache);&lt;/span&gt;

2. Make sure you set the automation name property.

&lt;span style="font-family:courier new;"&gt;
&amp;#60TextBox Name="textBox1" Text="some text" AutomationProperties.Name="textbox"/&amp;#62&lt;/span&gt;

3. Use the same Name when you try to find the control.

&lt;span style="font-family:courier new;"&gt;var textBox = window.Get&lt;textbox&gt;("textbox");&lt;/textbox&gt;&lt;/span&gt;

That is it. You'll need to make sure the names you use don't clash with the names of the IE controls, I'd suggest using GUI Spy for that but I've not been able to find a working link to download that tool for about 6 months now.

I'm using IE version 6.0 and Silverlight 2. I've not tried this with any other browsers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-6459247927404721087?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/6459247927404721087/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=6459247927404721087' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6459247927404721087'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6459247927404721087'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/02/acceptance-testing-silverlight-with.html' title='Acceptance Testing Silverlight with white'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-9210534029540888024</id><published>2009-02-17T09:35:00.003Z</published><updated>2009-02-17T09:59:28.640Z</updated><title type='text'>The Tyranny of corporate licences</title><content type='html'>I often hear enterprise architects say "we can use product X as it is free", as I know X is actually quite pricey I ask "wow, XYZ is giving you X for free?" To which they reply "No, we have a corporate license and that comes out of someone elses budget"

So this is a special kind of free which I'd not come across before, one that actually means "free, as in we don't care about the cost to the company as a whole". This sort of individualism is revealed as doubly corrosive when you talk to the developers who find product X is way more complex than they need and actually increases development costs by requiring specialist skills and damaging developer productivity.

This disconnect between people who actually have to use software and those who make the purchasing decisions is not at all unusual. The fact there is a corporate licenses leads to some nasty behaviors as well, it is argued that the software is  "free" when someone raises cost as an objection and it is argued as "highly expensive" when someone questions how much value it really adds (i.e. an erroneous equation of expensive and high ROI).

The other things that gets thrown into the mix is standards - I just ask people go and look if the standards are being driven and defined by companies that make big complex enterprise software products. They can then draw their conclusion on how much value those particular standards have and whether they truly help avoid lock in.

All of these things allow an easy defense for those purchasers who failed to exercise due diligence in their decision and create a a situation that is almost impossible for the people who have to use those products day-to-day to challenge. I suggest projects that use a license pay a proportion of its costs and any loss (or gain) the developers see is carefully tracked.

Before you comment you may want to check that your friendly enterprise software vendor hasn't placed you under a &lt;a href="http://blog.softwareinsider.org/2009/01/27/tuesdays-tip-nows-the-time-to-remove-gag-rule-clauses-in-your-software-contracts/"&gt;gagging order&lt;/a&gt;, no really, I'm not joking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-9210534029540888024?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/9210534029540888024/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=9210534029540888024' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9210534029540888024'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9210534029540888024'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/02/tyranny-of-corporate-licences.html' title='The Tyranny of corporate licences'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-9017481066037400871</id><published>2009-02-03T15:14:00.003Z</published><updated>2009-02-03T15:19:29.978Z</updated><title type='text'>problems with ./configure and make on osx</title><content type='html'>I've been trying out various AMQP implementations, most still require a build from source on OSX.

&lt;span style="font-family: times new roman;"&gt;So the usual&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;

./configure&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;
make&lt;/span&gt;

I'm sure others have seen this error before

&lt;span style="font-family: courier new;"&gt;/bin/sh: line 0: cd: someDirNameHere: No such file or directory&lt;/span&gt;
&lt;pre&gt;&lt;span style="font-family: times new roman;"&gt;More of a reminder for me than anything else, the solution is usually&lt;/span&gt;

&lt;span style="font-family: courier new;"&gt;export CONFIG_SHELL=/bin/bash&lt;/span&gt;
&lt;span style="font-family: courier new;"&gt;./configure&lt;/span&gt;
&lt;span style="font-family: courier new;"&gt;make&lt;/span&gt;



&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-9017481066037400871?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/9017481066037400871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=9017481066037400871' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9017481066037400871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9017481066037400871'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/02/problems-with-configure-and-make-on-osx.html' title='problems with ./configure and make on osx'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-2636555933831729493</id><published>2009-01-05T09:43:00.001Z</published><updated>2009-01-05T09:48:00.661Z</updated><title type='text'>Five kinds of technical debt</title><content type='html'>&lt;a href="http://www.martinfowler.com/"&gt;Martin&lt;/a&gt; has described a concept called &lt;a href="http://www.martinfowler.com/bliki/TechnicalDebt.html"&gt;Technical Debt&lt;/a&gt; and this seems to have entered the vocabulary of our projects. It is a useful term but I worry it is being abused somewhat.

It seems that the term is being used to described a multitude of evils. I've found it very useful to try and categorise the different kinds of debt. In fact I'd go further: I think it is dangerous not to understand the type of Technical Debt we are dealing with and, even more importantly, what the root cause of the Debt was (or continues to be).

The other thing I've seen with technical debt is that can be transparent or opaque. Transparent debt is 'on the books' so to speak, we incur it knowingly and the team and the client are part of the decision. Opaque debt is off the books, either developers don't realise they are incurring debt or the team decides, for whatever reason, to hide the debt from the client.

Anyway here are five different kinds of "Technical Debt", I'm sure we won't all agree with this list and it certainly isn't meant to be exhaustive but I do think there is merit in better categorising and understanding these issues:

&lt;span style="font-weight: bold;"&gt;Ignore&lt;/span&gt;
Skip doing something we know is really needed. So take a quicker path ignoring, say, the need to build something horizontally scalable. Of course there are sensible ways to do this, such as splitting a story into two parts and playing another part later. Unfortunately this doesn't always happen, instead developers sometimes take a unilateral decision to ignore a requirement and hence incur debt that is not visible to the team as a whole (at least not until later). I've heard people use 'yagni' to justify this behaviour.

&lt;span style="font-weight: bold;"&gt;Get Surprised&lt;/span&gt;
Your client comes up with some new requirement that means a change in direction we cannot easily accommodate with existing code. This often shows itself as a 'code base of two halves': pre and post change of direction. We may live with two ways of doing things in the code base and this might never become debt at all, or at least not until some cross cutting requirement forces the issue.

&lt;span style="font-weight: bold;"&gt;Disregard some data&lt;/span&gt;
A classic example of this is build time, or more specifically the time it takes to run all the unit tests. Projects teams often see this time starting to creep up but it gets ignored until such time as it starts to really impact productivity. They are slowly incurring more debt and as is the nature of debt the longer you wait to repay it the tougher it gets.

&lt;span style="font-weight: bold;"&gt;Repeat a mistake&lt;/span&gt;
We incur Technical Debt, often without even knowing it, when we repeat previous mistakes. The one I see the most is Hibernate hiding from the developers that there is a database is involved, to the extent they forget about the cost of a database call. I think this one could also be called 'ignore advice'. Either way we storing up trouble.

&lt;span style="font-weight: bold;"&gt;Guess&lt;/span&gt;
This usually happens in relationship to a non-functional requirement, such as performance, and again it is largely hidden incurrence of debt. Basically we just guess that something will work as expected and don't bother to check , we just assume it will be ok. It is easy to avoid guessing either by using a spike or just a 'back of the envelope' calculation.

And finally...

&lt;span style="font-weight: bold;"&gt;Phantom Debt&lt;/span&gt;
This is 'technical debt' as invoked by a developer who has decided they don't like part of the code base and hence want to rewrite it. Technical Debt certainly sounds better than 'egotistical refactoring session' ;-)

&lt;span style="font-style: italic;"&gt;(Many Thanks to all my colleagues for the internal discussion and feedback I got for the first version of these ideas, no doubt we will continue to argue about this)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-2636555933831729493?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/2636555933831729493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=2636555933831729493' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/2636555933831729493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/2636555933831729493'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2009/01/five-kinds-of-technical-debt.html' title='Five kinds of technical debt'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-6512171817231474543</id><published>2008-08-08T10:56:00.005+01:00</published><updated>2008-08-08T12:03:43.604+01:00</updated><title type='text'>Don't delete unit tests even if you have acceptance test coverage</title><content type='html'>I think it is well understood that the sooner you find a defect the less it will cost. A defect costs the most once into production whether that be a car that needs to be recalled or a software bug  that stops your companies orders being processed.

If you use TDD and CI then you get to find some defects at the development stage where it is clearly a lot cheaper to fix them than finding those same bugs in &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;QA&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;UAT&lt;/span&gt; or worse still in production.

What is really odd then is that people delete unit tests and while there can be &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_2"&gt;legitimate&lt;/span&gt; &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_3"&gt;reasons&lt;/span&gt; such as code no longer being used this is not why most tests get deleted. Some bogus reasons for deleting tests are

a) "They keep failing when I change things" - Well &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_4"&gt;that's&lt;/span&gt; because you just broke some existing &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_5"&gt;functionality&lt;/span&gt;! And guess what: it's a lot cheaper to fix that defect now at development time than once it gets into production. Of course maybe someone else has to deal with production bugs so you could just be selfish and ignore the cost to them and your company and go ahead and delete that failing test.

b) "They are very hard to maintain" - This is more insidious and is a &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_6"&gt;symptom&lt;/span&gt; of something that actually happens quite a lot: developers forget that tests are code too. So for some reason once developers are working on unit testing all those things we know are bad practice are allowed. Unsurprisingly the end result is poorly factored code that is hard to maintain. So should you delete those tests? No! You should do the same as you would when you find hard to maintain code anywhere else in the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;codebase&lt;/span&gt; (that is slowing you down), after all test code is still just code.

c) "I have coverage of that code from acceptance tests" - So what if you have coverage of that code from a higher level acceptance test? Is it &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;ok&lt;/span&gt; to delete the unit test then? I think the answer must still be no and the reason is to do with the cost of finding and fixing a defect. A while ago I was working on a big complex distributed application with great acceptance level tests but relatively poor coverage around the messaging layers. These acceptance tests failed &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_9"&gt;frequently&lt;/span&gt; and developers would spend a lot of time trying to track down the problem. We added units tests to flag those issues much earlier and in a far clearer way. So while we were adding test coverage to code that was already tested we did so to lower the cost of finding and fixing defects.

&lt;span style="font-weight: bold;"&gt;Deleting test code should only be done if it will not increase the cost of finding a defect.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-6512171817231474543?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/6512171817231474543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=6512171817231474543' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6512171817231474543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/6512171817231474543'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/08/dont-delete-unit-tests-even-if-you-have.html' title='Don&apos;t delete unit tests even if you have acceptance test coverage'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-4337563737218256417</id><published>2008-07-24T13:07:00.005+01:00</published><updated>2008-07-25T15:12:31.524+01:00</updated><title type='text'>Patterns for Concurrency over Concurrent Language Features</title><content type='html'>&lt;span style="font-weight: bold;"&gt;The Multi-core Problem&lt;/span&gt;
I continue to see lots of posts and articles about multicore and rightly so, multicore CPUs present some real challenges for the software industry. I think by now most people get it: by default most software is written in a way that can only make use of one CPU. If I buy a new faster CPU that code will run faster, but if I buy an additional CPU or a multi-core CPU then that code will not run faster. This is because it will use only one of the available CPUs, the rest will sit idle, in fact it might even run slower as the individual cores in a multi-core CPU often run slower than a single core CPU.

At the moment this problem is masked to some degree, if you've a 2 CPU machine you probably run enough pieces of software at the same time such that, along with the operating system,  both cores are kept reasonably busy. When we have 8, 16 or 32 cores the problem will be a lot more evident. I'm guessing the issue will really surface when large organizations start to upgrade their desktop machines and find out that instead of running faster, as has always been the case before, the software on those machines actually runs no faster or even slower. Nor a good return on their investment.


&lt;span style="font-weight: bold;"&gt;Evolution or Revolution&lt;/span&gt;
A lot of people are suggesting ways in which we get around this problem, the approaches advocated seem to fit broadly in two categories.

Evolutionary - We need to learn how to use existing threading libraries and features properly from within existing programming languages.

Revolutionary - We need new to start using different programming languages to write our code (i.e. Erlang)

What surprises me is the concentration on programming languages and language level features, I believe both approaches will fail unless we look at the architecture and patterns we use to deliver software.

&lt;span style="font-weight: bold;"&gt;Evolutionary&lt;/span&gt;
Lets look first at the evolutionary approach. Many people are recommending we learn how to use threads, locks, semaphores et al correctly but this is no use if you just try and bolt multithreading onto patterns you would choose for a non-concurrent design. All to often everything is designed and written ignoring the need for concurrency with the hope the bottlenecks can be tackling by adding a 'little bit' of concurrency later.

To give a concrete example consider the observer pattern, all too often this is implemented in a way that means the order that the observers are invoked is fixed. A common approach to 'bolting on' concurrency to a non-concurrent design is to service each observer on a different thread. In fact this often looks likes its working when tested on a single core machine (where the order is deterministic) but soon becomes a source of bugs once it's running on a multi-core machine and the order that the observers are called can change.

Another example is taking work from a queue, why not just have multiple threads consuming from the queue? If you've designed for this you'll be fine, but often that does not happen and it turns out that when the order items are taken from the queue is changed the outcome changes i.e the cancellation of an order overtaking the creation of that order. So the cancel is processed first and discarded (the order does not exist yet!) and then the order creation arrives.....

Concurrency is also something that is hard to compartmentalise and isolate to one part of software, the impact of concurrency tends to be pervasive. So you need to design for multi-core from the beginning and choose an architecture and patterns that create a suitable abstraction around the concurrency required for your problem domain. You may then find someone has already created or documented approaches and patterns that meet your need and you never actually need to start creating your own threads, locks, etc. This is where choosing from existing concurrency libraries comes in.

&lt;span style="font-weight: bold;"&gt;Revolutionary&lt;/span&gt;
So what about the revolutionary approach? I think here it's even clearer we need to change the patterns and architecture we use. I really hope people don't think that by just coding in Erlang, say, that software will magically scale to multiple cores. What's different about Erlang is that it has language features that provide really great support for a particular architectural approach. If you ignore those features and just write sequential code and design the solution as you always have you should not be surprised when it doesn't scale to many cores.

&lt;span style="font-weight: bold;"&gt;Patterns&lt;/span&gt;
So the evolutionary and revolutionary approaches are both valid if we adjust the architectures and the patterns we use to take account of concurrency. If we do that we'll create software that can better scale to multiple cores. If we try and ignore concurrency when making design decisions and instead expect languages or language features to just solve the problem for us we'll be no better off than we are today.
&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;
There is a reason we don't normally write assembly code directly, we have much higher level abstractions and languages. Using threads directly is not that dissimilar to assembly language, it directly exposes the underlying hardware level implementation. Therefore we should not be surprised that using threads directly is difficult and often unproductive, we need higher level abstractions for concurrency. Some languages come with features that make using certain patterns for concurrency much easier to implement than others, Erlang is the obvious example. Other languages have libraries that provide implementations of those same patterns, for example Retlang. Other people have written code that scales incredibly well across multiple CPUs in C and C++ as well. It's not about the language, it's about having a design that takes account of concurrency and then choosing patterns that provide suitable abstractions around it.

&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;
When it comes to software we have multiple patterns available and we try and choose the best ones for the problem at hand. We need to develop and use similar patterns to help use with concurrency and in fact many exist already. Many of the patterns described by http://www.enterpriseintegrationpatterns.com/ play equally well at the software level as they do the message level, many patterns in EDA (i.e. Event Collaboration Patterns) also lend themselves to concurrent solutions. There are plenty of other examples.

Its here at the level of patterns we should be focusing our efforts around solving the multi-core problem and not at the language level.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-4337563737218256417?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/4337563737218256417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=4337563737218256417' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/4337563737218256417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/4337563737218256417'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/07/patterns-for-concurrency-over.html' title='Patterns for Concurrency over Concurrent Language Features'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-4284889044773434861</id><published>2008-07-22T13:16:00.005+01:00</published><updated>2008-07-25T10:26:02.228+01:00</updated><title type='text'>Tools: Enablement or Control</title><content type='html'>I've used a lot of tools during my software development career and it seems they fall primarily into two categories - tools that seek to control and tools that seek to enable.

Enabling Tools
&lt;ul&gt;&lt;li&gt;Concentrate on addressing one key issue rather than the whole development life cycle
&lt;/li&gt;&lt;li&gt;Don't dictate a particular work flow or way of working&lt;/li&gt;&lt;li&gt;Are often written by people who use them&lt;/li&gt;&lt;li&gt;Require a degree of trust between end users (so someone can ignore the agreed approach if they want)
&lt;/li&gt;&lt;li&gt;Tend to be quick and easy to use&lt;/li&gt;&lt;li&gt;Are open and easy to integrate with other tools
&lt;/li&gt;&lt;li&gt;Are adaptable to many situations and ways of working&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;ul&gt;&lt;li&gt;Leading to: The ability to adapt to changing situations and constantly refine process leading to incremental improvements in productivity over time
&lt;/li&gt;&lt;/ul&gt;Controlling Tools
&lt;ul&gt;&lt;li&gt;Come with a built in vision on how a whole series of activities should be done&lt;/li&gt;&lt;li&gt;Dictate work flow and the ordering of activities&lt;/li&gt;&lt;li&gt;Assume no trust between end users (you must follow the tool's defined approach)
&lt;/li&gt;&lt;li&gt;Tend to be complex and hard to use&lt;/li&gt;&lt;li&gt;Only work in one situation, you change to work their way or you spend all your time fighting the tool&lt;/li&gt;&lt;li&gt;Are often closed, proprietary and hard to integrate with other tools
&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Leading to: A lack of flexibility and consequently much frustration when trying to respond to changing circumstances, ultimately this constrains the productivity of the team
&lt;/li&gt;&lt;/ul&gt;Another thing I've noticed over the years is no two projects are alike and no one approach fits every situation. If you don't have trust and good working relationships among your team members then the tools are the least of your worries.

So I prefer enabling tools over controlling tools - I'd much rather trust people and be flexible than constrain both people and the process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-4284889044773434861?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/4284889044773434861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=4284889044773434861' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/4284889044773434861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/4284889044773434861'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/07/tools-enablement-or-control.html' title='Tools: Enablement or Control'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-3401343436371676031</id><published>2008-07-21T19:14:00.004+01:00</published><updated>2008-07-22T12:25:47.628+01:00</updated><title type='text'>Marketing Driven Development</title><content type='html'>Marketing Driven Development is a process where the features and priorities for a new release of software are driven primarily by a marketing department. So a feature is valued more for the ability to create a marketing campaign around it over any usefulness or value that it may have for an end user.

Marketing Driven Development is primarily a reaction to the fear of &lt;a href="http://en.wikipedia.org/wiki/Commoditization"&gt;commoditization&lt;/a&gt;.

It's consequences often include
&lt;ul&gt;&lt;li&gt;Well known bugs (or to use the correct term: sub-optimal feature) remain for many versions as fixing them is never a priority&lt;/li&gt;&lt;li&gt;Usability does not improve as this is rarely seen as valuable by marketing and anyway it was sold as easy to use the first time around
&lt;/li&gt;&lt;li&gt;It tends to be expensive to buy, maintain and customize i.e. marketing driven development does seem to allow the avoidance of &lt;i style="font-style: italic;"&gt;commoditization&lt;/i&gt;&lt;span style="font-style: italic;"&gt;.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-3401343436371676031?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/3401343436371676031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=3401343436371676031' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3401343436371676031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3401343436371676031'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/07/marketing-driven-development.html' title='Marketing Driven Development'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-5298201658743863802</id><published>2008-07-17T19:24:00.002+01:00</published><updated>2008-07-17T20:59:40.875+01:00</updated><title type='text'>.net: A fluent Facade around System.CodeDom</title><content type='html'>I've been doing quite a lot of work with System.CodeDom recently (more on exactly what later) and have frankly found the api quite hard to use.

So I've found myself growing a &lt;a href="http://www.martinfowler.com/bliki/FluentInterface.html"&gt;fluent interface&lt;/a&gt; facade around the api, for example the following code creates a class with one method that always returns the integer four.
&lt;pre&gt;
  [TestFixture]
  public class TestsForWriteUp
  {
      [Test]
      public void testShouldCreateClassWithOneMethod()
      {
          ClassBuilder builder = new ClassBuilder(new ResolveTypeFromAssemblies());
          builder.AddMethod("theMethod", typeof (int)).Number(4).Return();

          Compiler compiler = new Compiler("CodeGenTests.dll");
          ReturnsAnInt dynamicallyBuildCode = compiler.compile&amp;#60;ReturnsAnInt&amp;#62;(builder);
          Assert.AreEqual(4,dynamicallyBuildCode.theMethod());
      }
  }

  public interface ReturnsAnInt
  {
      int theMethod();
  }
&lt;/pre&gt;
So I get hold of a classbuilder, add a method to it, then add the number four and finally return. The reason things look a bit back to front is that the facade is using a stack under the covers, so what I'm really saying is
&lt;ul&gt;&lt;li&gt;Push a statement (a constant integer of value four) on to the stack and then &lt;/li&gt;&lt;li&gt;Pop whatever is at the top of stack and return it.&lt;/li&gt;&lt;/ul&gt;Finally I take the builder and dynamically compile the contents of the builder into an assembly, in this case in a way that gives me back an instance of a particular type so I can immediately start working with. (I can also save the output to a dll if needed)

The new ResolveTypeFromAssemblies() injected into the ClassBuilder allows it to resolve any types that are used, in this case it just checks the loaded assemblies. The "CodeGenTests.dll" passed into the compiler lets it know the generated code depends upon that assembly (i.e. for the interface ReturnsAnInt). 

Actually that first example is not very interesting, how about a slightly more complex example:
&lt;pre&gt;
       [Test]
       public void testShouldCreateAddMethod()
       {
           MethodBuilder methodBuilder = builder.AddMethod("theMethod", typeof (int));
           methodBuilder.DeclareParameter(typeof (int), "a").DeclareParameter(typeof (int), "b");
           methodBuilder.VarUsage("a").VarUsage("b").BinaryOperator(CodeBinaryOperatorType.Add).Return();

           TakesTwoArgs takesTwoArgs = compiler.compile&amp;#60;TakesTwoArgs&amp;#62;(builder);
           Assert.AreEqual(3, takesTwoArgs.theMethod(1, 2));
           Assert.AreEqual(9, takesTwoArgs.theMethod(4, 5));
       }

       public interface TakesTwoArgs
       {
           int theMethod(int a, int b);
       }
&lt;/pre&gt;
Still pretty simple, declare a method, add two parameters, sum them and then return the result. Again note the back-to-front invocation style. This is considerably less code than you'd need to accomplish the same thing using the System.CodeDom classes directly.

The next example shows the use of a field, a constructor with an argument and then how to create an instance using that constructor. Hopefully much more terse than CodeDom but still easy to understand.
&lt;pre&gt;
        [Test]
        public void testShouldCreateConstructorAndField()
        {
            string fieldName = "seed";
            builder.DeclareVariable(typeof (int), fieldName);
            builder.AddConstructor().DeclareParameter(typeof (string), "input")
                .FieldUsage(fieldName).VarUsage("input").UnaryOperator(typeof(int),"Parse").VarAssignment();
            builder.AddMethod("theMethod", typeof (int)).FieldUsage(fieldName).Return();

            Parameter argument = new Parameter(typeof(string),"42");
            ReturnsAnInt returnsAnInt = compiler.compile&amp;#60;ReturnsAnInt&amp;#62;(builder,argument);
            Assert.AreEqual(42, returnsAnInt.theMethod());
        }
&lt;/pre&gt;
Again things look back to front, if you read the line ending in .VarAssignment() from right to left you'll probably find it clearer to see what is happening. The reason for the use of the stack is that it greatly simplifies the underlying code, in effect I'm using &lt;a href="http://en.wikipedia.org/wiki/Reverse_Polish_Notation"&gt;Reverse Polish Notation&lt;/a&gt;

Finally here is the c# version of the code that is compiled:
&lt;pre&gt;
namespace GeneratedCode {
    using System;
    using CodeGen;
    
    
    public class DynamicallyCompiledCode : CodeGen.CompiledBase, CodeGenTests.TestsForWriteUp.ReturnsAnInt, CodeGen.ICompiled {
        
        public int seed;
        
        public DynamicallyCompiledCode(string input) {
            this.seed = int.Parse(input);
        }
        
        public virtual int theMethod() {
            return this.seed;
        }
    }
}
&lt;/pre&gt;
I'd be interested in comments, is this something people would find useful? It's grown out of a formula compiler, a dynamic proxy generator and a compiler compiler so is reasonably complete. I'll be posting more about the compiler compiler and how it is using this facade around CodeCom to bootstrap itself soon. (btw I never meant to write the compiler compiler, it just grew out of some work I've been doing around DSL's)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-5298201658743863802?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/5298201658743863802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=5298201658743863802' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5298201658743863802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/5298201658743863802'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/07/net-fluent-facade-around-systemcodedom.html' title='.net: A fluent Facade around System.CodeDom'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-3635274279368953207</id><published>2008-04-23T11:46:00.002+01:00</published><updated>2008-04-23T12:19:12.134+01:00</updated><title type='text'>Cutting the Cost of Change over Just Cutting Cost</title><content type='html'>Many businesses talk about cutting cost, especially at the moment, and IT is often an easy target. The problem with focusing on just cutting cost is that you can increase your future cost of change. 

The most obvious example is when you let people go who are experts in your legacy systems, you certainly cut cost as you are not paying their salary any longer - but at the same time you've just constrained your ability to deliver changes to those legacy systems.

Now if a business has been focused on cutting cost for a while chances are that any initiatives to pull functionality out of a legacy systems (where change might be costly) into newer systems (where, hopefully, changes are cheaper) will not have happened. 

So you've been cutting opex, which funds the ability to change existing systems, and often also cutting capex which funds the creation of new systems or the movement of existing functionality into systems that require less opex.

So the end result of cost cutting is that the cost of change will increase over time, you are removing capability to change systems while at the same time restricting the ability to replace those systems. Eventually you reach a point where cost of change is high enough to mean you can't make the business case for even small changes. At that point you might decide to invest in replacing the legacy systems that have become so expensive to change - but chances are that you no longer understand what those systems do and that it's beyond the capacity of the business to fund replacing them. Even if you can afford to replace them what do you do in the mean time, it might takes months or years to build the replacement.

So what is the alternative? One might be to focus on cutting the cost of change as opposed to just cutting cost. So incrementally invest to move functionality off legacy systems into new systems or to keep software in a reasonable shape in the first place. Consider viewing systems as things that constantly evolve as opposed to things you grow to a certain point and then throw over the fence into support. Think about architectural approaches that reduce coupling and dependencies between systems. These are just suggestions and there are a lot of other things you could do, chances are the people on the ground will have some good idea of where to start.

If you can cut the cost of change that means you can get more done for less money, which &lt;span style="font-style:italic;"&gt;is&lt;/span&gt; cutting your cost. Focus on the cost of change may well be the thing to measure and to drive down over time, as opposed to just cost per se.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-3635274279368953207?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/3635274279368953207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=3635274279368953207' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3635274279368953207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/3635274279368953207'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/04/cutting-cost-of-change-over-just.html' title='Cutting the Cost of Change over Just Cutting Cost'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-1986930862026046614</id><published>2008-04-08T11:45:00.001+01:00</published><updated>2008-04-08T11:48:53.373+01:00</updated><title type='text'>Great post on "Bad Behaviour" in projects</title><content type='html'>&lt;a href="http://www.dancres.org/blitzblog/2008/04/06/bad-behaviour/"&gt;These&lt;/a&gt; all ring very true.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-1986930862026046614?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/1986930862026046614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=1986930862026046614' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/1986930862026046614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/1986930862026046614'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/04/great-post-on-bad-behaviour-in-projects.html' title='Great post on &quot;Bad Behaviour&quot; in projects'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-35609063.post-9192827046102195825</id><published>2008-03-19T10:17:00.005Z</published><updated>2008-03-21T16:17:15.998Z</updated><title type='text'>Will cost per transaction kill your business?</title><content type='html'>Imagine a business, Major Widgets Plc, that makes money by selling things in bulk, they sell a batch of 20,000 widgets on to a high street retailer who then sells the individual units. Each time Major Widgets sells a batch it costs them $1.5 to process that sale, as the overall cost of a batch is $20,000 that is less than 1 tenth of a percent cost overhead, pretty good. As they only sell 1000 batches per month they also process all the orders at the end of month in a legacy batch processing system. They are feeling pretty pleased with themselves.

Now imagine a new business, Overlooked Upstart Inc., who also sell widgets. They do something rather different though, they sell widgets directly to the consumer as individual units. They currently sell only around 10% of the total number of widgets per month of Major Widgets, so instead of 1000 transactions per month this means they have to do 2 million. It actually costs them $0.001 to process each transaction and as they charge only $1 per widget that is actually more cost overhead in percentage terms than Major Widgets. Overlooked Upstart also process each transaction as and when it happens; as their business is spread reasonably over the month that is about 66000 per day or around 47 per minute. They run this system on a couple of commodity blade servers and know they can scale up to a few hundred orders per minute without too much cost. They too are feeling pretty pleased with themselves because.....

...in this hypothetical universe it turns out consumers really like the convenience of buying widgets online directly without the high street retailer taking a cut. Major Widgets sees a downturn, the retailers only want 15,000 widgets per month, this is a problem and if it continues they'll be in serious trouble in a few years time.

So Major Widgets decides to launch online sales themselves and set the goal of having 10% of total widget sales online. The first challenge is that all the orders are currently keyed in by hand at the rate of about 30 per day, there is no electronic way to capture the orders at the required rate of 66000 a day without hiring a few new people(!). The second challenge is that the legacy billing system can only run on the last Monday of the month within a 4 hour time window -  which comes out at around 140 transactions per second (around 130 per second faster than their current system can do). The harsh truth is that they cannot currently compete selling individual widgets due to their high cost per transaction and aged infrastructure.

Ok, not the end of the world, they just need to spend money on a few new systems. But it turns out it's not that simple, they'd been seeing IT as a inconvenient cost for some time and over the past few years have been cutting both operational and capital spending as well outsourcing the support as part of a multiyear contract.  To make things worse all the people who knew how all the systems were tied together have left, the person who wrote the cobol transaction processing system retired and all the web developers have been poached by some company called Overlooked Upstart Inc. 

Major Widgets come to realize that they are best off being taken over by someone who has infrastructure with low cost per transaction and high scalability........

Of course this is a simplified made up example and I'm lucky enough to work with clients who see IT as an integral part of what they do, they are well aware of their legacy systems and what online offerings can require in terms of scaling and cost. 

However I am still amazed to hear of some companies who talk as if the internet is going to go away and that IT is just a cost to be controlled. What are they going to do when someone new moves into their market? This isn't really a new phenomena either, market de-regulation in equity trading led to a massive up turn in volumes that put some small brokerages out of business. I'm sure there are other examples as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/35609063-9192827046102195825?l=blog.iancartwright.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.iancartwright.com/feeds/9192827046102195825/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=35609063&amp;postID=9192827046102195825' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9192827046102195825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/35609063/posts/default/9192827046102195825'/><link rel='alternate' type='text/html' href='http://blog.iancartwright.com/2008/03/will-cost-per-transaction-kill-your.html' title='Will cost per transaction kill your business?'/><author><name>Ian Cartwright</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
