<?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/'><id>tag:blogger.com,1999:blog-13321238</id><updated>2008-05-20T09:01:55.605+10:00</updated><title type='text'>Richard's Braindump</title><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default?start-index=26&amp;max-results=25'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>356</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-13321238.post-5774158840179562197</id><published>2008-05-20T09:01:00.001+10:00</published><updated>2008-05-20T09:01:55.757+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='management'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Agile KPI's</title><content type='html'>&lt;p&gt;I was recently asked by Aaron to do a blog post about KPI's when using Scrum (thanks for the suggestion!).&amp;nbsp; The basic idea being that many companies want to have role level KPI's, but how do you do that with Scrum, especially when Scrum seems to be about the performance of the team.&amp;nbsp; I say "seems" because it's often how people who are new to Scrum will look at things, which is unfortunate as they miss the point that scrum is concerned with not just the team's results but with the individuals in that team as well.&lt;/p&gt; &lt;p&gt;So, what KPI's should be considered for members of a scrum team?&amp;nbsp; At a team level it's easy enough - delivery of working software that provides business value.&amp;nbsp; But for individuals, what do we do?&lt;/p&gt; &lt;p&gt;The first instinct might be do look for something that's easy to measure such as taking on a certain number of hours worth of tasks per sprint, or being present on time at the daily stand up meetings, but these would be poor KPI's as they measure attendance and allocation of tasks, and not delivery of business value by individuals.&amp;nbsp; Instead I'd suggest that what we really want to gauge is how much overall value people are adding to the business and the team - and that value isn't always tied directly to their work artifacts/outputs.&lt;/p&gt; &lt;p&gt;Here's some suggestions for KPI's you could apply to individuals in a scrum team:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Positive Team Involvement&lt;/li&gt; &lt;li&gt;Mentoring&lt;/li&gt; &lt;li&gt;Knowledge Sharing&lt;/li&gt; &lt;li&gt;Proactive Alerting of Impediments&lt;/li&gt; &lt;li&gt;Working According to Team Standards (e.g. TDD, style &amp;amp; naming, code comments &amp;amp; documentation, regular check-ins, etc)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;In case it's not implicitly obvious, I'd suggest that individuals in the team would have different expectations on their KPI measures.&amp;nbsp; For example, the more experienced team members would be expected to spend more time mentoring than the juniors.&amp;nbsp; The team members with greater domain knowledge would be expected to spend more time sharing that knowledge than those who are new to the problem domain, etc.&lt;/p&gt; &lt;p&gt;So then how do we then measure these factors?&amp;nbsp; One method might be through peer feedback, others might use independent observation, others might try something completely different.&amp;nbsp; My suggestion would be a mix of observation and feedback - the scrum master needs to observe the team dynamics and behaviours on an ongoing basis, however they often miss things that the quiet achievers do during the day, so having a peer review process is a good thing to add to the mix.&amp;nbsp; You might want to get feedback from the team at the end of each sprint or you might choose to have a 6 monthly anonymous feedback mechanism and use that with a mix of judgement to work things out.&amp;nbsp; Whatever you choose it will end up being a judgement call as to wether people met their KPI's or not.&lt;/p&gt; &lt;p&gt;The most important thing to remember is that in the same way that the team is measured on results, individual KPI's should be trying to measure the individuals value, not their effort or their time spent in the office.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/05/agile-kpi.html' title='Agile KPI&amp;#39;s'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=5774158840179562197' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/5774158840179562197/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5774158840179562197'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5774158840179562197'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2996122297004461182</id><published>2008-05-13T21:19:00.001+10:00</published><updated>2008-05-13T21:19:51.237+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='microsoft'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Unity vs Castle Windsor</title><content type='html'>&lt;p&gt;Recently I did a post on breaking dependencies which used the &lt;a href="http://www.castleproject.org/container/index.html" target="_blank"&gt;Castle Windsor container&lt;/a&gt; as the Inversion of Control container.&amp;nbsp; I thought I'd also have a look at doing the same thing with the &lt;a href="http://www.codeplex.com/unity" target="_blank"&gt;Unity container&lt;/a&gt; from Microsoft's Patterns &amp;amp; Practices team, which was version 1.0'ed last month.&lt;/p&gt; &lt;p&gt;The good news?&amp;nbsp; It's pretty much just some slight syntax changes and that's about it.&amp;nbsp; The only problem I found was that after installing the Unity help file for VS2008 my Visual Studio help is now broken and now I have to go and repair my Visual Studio installation.&amp;nbsp; I'd suggest sticking to the CHM file instead.&lt;/p&gt; &lt;p&gt;Here's the only differences between the two applications:&lt;/p&gt; &lt;h4&gt;1. References (duh!)&lt;/h4&gt; &lt;h5&gt;Castle Windsor&lt;/h5&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; Castle.Windsor;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; Castle.Core.Resource;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; Castle.Windsor.Configuration.Interpreters;&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;Unity&lt;/h5&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Configuration;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; Microsoft.Practices.Unity;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; Microsoft.Practices.Unity.Configuration;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;2. Instantiating the Container&lt;/h4&gt;&lt;br /&gt;&lt;h5&gt;Castle Windsor&lt;/h5&gt;&lt;pre class="codeblock"&gt;            &lt;span style="color: rgb(43,145,175)"&gt;IWindsorContainer&lt;/span&gt; container = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;WindsorContainer&lt;/span&gt;(&lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;XmlInterpreter&lt;/span&gt;());&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;Unity&lt;/h5&gt;&lt;pre class="codeblock"&gt;            &lt;span style="color: rgb(43,145,175)"&gt;IUnityContainer&lt;/span&gt; container = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;UnityContainer&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;UnityConfigurationSection&lt;/span&gt; section = (&lt;span style="color: rgb(43,145,175)"&gt;UnityConfigurationSection&lt;/span&gt;)&lt;span style="color: rgb(43,145,175)"&gt;ConfigurationManager&lt;/span&gt;.GetSection(&lt;span style="color: rgb(163,21,21)"&gt;"unity"&lt;/span&gt;);&lt;br /&gt;            section.Containers.Default.Configure(container);&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;3. The .config files&lt;/h4&gt;&lt;br /&gt;&lt;h5&gt;Castle Windsor&lt;/h5&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;xml&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;version&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;1.0&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;encoding&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;utf-8&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; ?&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configuration&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;section&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;castle&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;castle&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;components&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;prompt.input&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesPrompter.IInputControl, SalesPrompter&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesPrompter.PromptInputControl, SalesPrompter&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;console.display&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.IMessageDisplay, SalesTax&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.ConsoleMessageDisplay, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;input.parser&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.IInputParser, SalesTax&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.InputParser, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;salelinefactory&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.ISaleLineFactory, SalesTax&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.SaleLineFactory, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;        &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;components&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;castle&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configuration&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;Unity&lt;/h5&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;xml&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;version&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;1.0&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;encoding&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;utf-8&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; ?&amp;gt;&lt;br /&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configuration&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;section&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;unity&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;  &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;unity&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;typeAliases&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;typeAliases&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;containers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;container&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;                &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;types&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;                    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesPrompter.IInputControl, SalesPrompter&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;br /&gt;                        &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;mapTo&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesPrompter.PromptInputControl, SalesPrompter&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;/&amp;gt;&lt;br /&gt;                    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.IMessageDisplay, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;br /&gt;                        &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;mapTo&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.ConsoleMessageDisplay, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;/&amp;gt;&lt;br /&gt;                    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.IInputParser, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;br /&gt;                        &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;mapTo&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.InputParser, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;/&amp;gt;&lt;br /&gt;                    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.ISaleLineFactory, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;br /&gt;                        &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;mapTo&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;SalesTax.SaleLineFactory, SalesTax&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;/&amp;gt;&lt;br /&gt;                &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;types&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;container&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;containers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;unity&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configuration&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Note that there was nothing different in the resolving of references.&amp;nbsp; Both containers have a Resolve&amp;lt;T&amp;gt;() method that does exactly the same thing.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So, how easy is that? :-)&amp;nbsp; The P&amp;amp;P team have done a pretty good job with this container.&amp;nbsp; Admittedly I haven't really looked into some of the more advanced options, but it looks quite good and being from the P&amp;amp;P team it's likely to get more traction that the Castle Windsor container in the corporate development world.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/05/unity-vs-castle-windsor.html' title='Unity vs Castle Windsor'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=2996122297004461182' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/2996122297004461182/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2996122297004461182'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2996122297004461182'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7841774969302878737</id><published>2008-05-07T13:05:00.001+10:00</published><updated>2008-05-07T13:05:30.164+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Breaking Dependencies using Dependency Injection</title><content type='html'>&lt;p&gt;Let's assume you've done some reading recently and heard talk of this Loosely Coupled Architecture thing and how it's meant to help keep an application maintainable, make it more readily changeable and will assist in the creation of unit tests .&amp;nbsp;&amp;nbsp; You know that it's the right way to go and you can see all the benefits of it. You even think "I should do this on that application at work", but every time you look at the code at work and wonder what space monkeys were hired to write the code in the first place and you think about how tightly bound everything is it all starts to look too hard, so you give up and get back to working with things as they because the problem seems insurmountable.&lt;/p&gt; &lt;p&gt;Now a loosely coupled architecture is at it's heart just another way of saying object oriented design.&amp;nbsp; I'm sure you'll remember from when you learnt all that Object Oriented Programming stuff that there are some basic principle for OO Programming, namely:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Abstraction, Encapsulation, Polymorphism and Inheritance&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;And there are also some &lt;a href="http://www.surfscranton.com/Architecture/ObjectOrientedDesignPrinciples.htm" target="_blank"&gt;OO Design principles&lt;/a&gt; that sit alongside the OOP principles:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Encapsulate what changes&lt;/p&gt; &lt;p&gt;Favour composition over inheritance&lt;/p&gt; &lt;p&gt;Program to interfaces not implementations&lt;/p&gt; &lt;p&gt;Depend on abstractions, not concrete classes (dependency inversion)&lt;/p&gt; &lt;p&gt;A class should have only one reason to change (single responsibility)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Your work code and the "this problem is too big" feeling doesn't have to be the case.&amp;nbsp; It's just an indication that you've got legacy code and that it isn't well designed.&amp;nbsp; The good news is that by following some basic steps you can refactor a legacy, tightly-bound application into one with a loosely coupled architecture, though it will take some time (think many months, not weeks).&lt;/p&gt; &lt;p&gt;There are two basic methods for doing this.&amp;nbsp; The first is Extract &amp;amp; Override (see &lt;a href="http://weblogs.asp.net/rosherove/articles/DependencyIssues.aspx" target="_blank"&gt;Roy Osherove's blog entry&lt;/a&gt;) and the other is Dependency Injection.&amp;nbsp; This is what I'll focus on here.&lt;/p&gt; &lt;p&gt;First, let's have a look at some code from a simple sales application:&lt;/p&gt;&lt;pre class="codeblock"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Sale&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt;&amp;gt; saleLines = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt;&amp;gt;();&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;decimal&lt;/span&gt; totalTax;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;decimal&lt;/span&gt; totalValue;&lt;br&gt;        ...&lt;br&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;bool&lt;/span&gt; Add(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; inputLine)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt; saleLine;&lt;br /&gt;&lt;br /&gt;            saleLine = &lt;span style="color: rgb(43,145,175)"&gt;InputParser&lt;/span&gt;.ProcessInput(inputLine);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (saleLine == &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;)&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;false&lt;/span&gt;;&lt;br&gt;            saleLines.Add(saleLine); &lt;br&gt;            ...&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Here we see that a sale has multiple sale lines associated with it.&amp;nbsp; Pretty normal, and something you'll see in many code bases the world over, but we're breaking some of the OOD rules - we're programming to implementations not interfaces and we're using a static method (on the InputParser) which means we're tightly coupled between the Sale object and the SaleLine object.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;We're also doing something else that's a no-no.&amp;nbsp; The Add method is coordinating a whole lot of work to convert an input string into a SaleLine object before adding it to the saleLines collection, whichmeans the class does more than one thing.&amp;nbsp; &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's have a quick look at the ProcessInput method on the InputParser class as well&lt;/p&gt;&lt;pre class="codeblock"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt; ProcessInput(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; input)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt; saleLine;&lt;br /&gt;            ...&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// create the sale line&lt;br /&gt;&lt;/span&gt;            saleLine = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt;(quantity, name, price);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; saleLine;&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;What we have here is another tight coupling.&amp;nbsp; We're creating saleline objects from within the method (the new SaleLine() call) which means we have a tight dependency between the saleline class and the InputParser class.&amp;nbsp; If we wanted to change the SaleLine to some other class, we'd have some refactoring to do.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So let's make some changes...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Lets start by creating interfaces for all of our concrete classes, for example:&lt;/p&gt;&lt;pre class="codeblock"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Sale&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;ISale&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt;&amp;gt; saleLines = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt;&amp;gt;();&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now, if our definition of what a sale line is changes we don't have to make any changes in the Sale object.&amp;nbsp; Nice.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's also change the input parser so it doesn't create Sale objects directly.&amp;nbsp; We should do that through the use of a &lt;a href="http://en.wikipedia.org/wiki/Factory_method_pattern" target="_blank"&gt;factory pattern&lt;/a&gt; implementation, such as the following:&lt;/p&gt;&lt;pre class="codeblock"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SaleLineFactory&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;ISaleLineFactory&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt; GetNewSaleLine(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; lineQuantity, &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; name, &lt;span style="color: rgb(0,0,255)"&gt;decimal&lt;/span&gt; unitPrice)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt;(lineQuantity, name, unitPrice);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;And in our InputParser we change the code as follows:&lt;/p&gt;&lt;pre class="codeblock"&gt;            saleLine = saleLineFactory.GetNewSaleLine(quantity, productName, price);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Good. So now we're not actually creating the SaleLine object directly in our code.&amp;nbsp; This is a good thing as it breaks the tight coupling between the classes, but doesn't it just move the dependency from the SaleLine class to the SaleLineFactory class?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;It would if we just added saleLineFactory = new SaleLineFactory() to the InputParser, but not if we pass an instance of the saleLineFactory class to the InputParser.&amp;nbsp; This is the &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection" target="_blank"&gt;Dependency Injection&lt;/a&gt; pattern at work.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now, while we're working in the InputParser class we also have the opportunity to fix another tight coupling.&amp;nbsp; That between the Sale class and the InputParser.&amp;nbsp; Let's change the input parser so that we're no longer a static class, but rather a normal class (in a real world situation this could be complex) and let's pass to the ProcessInput method the Sale we want the new line to be added to.&amp;nbsp; We can then change the sale object so that it's Add method just adds ISaleLine objects instead of being passed a string and then calling to the parser.&amp;nbsp; Something like this:&lt;/p&gt;&lt;pre class="codeblock"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Sale&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;ISale&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt;&amp;gt; saleLines = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt;&amp;gt;();&lt;br&gt;        ...&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;bool&lt;/span&gt; Add(&lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt; saleLine)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (saleLine == &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;)&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;false&lt;/span&gt;;&lt;br /&gt;            saleLines.Add(saleLine);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;InputParser&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;IInputParser &lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;ISaleLineFactory&lt;/span&gt; saleLineFactory;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; InputParser(&lt;span style="color: rgb(43,145,175)"&gt;ISaleLineFactory&lt;/span&gt; saleLineFactory)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.saleLineFactory = saleLineFactory;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ProcessInput(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; input, &lt;span style="color: rgb(43,145,175)"&gt;ISale&lt;/span&gt; sale)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ISaleLine&lt;/span&gt; saleLine;&lt;br /&gt;            ...&lt;br /&gt;            saleLine = saleLineFactory.GetNewSaleLine(quantity, name, price);&lt;br /&gt;            sale.Add(saleLine);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt;;&lt;br /&gt;        }&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Much cleaner.&amp;nbsp; We now have no hard dependencies between our classes and we are programming to interfaces not concrete classes.&amp;nbsp; &lt;/p&gt;&lt;br /&gt;&lt;p&gt;But we're not done yet.&amp;nbsp; We still have to create a saleLineFactory class, an InputParser class, a Sale object, etc for us to be able to do any real work.&amp;nbsp; Where/when/how do we do all of this?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This is where an Inversion of Control (IoC) container can come in handy.&amp;nbsp; We could just create these objects manually at application startup, but using an IoC container is much easier.&amp;nbsp; An IoC container lets you map interfaces to classes, i.e. to say that InterfaceX is implemented by ClassY and the container will resolve these dependencies for you.&amp;nbsp; Most IoC containers let you do a whole lot more as well (Aspects for example).&amp;nbsp; For this example we'll use the &lt;a href="http://www.castleproject.org/container/index.html" target="_blank"&gt;Castle Windsor&lt;/a&gt; container to control our object creation for us:&lt;/p&gt;&lt;pre class="codeblock"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;[] args)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;IWindsorContainer&lt;/span&gt; container = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;WindsorContainer&lt;/span&gt;(&lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;XmlInterpreter&lt;/span&gt;());&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;IInputControl&lt;/span&gt; inputter = container.Resolve&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;IInputControl&lt;/span&gt;&amp;gt;();&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;IInputParser&lt;/span&gt; parser = container.Resolve&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;IInputParser&lt;/span&gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ISale&lt;/span&gt; sale;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; input;&lt;br /&gt;&lt;br /&gt;            sale = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Sale&lt;/span&gt;();&lt;br /&gt;            input = inputter.GetInput();&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;while&lt;/span&gt; (!inputter.EOF())&lt;br /&gt;            {&lt;br /&gt;                parser.ProcessInput(input, sale);&lt;br /&gt;                input = inputter.GetInput();&lt;br /&gt;            }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;And here's the configuration file:&lt;/p&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;section&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;name&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;castle&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;configSections&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;castle&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;components&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;prompt.input&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.IInputControl, Sales&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.PromptInputControl, Sales&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;input.parser&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.IInputParser, Sales&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.InputParser, Sales&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;component&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;id&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;salelinefactory&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;service&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.ISaleLineFactory, Sales&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;type&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Sales.SaleLineFactory, Sales&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;        &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;components&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;castle&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now the astute among you will immediately ask why I didn't put the Sale or SaleLine objects into the container?&amp;nbsp; The answer is fairly simple - they're not good candidates for objects to place in the container.&amp;nbsp; You want to put things in the container that are service classes, not data classes.&amp;nbsp; The definition of a sale and saleline will be fairly static, plus the use of the ISaleLineFactory lets us control which ISaleLine implementation we want to use. it's also why the factory is one of the classes the IoC container is aware of - a factory class is a service that the application uses.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You might also ask, why am I using sale = new Sale() in my sample code?&amp;nbsp; Well, I could have written an ISaleFactory and used that instead (and in a real world system it's what I'd recommend) but this is a sample app, so I beg your forgiveness :-)&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Finally, the really astute amongst you will notice that I never call Resolve for the ISaleLineFactory interface and I don't provide parameters for the IInputParser class when it's resolved.&amp;nbsp; So how does it get into the input parser?&amp;nbsp; Well, the cool thing about the Windsor container is that it will automatically scan the constructors for classes it's aware of, and if that constructor has a dependency (i.e. the ISaleLineFactory interface) then the container will automatically instantiate an object for that interface and pass it through.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This makes managing dependencies and instantiation of classes so much simpler, and in large applications this is invaluable.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now there is one other side effect to these changes.&amp;nbsp; By having loosely coupled objects writing unit tests is a lot easier now, and they are genuine unit tests, in other words they test one thing and one thing only.&amp;nbsp; Previously I would have had an input parser test as follows:&lt;/p&gt;&lt;pre class="codeblock"&gt;        [&lt;span style="color: rgb(43,145,175)"&gt;Test&lt;/span&gt;]&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ParseACorrectLine()&lt;br /&gt;        {&lt;br /&gt;            saleLine = &lt;span style="color: rgb(43,145,175)"&gt;InputParser&lt;/span&gt;.ProcessInput(&lt;span style="color: rgb(163,21,21)"&gt;"1 book at 12.49"&lt;/span&gt;);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.IsNotNull(saleLine);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.AreEqual(&lt;span style="color: rgb(163,21,21)"&gt;"book"&lt;/span&gt;,saleLine.ProductName);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.AreEqual(1, saleLine.Quantity);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.AreEqual(12.49m, saleLine.Price);&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;But this is effectively an integration test.&amp;nbsp; It's testing that the parser and the saleline classes are both working and working together properly.&amp;nbsp; Not an ideal situation.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;But with a loosely coupled design I can now use mocking and write a test as follows:&lt;/p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;pre class="codeblock"&gt;        [&lt;span style="color: rgb(43,145,175)"&gt;TestMethod&lt;/span&gt;]&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ParseACorrectLine()&lt;br /&gt;        {&lt;br /&gt;            mocksRepository = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;MockRepository&lt;/span&gt;();&lt;br /&gt;            saleLineFactory = mocksRepository.DynamicMock&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISaleLineFactory&lt;/span&gt;&amp;gt;();&lt;br /&gt;            sale = mocksRepository.DynamicMock&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;ISale&lt;/span&gt;&amp;gt;();&lt;br /&gt;            parser = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;InputParser&lt;/span&gt;(display, saleLineFactory);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Expect&lt;/span&gt;.Call(saleLineFactory.GetNewSaleLine(1,&lt;span style="color: rgb(163,21,21)"&gt;"book"&lt;/span&gt;,12.49m)).Return(&lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SaleLine&lt;/span&gt;(1,&lt;span style="color: rgb(163,21,21)"&gt;"book"&lt;/span&gt;,12.49m));&lt;br /&gt;            mocksRepository.ReplayAll();&lt;br /&gt;            parser.ProcessInput(&lt;span style="color: rgb(163,21,21)"&gt;"1 book at 12.49"&lt;/span&gt;, sale);&lt;br /&gt;            mocksRepository.VerifyAll();&lt;br /&gt;        }&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now my test is checking that the interactions between the InputParser and the other classes are correct, not that the other classes are functioning correctly as well (which is what the first test did).&amp;nbsp; Note that the return of a new SaleLine object is just there to provide a return value for the call to the saleline factory.&amp;nbsp; Not having it would cause the test to fail.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Hopefully this is enough to get you started on improving that crappy code you have to work with every day and making it a more loosely coupled application, and as always I'd love some feedback :-)&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/05/breaking-dependencies-using-dependency.html' title='Breaking Dependencies using Dependency Injection'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=7841774969302878737' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/7841774969302878737/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7841774969302878737'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7841774969302878737'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1931794116437530298</id><published>2008-05-07T10:09:00.001+10:00</published><updated>2008-05-07T10:09:04.859+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Agile and Remuneration</title><content type='html'>&lt;p&gt;A thread has sprung up on the &lt;a href="http://groups.yahoo.com/group/scrumdevelopment/" target="_blank"&gt;scrumdevelopment&lt;/a&gt; list about seniority and scrum, and how some people don't like scrum because if it's all about the team then how do individuals get recognized and get treated as more senior than others.&lt;/p&gt; &lt;p&gt;Well firstly, there's a misunderstanding that needs to be overcome here.&amp;nbsp; The agile manifesto states that one of the principles is "Individuals and interactions over processes and tools." Individuals!&amp;nbsp; If you're a member of a scrum team you won't suddenly be seen the same as everyone else yet at the same time having a job title like "super senior developer uber architect" doesn't mean the team will respect you either.&amp;nbsp; As in all team situations individuals need to prove themselves to their team mates through valuable contributions.&amp;nbsp; Respect and leadership will come naturally as a result.&lt;/p&gt; &lt;p&gt;This is all well and good, but how do you then tie this to remuneration, salaries, bonuses, etc.&amp;nbsp; How do you ensure that a team gets rewarded but that individuals who contribute more to the team get rewarded further?&lt;/p&gt; &lt;p&gt;In &lt;a href="http://aru.rugby.com.au/" target="_blank"&gt;Rugby Union&lt;/a&gt; (the sport when the scrum name came from) they have the concept of a man-of-the-match.&amp;nbsp; After each match a team will succeed or fail, but regardless of the result an individual is voted for as the player who contributed most to the performance of their team.&amp;nbsp; At the end of the season those votes are tallied and a "best and fairest" &lt;a href="http://aru.rugby.com.au/news/sharpe_eyes_silverware,62136.html" target="_blank"&gt;award&lt;/a&gt; is given to the person who contributed most to the overall performance of the team.&amp;nbsp; For software development why not apply the exact same principle to work out who has contributed the most?&lt;/p&gt; &lt;p&gt;The process is straightforward:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;At the start of the sprint retrospective (you are doing one, aren't you?) get the team to vote using a 3-2-1 system for the 3 people who contributed most to the performance of the team.&amp;nbsp; Obviously alter this slightly if you have a smaller team.&amp;nbsp; This also has the handy side-effect of getting the team thinking about what happened over the duration of the sprint, which is useful for the retrospective itself.&lt;/li&gt; &lt;li&gt;Don't work out the results straight away.&amp;nbsp; Conduct the sprint retrospective as per usual.&lt;/li&gt; &lt;li&gt;After the retrospective is done, work out the results and tell the team who the MVP or person-of-the-sprint is.&amp;nbsp; If you can, give that person a prize or some other form of recognition on the spot in front of their peers.&lt;/li&gt; &lt;li&gt;At the end of a release cycle, or some other time period of your choosing (maybe based on pay review cycles) tally up the votes over the sprints in that period and you'll have a clear understanding of who the main contributors are.&amp;nbsp; Being peer voted there's no "teachers pet" syndrome to worry about either.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Once you know who the best people on the team are you can provide rewards, recognition, bonuses and/or pay increases accordingly.&lt;/p&gt; &lt;p&gt;Be aware that individuals who do not rate highly in the voting may feel excluded which is why it's important to reward the team as a whole as well.&amp;nbsp; In general try and ensure that team rewards are better than individual rewards otherwise you'll end up with a fractured team.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/05/agile-and-remuneration.html' title='Agile and Remuneration'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=1931794116437530298' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/1931794116437530298/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1931794116437530298'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1931794116437530298'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6411867719571136195</id><published>2008-04-29T20:59:00.001+10:00</published><updated>2008-04-29T20:59:19.490+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>An Agile Agile Presentation for SBTUG Tomorrow Night</title><content type='html'>&lt;p&gt;Unfortunately &lt;a href="http://notgartner.com" target="_blank"&gt;Mitch&lt;/a&gt; has come down ill (get better, mate!) and won't be able to present at &lt;a href="http://www.sbtug.com/" target="_blank"&gt;SBTUG&lt;/a&gt; - The Sydney Business &amp;amp; Technology User Group - tomorrow night.&amp;nbsp; &lt;a href="http://www.craigbailey.net" target="_blank"&gt;Craig&lt;/a&gt; has kindly let me step in and cover for him and I'll be doing my Agile Agile presentation so feel free to come along and be a part of the action.&lt;/p&gt; &lt;p&gt;I hope to see you there, and bring your questions!&lt;/p&gt; &lt;p&gt;P.S. Thanks Craig for the &lt;a href="http://twitter.com/craigbailey/statuses/799418093" target="_blank"&gt;kind words&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/agile-agile-presentation-for-sbtug.html' title='An Agile Agile Presentation for SBTUG Tomorrow Night'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=6411867719571136195' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/6411867719571136195/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6411867719571136195'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6411867719571136195'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2950201736308457812</id><published>2008-04-26T18:02:00.001+10:00</published><updated>2008-04-26T18:02:46.015+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><category scheme='http://www.blogger.com/atom/ns#' term='holiday'/><title type='text'>The Whitest Beach In Australia</title><content type='html'>&lt;p&gt;Another quick holiday update - Wednesday we spent the day in the &lt;a href="http://en.wikipedia.org/wiki/Cape_Le_Grand_National_Park" target="_blank"&gt;Cape Le Grande National Park&lt;/a&gt; near &lt;a href="http://www.visitesperance.com" target="_blank"&gt;Esperance&lt;/a&gt; in Western Australia, which is apparently home to &lt;a href="http://www.visitesperance.com/displaynews_text.asp?Id=8411" target="_blank"&gt;the whitest beach in Australia&lt;/a&gt; at Lucky Bay.&amp;nbsp; After visiting it I can understand why it's so highly rated; and the other beaches in the park aren't that far behind it.&lt;/p&gt; &lt;p&gt;The water is crystal clear, the ocean is a brilliant blue and the sand is so soft and clean it's almost unbelievable. It actually crunches and squeaks underfoot and clumps up like icing sugar, just fantastic! Plus, if you have a 4WD you can drive out along Luck Bay's 3km of shoreline. Oh, and there's almost no-one around - being 750km from Perth certainly helps. Check out these shots for an idea of what I'm talking about.&lt;/p&gt; &lt;p&gt;Here's my foot seen through thigh deep water - you can't do that at a city beach!&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/rbanks54/SBLhKqETePI/AAAAAAAAAU8/fzhM2M7AV0M/s1600-h/image9.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="image" src="http://lh6.ggpht.com/rbanks54/SBLhQKETeQI/AAAAAAAAAVE/Myt7HLlvTnE/image_thumb3.png?imgmax=800" width="561" height="484"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;And here's a view towards the ocean.&amp;nbsp; Check out the range of blues - simple amazing!&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/rbanks54/SBLhV6ETeRI/AAAAAAAAAVM/RS6LCXSZSLI/s1600-h/image14.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="image" src="http://lh6.ggpht.com/rbanks54/SBLhdKETeSI/AAAAAAAAAVU/_WZ_g4i-970/image_thumb8.png?imgmax=800" width="644" height="439"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;And finally a shot along the shore - as you can see the waves are small enough for me to take the camera into the water without fear of getting wet, and being in a national park there's no development along the coastline.&amp;nbsp; Just Australia at it's best!&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/rbanks54/SBLhi6ETeTI/AAAAAAAAAVc/_Vr7IXEmbfU/s1600-h/image15.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="image" src="http://lh5.ggpht.com/rbanks54/SBLho6ETeUI/AAAAAAAAAVk/W4us7zML7pw/image_thumb9.png?imgmax=800" width="644" height="442"&gt;&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/whitest-beach-in-australia.html' title='The Whitest Beach In Australia'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=2950201736308457812' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/2950201736308457812/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2950201736308457812'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2950201736308457812'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5027141073376247705</id><published>2008-04-14T19:16:00.001+10:00</published><updated>2008-04-14T19:16:30.085+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>In Scarborough Beach</title><content type='html'>&lt;p&gt;Holidays are great when you're in a fantastic location.&amp;nbsp; The family and I are currently in Scarborough Beach, Western Australia have a great, relaxing time.&lt;/p&gt; &lt;p&gt;&lt;a href="http://maps.live.com/default.aspx?v=2&amp;amp;FORM=LMLTCP&amp;amp;cp=-31.896588~115.75839&amp;amp;style=h&amp;amp;lvl=17&amp;amp;tilt=-90&amp;amp;dir=0&amp;amp;alt=-1000&amp;amp;phx=0&amp;amp;phy=0&amp;amp;phscl=1&amp;amp;where1=scarborough%20beach%2C%20wa%2C%20australia&amp;amp;sp=Point.9phdk5tncss4_Here____~Point.9phdx1tncskb_Here____&amp;amp;encType=1"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" border="0" alt="image" src="http://lh3.ggpht.com/rbanks54/SAMgyqTpjsI/AAAAAAAAAUQ/0IYBJUk_bCM/image%5B4%5D.png?imgmax=800" width="608" height="442"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Check out this wonderful sunset.&amp;nbsp; You don't see sunsets like this on the east coast (for obvious reasons)&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/rbanks54/SAMg3KTpjtI/AAAAAAAAAUc/FGtB78dWIkk/s1600-h/image%5B8%5D.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" border="0" alt="image" src="http://lh5.ggpht.com/rbanks54/SAMg7KTpjuI/AAAAAAAAAUk/SmwZ9r5N_0U/image_thumb%5B3%5D.png?imgmax=800" width="644" height="336"&gt;&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/in-scarborough-beach.html' title='In Scarborough Beach'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=5027141073376247705' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/5027141073376247705/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5027141073376247705'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5027141073376247705'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3183111863942532086</id><published>2008-04-11T18:48:00.001+10:00</published><updated>2008-04-11T18:48:15.155+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Away for a Few Weeks</title><content type='html'>&lt;p&gt;Just a quick note to let you all know I'll be on leave for a few weeks in Western Australia driving around the southwest corner of this great country of mine.&amp;nbsp; I _might_ do an occasional blog post while I'm away but it's more likely that I'll be relaxing and sleeping instead - you can always &lt;a href="http://twitter.com/rbanks54" target="_blank"&gt;keep up with what I'm doing via twitter&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/away-for-few-weeks.html' title='Away for a Few Weeks'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=3183111863942532086' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/3183111863942532086/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3183111863942532086'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3183111863942532086'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7857070889282678602</id><published>2008-04-11T18:47:00.001+10:00</published><updated>2008-04-11T18:47:58.514+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>How To Fix Problems With Locked Files During a Web Application Build</title><content type='html'>&lt;p&gt;I had a client recently that was having a big problem with builds on dev machines failing due to files being locked and unable to be deleted. The problem irregular and it was causing the developers to get frustrated and waste a lot of time in stopping and restarting Visual Studio to try and release the locks.&lt;/p&gt; &lt;p&gt;The errors the team were seeing were all similar to this:&lt;/p&gt; &lt;p&gt;&lt;pre class="codeblock"&gt;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets : warning MSB3061: Unable to delete file "C:\WebApplication\bin\Web.dll". Access to the path 'C:\WebApplication\bin\Web.dll' is denied.&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The application itself was a web application, build on XP machines running VS2005 SP1 and set to use IIS as the default web server (i.e. the application virtual directory was pointed at the source code folder).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The team had already checked that the indexing service was off and that antivirus wasn't locking files.&amp;nbsp; When using the &lt;a href="http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx" target="_blank"&gt;sysinternals utilities&lt;/a&gt; to see what other processes were locking those files nothing appeared apart from devenv.exe itself (i.e. visual studio).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The problem as it turned out was in the settings of the virtual directory in IIS6.&amp;nbsp; The virtual directory still had the "index this location" flag turned on.&amp;nbsp; It turned out that IIS was placing a temporary lock on these files, even though the web application wasn't being started (ie it was just a compile, not a debug run).&amp;nbsp; Why this didn't appear in the sysinternals utilities I'm not sure.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Anyway, after turning the "index this location" setting off the file locking problem disappeared and the devs started smiling again.&amp;nbsp; Happy days :-)&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/how-to-fix-problems-with-locked-files.html' title='How To Fix Problems With Locked Files During a Web Application Build'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=7857070889282678602' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/7857070889282678602/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7857070889282678602'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7857070889282678602'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2295061555959136055</id><published>2008-04-07T18:17:00.001+10:00</published><updated>2008-04-07T18:17:00.922+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='powershell'/><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Deploying Web Sites using TFS Deployer, PowerShell and FTP</title><content type='html'>&lt;p&gt;I had a situation recently where I needed to deploy a web site into a production environment from TFS where the only way we could transfer files was via FTP.&amp;nbsp; In case it's not obvious I didn't want to deploy every time the build ran, but rather, only when the build was declared good.&lt;/p&gt; &lt;p&gt;To do this, I used &lt;a href="http://www.codeplex.com/tfsdeployer" target="_blank"&gt;TFS Deployer&lt;/a&gt; (developed by &lt;a href="http://notgartner.wordpress.com/2006/12/16/getting-started-with-tfs-deployer/" target="_blank"&gt;Mitch&lt;/a&gt;, &lt;a href="http://blogs.madtechnology.net/blogs/chris/default.aspx" target="_blank"&gt;Chris&lt;/a&gt;, &lt;a href="http://showusyourcode.spaces.live.com/" target="_blank"&gt;Darren&lt;/a&gt; and Geoff of &lt;a href="http://www.readify.net" target="_blank"&gt;Readify&lt;/a&gt;) available from &lt;a href="http://www.codeplex.com/tfsdeployer" target="_blank"&gt;CodePlex&lt;/a&gt;. TFS Deployer is a small utility that monitors changes in the build quality of a TFS Team Build and initiates a &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx" target="_blank"&gt;PowerShell&lt;/a&gt; script based on what the change in build quality was.&amp;nbsp; It runs something like this:&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.google.com/rbanks54/R_nYdH5nb1I/AAAAAAAAATw/CN7lOU4aceI/deploy1%5B4%5D.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="deploy1" src="http://lh4.google.com/rbanks54/R_nYd35nb2I/AAAAAAAAAT4/y8VseOlnVpM/deploy1_thumb%5B2%5D.png" width="640" height="376"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;A TeamBuild is executed and after completion the build has a quality of "Unexamined"&lt;/p&gt; &lt;p&gt;1. Someone runs some tests, checks the build is OK and decides the build quality can be progressed&lt;/p&gt; &lt;p&gt;2. TFS updates the build quality and raises an alert that the build quality has changed&lt;/p&gt; &lt;p&gt;3. On startup TFS Deployer registers itself as a listener for build quality events and picks up the alert.&amp;nbsp; It examines the old/new qualities and determines if a PowerShell script should be run. If a script needs to be run then PowerShell is started and the script executed.&lt;/p&gt; &lt;p&gt;4. Results from the script are emailed to a specified email address of distribution list.&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.google.com/rbanks54/R_nYeX5nb3I/AAAAAAAAAUA/X8OosFUwdko/clip_image002%5B6%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="clip_image002" src="http://lh4.google.com/rbanks54/R_nYe35nb4I/AAAAAAAAAUI/I2EGrV6SVc8/clip_image002_thumb%5B3%5D.jpg" width="702" height="191"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;h3&gt;TFS Deployer Setup&lt;/h3&gt; &lt;p&gt;I'll assume that TFS Deployer is installed and set up as per the instructions.&amp;nbsp; To initiate the PowerShell script we set up a deployment mapping as follows:&lt;/p&gt;&lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Mapping&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;xmlns&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;""&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                 &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;Computer&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;font color="#0000ff"&gt;MyBuildServer&lt;/font&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                 &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OriginalQuality&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Unexamined&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                 &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;NewQuality&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Released to Production&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                 &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;Script&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;DeployToProd.ps1&lt;/span&gt;"&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;                 &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;NotificationAddress&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;you.are@here.com&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;That's about it.&amp;nbsp; Pretty tough :-) Now for something a little harder.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Getting Ready for Deployment via FTP&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;OK, this is where it could get painful.&amp;nbsp; PowerShell doesn't have any native FTP support. So the first option you have would be to use the native .NET FTP classes from but that's a real pain because you effectively have to implement your own FTP client in PowerShell.&amp;nbsp; No thanks!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;But don't despair - the open source &lt;a href="http://www.indyproject.org/" target="_blank"&gt;Indy Project&lt;/a&gt; is a project that provides a wrapper and helper functions around for all the FTP calls you might want to make (plus a whole bunch of other low level networking goodness) and they have a &lt;a href="http://www.indyproject.org/Sockets/Download/Files/DotNet.de.aspx" target="_blank"&gt;.NET version&lt;/a&gt; of their library.&amp;nbsp; Grab a copy of that (and the Mono.Security DLL that goes with it) and you'll have all you really need to get going.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I should also mention that because I'm deploying to a production box I'm assuming that IIS is already set up - after all it's very unlikely that we'd be dropping the IIS virtual directory on every deployment.&amp;nbsp; We'll just be dropping our files in over the top of whatever is already there.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The PowerShell Script&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Time for the script itself - I'll just take this section at a time.&amp;nbsp; First up, the script is called from TFS Deployer which means we have access to the TFS build data for the build that just had it's quality changed. So we're going to set two variables for the folders where things live.&amp;nbsp; $loc is the root of the build drop location, and $sourcefiles is where the published web site live (ie the stuff we need to deploy).&lt;/p&gt;&lt;pre class="codeblock"&gt;Set-Location $TfsDeployerBuildData.DropLocation;&lt;br /&gt;$loc = get-location;&lt;br /&gt;set-location "Mixed Platforms\Release\_PublishedWebSites\";&lt;br /&gt;$sourceFiles = get-location;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Opening and Closing FTP Connections&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Next we're going to create a few PowerShell script functions to support the opening and closing of our FTP connections.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;When we open the connection we're first going to use the .NET assembly loader to bring in the Indy.Sockets library then use the methods in that to make the connection and then return the FTP connection object from the function.&amp;nbsp; The close-method is a bit simpler in that we simply wrap the call to the FTP-disconnect. (yes, it's not really required as a function, but you never know when you might want to add logging, error handling, etc).&lt;/p&gt;&lt;pre class="codeblock"&gt;function Open-FTPConnection($ftphost, $username, $password) {&lt;br /&gt;&lt;br /&gt;[void][Reflection.Assembly]::LoadFrom("C:\path\to\Indy.Sockets.dll")&lt;br /&gt;  $ftp = new-object Indy.Sockets.FTP  &lt;br /&gt;  $ftp.Disconnect()&lt;br /&gt;  $ftp.Host = $ftphost&lt;br /&gt;  $ftp.Username = $username&lt;br /&gt;  $ftp.Password = $password&lt;br /&gt;  $ftp.Connect()&lt;br /&gt;  $ftp.Passive=$true;&lt;br /&gt;  return $ftp&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Close-FTPConnection($ftp) {&lt;br /&gt;    $ftp.Disconnect();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Oh - For those not familiar with PowerShell syntax the :: operator lets us call static methods on a type and the [] wrap a type identifier.&amp;nbsp; &lt;/p&gt;&lt;br /&gt;&lt;h3&gt;FTP Miscellaneous Functions&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Next we define a few more functions just to wrap up some of the basic FTP calls.&amp;nbsp; Note: Download-FTPFile isn't used - it's just there for your reference.&lt;/p&gt;&lt;pre class="codeblock"&gt;function Get-FTPCurrentLocation($ftp) {&lt;br /&gt;  return $ftp.RetrieveCurrentDir();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Download-FTPFile($ftp, $sourceFileName, $targetDir) {&lt;br /&gt;    $ftp.Get($sourceFileName, ($targetDir + $sourceFileName), $true, $false);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Upload-FTPFile($ftp, $sourceFileName, $targetFileName) {&lt;br /&gt;    $ftp.Put($sourceFileName, $targetFileName, $false);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Nothing overly complex in that.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Getting the Contents of an FTP Folder&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Now we get to something a bit more interesting.&amp;nbsp; Here we're creating a function that iterates over the contents of the FTP location and optionally deleting files as it goes.&amp;nbsp; It will return a list of sub-folders in the folder for later use.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The function works as follows:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;1. Get the directory listing for the current FTP location.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;2. Checks if the current directory is the root folder or not - appends a trailing slash if it isn't.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3. For each item in the folder...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3a. Parses the string to get the file name&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3b. Checks if the item is a directory or a file (directories have a "d" in their attributes)&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3c. For folders we return the full path to the sub-folder.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3d. For files we check the delete flag and nuke the file if it is set.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You'll notice that the $result.add($name) call is cast to a [void].&amp;nbsp; If we don't do this then when we return from the function we get 2 sets of files.&amp;nbsp; One from the $result object and one for each file name that was written to the output stream by the $result.add() method call. &lt;/p&gt;&lt;pre class="codeblock"&gt;function Get-FTPFolders($ftp, [bool]$removeFiles) {&lt;br /&gt;    $ls = new-object System.Collections.Specialized.StringCollection;    &lt;br /&gt;    $result = new-object System.Collections.Specialized.StringCollection;    &lt;br /&gt;    $ftp.List($ls, "", $true);&lt;br /&gt;    $currdir = Get-FTPCurrentLocation($ftp);&lt;br /&gt;    if($currdir -ne "/") {&lt;br /&gt;        $currdir = $currdir + "/";&lt;br /&gt;    }&lt;br /&gt;    foreach ($item in $ls)&lt;br /&gt;    {&lt;br /&gt;        [string[]]$fields=[Regex]::Split($item, " +");&lt;br /&gt;        $startField=8;   #the file/directory name starts after 8 fields&lt;br /&gt;        [string]$name=$currdir;&lt;br /&gt;&lt;br /&gt;        #make sure we join up file names that were split (ie ones with spaces)&lt;br /&gt;        for ($field=$startField; $field -lt $fields.Length; $field++)&lt;br /&gt;        {&lt;br /&gt;            if ($field -eq $startField)&lt;br /&gt;            {&lt;br /&gt;              $temp = ""&lt;br /&gt;            } else&lt;br /&gt;            {&lt;br /&gt;              $temp = " "&lt;br /&gt;            }&lt;br /&gt;            $name += $temp + $fields[$field];&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        if ($item.StartsWith("d")) &lt;br /&gt;        { #directory&lt;br /&gt;            [void]$result.Add($name);&lt;br /&gt;        } &lt;br /&gt;        else &lt;br /&gt;        {&lt;br /&gt;        if ($item.StartsWith("-")) { #files have '-' as first character&lt;br /&gt;            if ($removeFiles)&lt;br /&gt;                {&lt;br /&gt;                $ftp.Delete($name);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return $result&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Iterating/Deleting the FTP Folder Structure&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Next I have two more methods.&amp;nbsp; One to read through the contents of the FTP folder hierarchy, and one to clear it out.&amp;nbsp; Both methods are roughly the same with only variations for the call to Get-FTPFolders.&lt;/p&gt;&lt;pre class="codeblock"&gt;function Get-FTPTree($ftp)&lt;br /&gt;{&lt;br /&gt;    $thisdir = Get-FTPCurrentLocation($ftp);&lt;br /&gt;    $thisdir;&lt;br /&gt;    $subfolders = (Get-FTPFolders $ftp $false);&lt;br /&gt;    if ($subfolders -ne $null) {&lt;br /&gt;        foreach ($xitem in $subfolders)&lt;br /&gt;        {&lt;br /&gt;            $ftp.ChangeDir($xitem);&lt;br /&gt;            Get-FTPTree($ftp);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    $ftp.ChangeDir($thisdir);&lt;br /&gt;    return;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Clean-FTPTree($ftp)&lt;br /&gt;{&lt;br /&gt;    $thisdir = Get-FTPCurrentLocation($ftp);&lt;br /&gt;    $thisdir;&lt;br /&gt;    $subfolders = (Get-FTPFolders $ftp $true);&lt;br /&gt;    if ($subfolders -ne $null) {&lt;br /&gt;        foreach ($xitem in $subfolders)&lt;br /&gt;        {&lt;br /&gt;            $ftp.ChangeDir($xitem);&lt;br /&gt;            Clean-FTPTree($ftp);&lt;br /&gt;            $foldername = $xitem.split("/");&lt;br /&gt;            $ftp.ChangeDir($thisdir);&lt;br /&gt;            $ftp.RemoveDir($foldername[$foldername.Count - 1]);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    $ftp.ChangeDir($thisdir);&lt;br /&gt;    return;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Note that in the Clean method we delete folders by stripping the folder name off the end of the full path, stepping back up a level and then calling the removedir method.&amp;nbsp; It's not pretty, but it works.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Putting it all Together&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Now we have everything in place let's actually do what we need to do. Here's what happens:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;1. We make the connection to the FTP server.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;2. We clean out the existing FTP tree&lt;/p&gt;&lt;br /&gt;&lt;p&gt;3. We get a list of all the files we're going to upload.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;4. We copy each file individually to the ftp server, creating folders where required (note the filename processing). &lt;/p&gt;&lt;br /&gt;&lt;p&gt;5. Just to make sure things are right - we get the contents of the site so we can check the upload worked.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;6. Close the connection - we're done!&lt;/p&gt;&lt;pre class="codeblock"&gt;#Make a connection&lt;br /&gt;&lt;br /&gt;$f = Open-FTPConnection "my.ftp.server.com" "ftp_user_account" "ftp_account_password";&lt;br /&gt;&lt;br /&gt;write-output "----CLEANING----";&lt;br /&gt;Clean-FTPTree($f);&lt;br /&gt;&lt;br /&gt;write-output "----UPLOADING----";&lt;br /&gt;&lt;br /&gt;$localfiles = (get-childitem $sourcefiles -r)&lt;br /&gt;foreach ($localfile in $localfiles) {&lt;br /&gt;  $remfilename = $localfile.FullName.Replace($sourcefiles.ProviderPath, "");&lt;br /&gt;  $remfilename = $remfilename.Replace("\", "/");&lt;br /&gt;  if ($localfile.Attributes -eq "Directory") {&lt;br /&gt;    Write-Output ("Creating " + $localfile.FullName + ":" + $remfilename);&lt;br /&gt;    $f.MakeDir($remfilename);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   Write-Output ("   Uploading " + $localfile.FullName + ":" + $remfilename);&lt;br /&gt;   upload-ftpfile $f $localfile.FullName $remfilename&lt;br /&gt;   $remfilename;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;write-output "----VERIFYING----";&lt;br /&gt;Get-FTPTree($f);&lt;br /&gt;&lt;br /&gt;Close-FTPConnection $f &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;One thing you might notice is that we use the ProviderPath property of the $sourcefiles variable.&amp;nbsp; This is because $sourcefiles is a PathInfo object and when TFS Deployer runs the PathInfo will contain a UNC path pointing to a network share - if we just use the Path property then we will get the PowerShell Provider identifier in the string causing the FTP upload to fail.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Hopefully this is a good starting point for you if you are trying to do the same thing.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/deploying-web-sites-using-tfs-deployer.html' title='Deploying Web Sites using TFS Deployer, PowerShell and FTP'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=2295061555959136055' title='4 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/2295061555959136055/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2295061555959136055'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2295061555959136055'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8492051159051227200</id><published>2008-04-01T08:54:00.001+11:00</published><updated>2008-04-01T08:54:32.054+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='management'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Deming's Rules of Management and Agile Methods</title><content type='html'>&lt;p&gt;I saw a post this morning by David Anderson this morning on how &lt;a href="http://www.agilemanagement.net/Articles/Weblog/AFeailureTolerantCultureL.html" target="_blank"&gt;a fault tolerant culture helps create great teams&lt;/a&gt;.&amp;nbsp; Admittedly he was talking about the British cycling team's recent gold medal haul but the point is valid regardless.&amp;nbsp; In his post he referred to &lt;a href="http://en.wikipedia.org/wiki/Edwards_Deming" target="_blank"&gt;W. Edwards Deming&lt;/a&gt; whom I'd never heard of as a source for the principle of removing fear from a culture.&amp;nbsp; Curious, I flicked over to the wikipedia entry and had a look at what the esteemed but unheard of Deming had to say.&lt;/p&gt; &lt;p&gt;Here's an excerpt from the &lt;a href="http://en.wikipedia.org/wiki/Edwards_Deming" target="_blank"&gt;wikipedia entry&lt;/a&gt;:&lt;/p&gt; &lt;h5&gt;Deming's 14 points&lt;/h5&gt; &lt;p&gt;Deming offered fourteen key principles for &lt;a href="http://en.wikipedia.org/wiki/Management"&gt;management&lt;/a&gt; for transforming business effectiveness. The points were first presented in his book &lt;i&gt;Out of the Crisis&lt;/i&gt; (p. 23-24)&lt;sup&gt;&lt;a href="http://en.wikipedia.org/wiki/Edwards_Deming#cite_note-18"&gt;[19]&lt;/a&gt;&lt;/sup&gt;. &lt;ol&gt; &lt;li&gt;Create constancy of purpose toward improvement of product and service, with the aim to become competitive and stay in business, and to provide jobs.  &lt;li&gt;Adopt the new philosophy. We are in a new economic age. Western management must awaken to the challenge, must learn their responsibilities, and take on leadership for change.  &lt;li&gt;Cease dependence on inspection to achieve quality. Eliminate the need for inspection on a mass basis by building quality into the product in the first place.  &lt;li&gt;End the practice of awarding business on the basis of price tag. Instead, minimize total cost. Move towards a single supplier for any one item, on a long-term relationship of loyalty and trust.  &lt;li&gt;Improve constantly and forever the system of production and service, to improve quality and productivity, an thus constantly decrease cost.  &lt;li&gt;Institute training on the job.  &lt;li&gt;Institute leadership (see Point 12 and Ch. 8 of "Out of the Crisis"). The aim of supervision should be to help people and machines and gadgets to do a better job. Supervision of management is in need of overhaul, as well as supervision of production workers.  &lt;li&gt;Drive out fear, so that everyone may work effectively for the company. (See Ch. 3 of "Out of the Crisis")  &lt;li&gt;Break down barriers between departments. People in research, design, sales, and production must work as a team, to foresee problems of production and in use that may be encountered with the product or service.  &lt;li&gt;Eliminate slogans, exhortations, and targets for the work force asking for zero defects and new levels of productivity. Such exhortations only create adversarial relationships, as the bulk of the causes of low quality and low productivity belong to the system and thus lie beyond the power of the work force.  &lt;li&gt;a. Eliminate work standards (quotas) on the factory floor. Substitute leadership.&lt;br&gt;b. Eliminate management by objective. Eliminate management by numbers, numerical goals. Substitute leadership.  &lt;li&gt;a. Remove barriers that rob the hourly worker of his right to pride of workmanship. The responsibility of supervisors must be changed from sheer numbers to quality.&lt;br&gt;b. Remove barriers that rob people in management and in engineering of their right to pride of workmanship. This means, &lt;i&gt;inter alia&lt;/i&gt;, abolishment of the annual or merit rating and of management by objective (See CH. 3 of "Out of the Crisis").  &lt;li&gt;Institute a vigorous program of education and self-improvement.  &lt;li&gt;Put everybody in the company to work to accomplish the transformation. The transformation is everybody's work. &lt;/li&gt;&lt;/ol&gt; &lt;p&gt;What struck me as I read this was how much lean and agile processes try to utilise the principles.&lt;/p&gt; &lt;p&gt;Here's my quick take on alignment between Deming's principles and agile methods:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 1 - &lt;em&gt;Constancy of purpose&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Best described by the &lt;a href="http://www.agilemanifesto.org/" target="_blank"&gt;agile manifesto&lt;/a&gt;.&amp;nbsp; It's a clear and unchanging vision of what agile methods are all about and what they are trying to achieve.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 2 - &lt;em&gt;Take on leadership for change&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Agile understands and accepts change.&amp;nbsp; Change happens - let's make the most of it and lead the way towards change.&amp;nbsp; Change for the better.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 3 - &lt;em&gt;Build quality into the product from the start&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Test Driven Development (&lt;a href="http://www.youtube.com/watch?v=f60aIlNhMoE" target="_blank"&gt;see video&lt;/a&gt;) is a perfect example of this as is the desire to minimise code debt.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 4 - &lt;em&gt;Minimize cost.&lt;/em&gt;&lt;/strong&gt;&amp;nbsp; Time is the number one cost in software development.&amp;nbsp; Minimizing wasted effort is the single best way to reduce cost.&amp;nbsp; Minimal code debt also goes a very long way to achieving this&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 5 - &lt;em&gt;Improve constantly&lt;/em&gt;&lt;/strong&gt;.&amp;nbsp; Agile calls this principle "inspect and adapt". It's embodied in scrum retrospectives and other review processes.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 6 - &lt;em&gt;Training on the job&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; For developers this is encapsulated by pair programming.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 7 - &lt;em&gt;Institute leadership&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Leadership in an agile project arises from within a team - it is not imposed through management hierarchies (and imposed "leadership" is typically psuedo-leadership - leaders still naturally arise in teams).&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 8 - &lt;em&gt;Drive out fear&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; I don't know that this is explicitly addressed by any agile methodology per se but it is something that all good leaders will endeavour to build into their teams.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 9 - &lt;em&gt;Break down barriers&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Agile methods cry out for openness and collaboration.&amp;nbsp; It's at the heart of what agile is all about.&amp;nbsp; Barriers are impediments and slow down teams.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 10&amp;nbsp; &lt;em&gt;Eliminate slogans, targets, etc&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; The essence of this is that management can't drive people to work harder to improve because of organisational impediments (the system). In software development "the system" is the organisation and the red-tape and processes that organisation imposes on their staff.&amp;nbsp; Scrum in particular addresses this (indirectly) through the role of Scrum Master - a person responsible for removing impediments to the team.&amp;nbsp; If the organisation is slow the Scrum Master should be addressing that.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 11 - &lt;em&gt;Substitute Leadership&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; See point 7.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 12 - &lt;em&gt;Remove barriers&lt;/em&gt;&lt;/strong&gt;.&amp;nbsp; Covered in points 9 &amp;amp; 10.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 13 - &lt;em&gt;Self improvement&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; Once again the inspect and adapt principle at play - good teams should be applying this personally as well as at a team level.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Point 14 - &lt;em&gt;Everyone works to achieve transformation&lt;/em&gt;.&lt;/strong&gt;&amp;nbsp; The is the very essence of an organisation with an agile heart.&amp;nbsp; Everyone working together to achieve an objective and improve what they do.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;I think I might have to go and hunt for Deming's book and have a read.&amp;nbsp; I'd be interested in your thoughts as well - especially if you've read Deming's work.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/04/deming-rules-of-management-and-agile.html' title='Deming&amp;#39;s Rules of Management and Agile Methods'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=8492051159051227200' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/8492051159051227200/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8492051159051227200'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8492051159051227200'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3632752776802144545</id><published>2008-03-29T18:13:00.001+11:00</published><updated>2008-03-29T18:13:22.737+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Syntax Highlighting for Powershell in Notepad++</title><content type='html'>&lt;p&gt;If you ever find yourself doing any &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx" target="_blank"&gt;Powershell&lt;/a&gt; work (maybe when using &lt;a href="http://www.codeplex.com/tfsdeployer" target="_blank"&gt;TFSDeployer&lt;/a&gt; for example) then you'll probably be editing .ps1 files in a text editor (I hope you're not still using notepad!!).&amp;nbsp; My personal tool of choice is &lt;a href="http://notepad-plus.sourceforge.net" target="_blank"&gt;Notepad++&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Unfortunately most notepad replacements still aren't aware of the syntax rules for .ps1 files and treat them like plain text.&amp;nbsp; But fret no more - you can now get powershell syntax highlighting in Notepad++.&amp;nbsp; Have a look at &lt;a href="http://weblogs.asp.net/jgalloway/archive/2006/11/25/powershell-language-definitions-for-notepad.aspx"&gt;http://weblogs.asp.net/jgalloway/archive/2006/11/25/powershell-language-definitions-for-notepad.aspx&lt;/a&gt; and download the language definition.&amp;nbsp; It works a treat! &lt;p&gt;Here’s what it looks like in action:  &lt;p&gt;&lt;a href="http://lh4.google.com/rbanks54/R-3sD35nbzI/AAAAAAAAATg/Ar2T5XVfbME/clip_image002%5B5%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" border="0" alt="clip_image002" src="http://lh6.google.com/rbanks54/R-3sEX5nb0I/AAAAAAAAATo/FGIo8pJxFsQ/clip_image002_thumb%5B2%5D.jpg" width="604" height="124"&gt;&lt;/a&gt;  &lt;p&gt;Nice! Thanks Jon!&lt;/p&gt; &lt;p&gt;As a footnote &lt;a href="http://blog.snagy.name" target="_blank"&gt;Steven Nagy&lt;/a&gt; had the following to say in a recent conversation we had:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;I had a nice play with &lt;a href="http://powergui.org/index.jspa" target="_blank"&gt;PowerGui&lt;/a&gt; today and think I will keep both handy. PowerGui gives you intellisense and richer syntax highlighting than Notepad++ &lt;p&gt;However you can open 5 ps1 files in NP++ in one click and they all open in ONE window, unlike PowerGui which creates a new instance with each file. Other than that, PowerGui owns. And it costs the same: $0.00&lt;/p&gt;&lt;/blockquote&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/03/syntax-highlighting-for-powershell-in.html' title='Syntax Highlighting for Powershell in Notepad++'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=3632752776802144545' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/3632752776802144545/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3632752776802144545'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3632752776802144545'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4790891309774835320</id><published>2008-03-26T22:30:00.001+11:00</published><updated>2008-03-26T22:30:14.973+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='management'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>A Tale of Two Companies</title><content type='html'>&lt;p&gt;There were once 2 companies who both wanted to develop some custom software to help their businesses enter an evolving market and gain a competitive edge.&amp;nbsp; Both companies made an initial approximation of the costs involved in getting the software developed and both companies set aside a similar sum of money for the project.&amp;nbsp; Neither company had any real appetite or skill for developing the software internally and decided to get external companies to help.&lt;/p&gt; &lt;h3&gt;Company A&lt;/h3&gt; &lt;p&gt;Company A decided to take the traditional, recommended, "best practice" approach for getting the software developed.&amp;nbsp; It's what they'd always done in the past, it's what their project managers recommended and even though they'd never had a project come in on time before they hoped that if they&amp;nbsp; did things right this time then it would all be OK.&amp;nbsp; They also decided to set aside a portion of their budget just in case there was some overrun in the project.&lt;/p&gt; &lt;p&gt;Because Company A wanted to get the best value for their money they decided to put out a tender.&amp;nbsp; They knew that a tender would give them a result they wanted for the lowest fixed price and they'd get everything they asked for.&lt;/p&gt; &lt;p&gt;They also wanted to get the tender right. They knew how many software projects failed but they figures that that was mainly because requirements in tenders had never been quite right before. If they could get their tender requirements right this time then it would give them their best shot at a successful project and they'd be able to enter this new market with confidence.&lt;/p&gt; &lt;p&gt;Company A decided that the best way to get requirements right was to get in an external expert to help draft and clarify the requirements and to word the tender in the best way possible so that anyone could to clearly understand what it is that they were after.&lt;/p&gt; &lt;p&gt;It takes a week to find the right expert and during the initial meeting the expert listens to the high level overview of the project and estimates about 6 weeks to get the tender ready.&amp;nbsp; The expert then starts talking to the stakeholders in the company and begins to draw up an initial draft of the tender.&lt;/p&gt; &lt;p&gt;The expert uncovered more requirements than were initially explained and, as this was a perfect opportunity, a number of stakeholders added wish list items to the tender to be included as low priority items. After two months the tender draft is ready and placed before the stakeholders for review.&amp;nbsp; Everyone in the company understood why the extra 2 weeks were needed to get the draft completed and thought that it was time well spent, especially as some critical requirements were discovered that would otherwise have been missed.&amp;nbsp; Unfortunately the review also determined that some of the requirements weren't well described and others some seemed to have been misunderstood.&lt;/p&gt; &lt;p&gt;2 weeks later the revised draft was again presented to the stakeholders for review.&amp;nbsp; There were still a few things that weren't quite right and a couple of the stakeholders were unable to attend due to scheduling conflicts but as the tender had now taken a month longer than estimated the remaining stakeholders signed off on it and got the project moving.&lt;/p&gt; &lt;p&gt;The published tender had over 400 requirements and suppliers were given a 4 week window in which to respond.&amp;nbsp; Responses when they arrived were larger in both number and size than expected and the review of the responses took 4 weeks to score and complete before a group of 3 potential suppliers were short listed and allocated a day each to present their proposals, 2 weeks after being notified.&lt;/p&gt; &lt;p&gt;Now after 20 weeks the company was at the point where they were ready for vendor presentations.&amp;nbsp;&amp;nbsp; And they still had no software to show for it. Plus they'd spent 15% of their project budget already.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;h3&gt;Company B&lt;/h3&gt; &lt;p&gt;Company B took a rather different approach.&amp;nbsp; They'd been burned by tenders and fixed price, fixed scope contracts before and their CIO had read about agile development and some of the success stories that were talked about.&lt;/p&gt; &lt;p&gt;While it was considered a risky approach,&amp;nbsp; the CIO knew that repeating what had failed before in the hope that the result would be different was the very definition of insanity, and recommended that an agile approach be taken and immediately set about locating a number of potential vendors who could deliver a project using an agile methodology.&amp;nbsp; After 2 weeks the potential vendors had been contacted and a preferred vendor chosen.&amp;nbsp; The CIO and the vendor also took an unusual approach to the contract. They agreed that cost would be limited, but that scope would not.&amp;nbsp; A minimum set of functionality would be delivered but beyond that anything could be changed by the company.&amp;nbsp; Company B could terminate the contract at any time of their choosing, as long as they paid out a portion of the remaining contract.&amp;nbsp; The vendor would deliver the highest value software they could using a prioritised list of requirements as determined by Company B. The vendor also stipulated that they wanted to have a single representative of Company B to work with, not a committee. Changes to functionality, priorities and requirements were allowed at any time with no extra cost so long as those things changed were not currently being worked on.&amp;nbsp; If Company B wanted to add new features or change existing ones then that was their right.&amp;nbsp; The vendor also agreed to deliver regular releases of the product so that the company could see what was being built and could optionally put that code into production should they deem it appropriate.&lt;/p&gt; &lt;p&gt;It took a few more weeks to get the legals and the resourcing of people sorted out but even so, the project kicked off 4 weeks after the initial "go" decision.&lt;/p&gt; &lt;p&gt;During the initial week of the project stakeholders from Company B and members of the vendor's project team sat together and worked out a prioritised list of high level requirements.&amp;nbsp; Many of the requirements were still somewhat vague but the vendor wasn't concerned - they would work with Company B's people to clarify those requirements better once they got to them.&amp;nbsp; Each requirement was given a relative effort estimate based on how much effort there might be compared to other requirements in the list, and was also given a simple set of criteria to help determine when a requirement could be considered complete.&amp;nbsp; After some consultation with the vendor the prioritised list of requirements was altered slightly to take into consideration any cross-requirement dependencies and pre-requisites but the end result was an order list of things that Company B wanted in their software.&lt;/p&gt; &lt;p&gt;The very next week development work began.&lt;/p&gt; &lt;p&gt;The vendors team started by taking the 4 first items from the requirement list making a commitment to complete them over the next 3 weeks, saying they would have something finished that they could show the people of Company B when the time was up.&amp;nbsp; However the CIO was a little concerned - 4 items didn't seem like that much to work on.&amp;nbsp; However after the team talked through how much code and database set up needed to be done in order to complete a single item the CIO was willing to go with it.&amp;nbsp; At worst they would waste 3 weeks and if they didn't like what they saw they could always terminate the contract.&lt;/p&gt; &lt;p&gt;Over the next 3 weeks the team set to work.&amp;nbsp; However the teams definition of work was a little unusual, they didn't go and lock themselves away and then turn up 3 weeks later with something to show, they were very visible, spent a lot of time asking people questions, clarifying what they were doing and getting stakeholders together to make sure they understood what needed to be done.Plus there were all these wall charts and sticky notes being stuck up on windows and walls so that anyone could see what was being done by the team and how they were tracking.&amp;nbsp; For the first time people in Company B could see what those mysterious developers were actually doing.&lt;/p&gt; &lt;p&gt;At the end of the third week of development the team got the stakeholders together and showed them what they had done.&amp;nbsp; All 4 items they took on were there and working.&amp;nbsp; No smoke, no mirrors. Plus the people from company B were allowed to play with that version of the software whenever they wanted.&amp;nbsp; Company B was quietly pleased.&lt;/p&gt; &lt;p&gt;The next week the vendors team took on 12 items to deliver in 3 weeks.&amp;nbsp; Now the CIO was thinking that they'd taken on too much.&amp;nbsp; But the same thing happened - lots of communication, lots of conversations and at the end of the next 3 weeks more product was delivered and things were coming together.&amp;nbsp; Company B was starting to take notice.&lt;/p&gt; &lt;p&gt;Shortly after this some of the Company B people were playing with the software and realised that with what they had already it would be a great idea and would really improve things if they could make a few changes to what they had planned.&amp;nbsp; They hadn't thought of them before, but now that they had seen the software and how it was shaping up it just seemed so logical.&amp;nbsp; The vendor was quite happy for the company to add requirements to their list, just as long as the list was re-prioritised and anything they didn't want any more was removed.&lt;/p&gt; &lt;p&gt;This pattern of work for another 4 iterations and at the end of the fourth iteration the CIO and the stakeholders looked at the product and realised that all of their main functionality was already in place.&amp;nbsp; There were still a lot of items on the requirements list, but they were low priority items and probably not worth the development cost, at least not for an initial release.&lt;/p&gt; &lt;p&gt;After 23 weeks the software was put into production.&lt;/p&gt; &lt;p&gt;As everything that was needed was already delivered Company B decided to terminate the contract and saved around 40% of their budget.&amp;nbsp; The money saved then went into further efforts to open up a lead in the emerging market giving the company even more of a competitive edge. As for the vendor they became the sole supplier of development services to Company B from that point on.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Company B was already up and running with their system and starting to make inroads into the new market before Company A even got the development work started on their project.&amp;nbsp; Over the next few years Company B continued to push hard providing their customers with regular updates to their offerings, adapting to the changing state of the market and managed to do so at a pace that no-one else could get close to matching.&amp;nbsp; When Company A finally made their venture into that market it was with a poorly conceived, me-too product that cost way more than budgeted and didn't work anywhere near as well as that of the established players.&amp;nbsp; Company A withdrew from that market not long after.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Before you ask - no, this isn't a story based on any specific companies.&amp;nbsp; But it is a story I'm sure you can relate to as this sort of thing happens all over the world every single day.&amp;nbsp; There are still far too many Company A's in this world and we should do all we can to help turn them into Company B's.&lt;/p&gt; &lt;p&gt;I'd love to hear your comments!&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/03/tale-of-two-companies.html' title='A Tale of Two Companies'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=4790891309774835320' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/4790891309774835320/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4790891309774835320'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4790891309774835320'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3264246928252329080</id><published>2008-03-12T10:56:00.001+11:00</published><updated>2008-03-12T10:56:09.982+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='microsoft'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Firefox 3 Beta 4 - A Mini Review</title><content type='html'>&lt;p&gt;With IE8 Beta 1 finally getting out the door at Mix08 last week I figured I’d check on the competition and spark up FF3 Beta 4. &lt;p&gt;Here’s a quick summary of what’s new (from a visible perspective) &lt;p&gt;1. Speed – it’s sooo much quicker to use.&amp;nbsp; You’ll notice the difference in ajax heavy apps (google reader, etc) as well as in general rendering. &lt;p&gt;2. Downloads now get virus checked.&amp;nbsp; Nice.&lt;br&gt;&lt;a href="http://lh3.google.com/rbanks54/R9cb4LqzFhI/AAAAAAAAARA/n5BqB1drSwg/clip_image002%5B6%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="137" alt="clip_image002" src="http://lh6.google.com/rbanks54/R9cb47qzFiI/AAAAAAAAARI/1v8YeaDkp-Y/clip_image002_thumb%5B3%5D" width="473" border="0"&gt;&lt;/a&gt; &lt;p&gt;3. New address bar – very funky.&amp;nbsp; Not only address search, but titles as well&lt;br&gt;&lt;a href="http://lh4.google.com/rbanks54/R9cb5bqzFjI/AAAAAAAAARQ/1WgRZSX0VO4/clip_image004%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="240" alt="clip_image004" src="http://lh3.google.com/rbanks54/R9cb6LqzFkI/AAAAAAAAARY/2fG8v1AdwAk/clip_image004_thumb%5B1%5D" width="553" border="0"&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="http://lh6.google.com/rbanks54/R9cb67qzFlI/AAAAAAAAARg/0yY3ylXR30o/clip_image006%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="161" alt="clip_image006" src="http://lh4.google.com/rbanks54/R9cb7bqzFmI/AAAAAAAAARo/3t1AP-NBkPM/clip_image006_thumb%5B1%5D" width="511" border="0"&gt;&lt;/a&gt; &lt;p&gt;4. Most FF2 add-ons don’t work.&amp;nbsp; You’ll need to find updates.&amp;nbsp; Here’s &lt;a href="http://www.getfirebug.com/" target="_blank"&gt;Firebug 1.1 beta&lt;/a&gt; (&lt;a href="http://fireftp.mozdev.org/developers.html" target="_blank"&gt;FireFtp&lt;/a&gt; has a beta you can get as well)&lt;br&gt;&lt;a href="http://lh3.google.com/rbanks54/R9cb8LqzFnI/AAAAAAAAARw/wmh9U2AqeHs/clip_image008%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="298" alt="clip_image008" src="http://lh6.google.com/rbanks54/R9cb87qzFoI/AAAAAAAAAR4/XPqsiMObXNY/clip_image008_thumb%5B1%5D" width="704" border="0"&gt;&lt;/a&gt;&lt;br&gt;You can see the parallel loading has increased to 6-7 items instead of 2, which explains some of the speed boost. &lt;p&gt;5. Bookmarking is a little different – note the tagging.&amp;nbsp; Very cool.&lt;br&gt;&lt;a href="http://lh4.google.com/rbanks54/R9cb9bqzFpI/AAAAAAAAASA/mPkUYn7AAEY/clip_image010%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="215" alt="clip_image010" src="http://lh3.google.com/rbanks54/R9cb-LqzFqI/AAAAAAAAASI/Smq-s62I01I/clip_image010_thumb%5B1%5D" width="334" border="0"&gt;&lt;/a&gt; &lt;p&gt;6. IE8 Activities work and are available from &lt;a href="http://www.kaply.com/weblog/2008/03/07/microsoft-activities-for-firefox-new-version/"&gt;http://www.kaply.com/weblog/2008/03/07/microsoft-activities-for-firefox-new-version/&lt;/a&gt;&lt;br&gt;Adding activities from the IE8 activity gallery works, but &lt;a href="http://notgartner.com" target="_blank"&gt;Mitch&lt;/a&gt;’s &lt;a href="http://notgartner.wordpress.com/2008/03/09/two-new-activities-for-internet-explorer-80/" target="_blank"&gt;Wikipedia activity&lt;/a&gt; doesn’t due to the use of IE8 specific javascript.&amp;nbsp; Even so...&lt;br&gt;&lt;a href="http://lh5.google.com/rbanks54/R9cb_rqzFrI/AAAAAAAAASQ/fEBXIZFDLkM/clip_image012%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="301" alt="clip_image012" src="http://lh3.google.com/rbanks54/R9ccBLqzFsI/AAAAAAAAASY/w2CfwI6ZBDM/clip_image012_thumb%5B1%5D" width="509" border="0"&gt;&lt;/a&gt;&lt;br&gt;results in&lt;br&gt;&lt;a href="http://lh5.google.com/rbanks54/R9ccBrqzFtI/AAAAAAAAASg/aNvosMJkNJI/clip_image014%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="53" alt="clip_image014" src="http://lh3.google.com/rbanks54/R9ccCLqzFuI/AAAAAAAAASo/UuUj26n7fyI/clip_image014_thumb%5B1%5D" width="346" border="0"&gt;&lt;/a&gt; &lt;p&gt;7. IE8 style Webslices can be got from &lt;a href="http://www.glazman.org/weblog/dotclear/index.php?post/2008/03/10/WebSlices-in-Firefox-who-wants-to-try-"&gt;http://www.glazman.org/weblog/dotclear/index.php?post/2008/03/10/WebSlices-in-Firefox-who-wants-to-try-&lt;/a&gt; but they’re still halfbaked (it’s obviously a quick hack and more work is needed) &lt;p&gt;&lt;a href="http://lh5.google.com/rbanks54/R9ccCrqzFvI/AAAAAAAAASw/xXDAG_pi_TA/clip_image016%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="124" alt="clip_image016" src="http://lh3.google.com/rbanks54/R9ccDLqzFwI/AAAAAAAAAS4/RZ-TE2G2IyE/clip_image016_thumb%5B1%5D" width="329" border="0"&gt;&lt;/a&gt;&lt;br&gt;When you try to mouse over the icon so you can add it, it disappears because you've left the webslice (doh!).&amp;nbsp; I’m sure there’ll be an update soon enough, but in the meantime you can right-click to add a “webchunk” which then looks like this: &lt;p&gt;&lt;a href="http://lh6.google.com/rbanks54/R9ccD7qzFxI/AAAAAAAAATA/IDtx6-20wV4/clip_image018%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="339" alt="clip_image018" src="http://lh5.google.com/rbanks54/R9ccErqzFyI/AAAAAAAAATI/VXlOabL_rI0/clip_image018_thumb%5B1%5D" width="483" border="0"&gt;&lt;/a&gt; &lt;p&gt;Oh- before you make any comments on the content this is all legit ebay search results and all stemmed from the selection on the SMH web site. &lt;p&gt;8. Oh - it does occasionally crash L&amp;nbsp; But at&amp;nbsp; least the crash reporting is OK, and when you restart you can try and restore your sessions which is OK (unless the offending page always causes the crash).&lt;br&gt;&lt;a href="http://lh4.google.com/rbanks54/R9ccFbqzFzI/AAAAAAAAATQ/UnUE--XYX4Q/clip_image020%5B4%5D"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="419" alt="clip_image020" src="http://lh3.google.com/rbanks54/R9ccGLqzF0I/AAAAAAAAATY/sVTf7DttWEY/clip_image020_thumb%5B1%5D" width="384" border="0"&gt;&lt;/a&gt; &lt;p&gt;Overall– a really good experience, but being beta I’d expect that there are still issues with it I haven’t seen in the short time I’ve used it.&amp;nbsp; Is it better than IE8?&amp;nbsp; Some people think so - &lt;a href="http://blogs.computerworld.com/battle_of_the_betas_firefox_3_beats_ie8"&gt;http://blogs.computerworld.com/battle_of_the_betas_firefox_3_beats_ie8&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/03/firefox-3-beta-4-mini-review.html' title='Firefox 3 Beta 4 - A Mini Review'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=3264246928252329080' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/3264246928252329080/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3264246928252329080'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3264246928252329080'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3236851723647498035</id><published>2008-03-11T14:05:00.001+11:00</published><updated>2008-03-11T14:05:45.370+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Improve Your Technical Presentations</title><content type='html'>&lt;p&gt;I was watching Scott Hansleman’s &lt;a href="http://sessions.visitmix.com/?selectedSearch=T22" target="_blank"&gt;ASP.NET MVC presentation&lt;/a&gt; this morning and noticed him zooming in on various screen areas and drawing around parts of the screen he was talking about during the demo.&amp;nbsp; “What the hell is he using to do that?” thought I. &lt;p&gt;Initially I thought it must’ve been a tablet thingy and that if I mentioned it &lt;a href="http://notgartner.wordpress.com/" target="_blank"&gt;Mitch&lt;/a&gt; would get all “nah-ne-nah-ne-nah-nah” on me.&amp;nbsp; But I did a quick bit of digging and learned that it’s actually a utility from the SysInternals team called ZoomIt - &lt;a href="http://www.microsoft.com/technet/sysinternals/Miscellaneous/ZoomIt.mspx"&gt;http://www.microsoft.com/technet/sysinternals/Miscellaneous/ZoomIt.mspx&lt;/a&gt; &lt;p&gt;I’ve downloaded it and played about with it and think it’s fantastic – it makes showing small fonts and display areas (such as the solution panel in Visual Studio) so much easier and avoids the usual presenters question of "Can you guys up the back see that?". I wish I’d known about this in the past. If you haven’t already seen it, I’d recommend getting it and trying it out. &lt;p&gt;P.S. Don’t worry about the size – it’s all of 74KB to download.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/03/improve-your-technical-presentations.html' title='Improve Your Technical Presentations'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=3236851723647498035' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/3236851723647498035/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3236851723647498035'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3236851723647498035'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8515447708460810856</id><published>2008-03-10T19:19:00.001+11:00</published><updated>2008-03-10T19:19:05.757+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>Castle Windsor</title><content type='html'>&lt;p&gt;Spent a couple of hours this afternoon getting to know the &lt;a href="http://www.castleproject.org" target="_blank"&gt;Castle Windsor IoC container&lt;/a&gt; again.  &lt;p&gt;I ran through the sample as a refresher and then converted a small app I had lying around to use loosely coupled services/components.  &lt;p&gt;Talk about simple, easy and powerful.&amp;nbsp; I've read enough about this thing to know how it works and played with it briefly in the past, but the latest release adds support for generics and makes it so much easier to use than before (not that it was hard).&amp;nbsp; &lt;p&gt;The effort involved in converting an existing "hard wired" app to be one using loosely coupled services connected at runtime via the plumbing magic of an IoC container is obviously dependent on the current state of the app, but even so switching my app across was only a matter of adding a few lines of code and doing a bit of refactoring.  &lt;p&gt;For instance let's say I have an app that parses some input, I could instantiate parsing and messaging services like this: &lt;pre class="codeblock"&gt;&lt;span style="color: rgb(43,145,175)"&gt;IWindsorContainer&lt;/span&gt; container = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;WindsorContainer&lt;/span&gt;();&lt;br /&gt;container.AddComponent&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;IMessageDisplay&lt;/span&gt;, &lt;span style="color: rgb(43,145,175)"&gt;ConsoleMessageDisplay&lt;/span&gt;&amp;gt;(&lt;span style="color: rgb(163,21,21)"&gt;"console.display"&lt;/span&gt;);&lt;br /&gt;container.AddComponent&amp;lt;&lt;span style="color: rgb(43,145,175)"&gt;IInputParser&lt;/span&gt;, &lt;span style="color: rgb(43,145,175)"&gt;InputParser&lt;/span&gt;&amp;gt;(&lt;span style="color: rgb(163,21,21)"&gt;"inputparser"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;If I wanted my parser to display messages to the user when there was a problem I'd normally have to wire up the connection between the classes myself, but now I don't need to.&amp;nbsp; All I need to do is have a constructor for the parser like so: &lt;pre class="codeblock"&gt;&lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;IMessageDisplay&lt;/span&gt; display;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; InputParser(&lt;span style="color: rgb(43,145,175)"&gt;IMessageDisplay&lt;/span&gt; display)&lt;br /&gt;{&lt;br /&gt;  &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.display = display;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;and the Windsor container takes care of injecting the appropriate IMessageDisplay class for me.&lt;br /&gt;&lt;p&gt;The container looks after the lifestyle (singleton, pooling, instance-per-request, etc) of each service as well and the lifecycle (instantiation and disposal) of each object. &lt;br /&gt;&lt;p&gt;All that nasty plumbing that we typically have to do is removed and our application becomes so much easier to maintain. &lt;br /&gt;&lt;p&gt;Why do we still insist on writing code in such a tightly coupled manner?&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://richardsbraindump.blogspot.com/2008/03/castle-windsor.html' title='Castle Windsor'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=13321238&amp;postID=8515447708460810856' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://richardsbraindump.blogspot.com/feeds/8515447708460810856/comments/default' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8515447708460810856'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8515447708460810856'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7129749846687615616</id><published>2008-03-10T10:09:00.001+11:00</published><updated>2008-03-10T10:09:20.174+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.