<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-28171798</id><updated>2011-04-21T21:50:25.464-04:00</updated><title type='text'>The ScratchPad</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-28171798.post-115198505433483629</id><published>2006-07-03T23:46:00.000-04:00</published><updated>2006-07-03T23:50:54.436-04:00</updated><title type='text'>The Simpler Life 1.0</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;p&gt;I have no self-control.  What started as a simple redesign of my old website turned into an endless year of tweaking and distraction. It ended in liberation.&lt;/p&gt;  &lt;p&gt;For the better part of a year, I obsessed over the little design details of my blog.  I read all about making access to information easy.  I experimented with branding and identity and style.  I lurked about in CSS and design forums.  Tinkered. Cobbled. Forged.&lt;/p&gt;  &lt;p&gt;Never happy with the results.  Many designs in the dustbin.  Why was I doing this?&lt;/p&gt;  &lt;p&gt;&lt;i&gt;Good question.&lt;/i&gt;&lt;/p&gt;  &lt;p&gt;And like that, I realised I couldn't remember what I was doing. I wanted to &lt;i&gt;write, &lt;/i&gt;not &lt;i&gt;tweak. &lt;/i&gt;I woke up.&lt;/p&gt;  &lt;p&gt;First thing to go - the original blog.  I was spending so much time fiddling with the styles, admin settings, and all the other goodies that I was left with little time to actually add content.  So goodbye old blog, hello Blogger.  Free, configurable enough, but not enough to be distracting.&lt;/p&gt;  &lt;p&gt;Next, I started using &lt;a href="http://www.backpackit.com/"&gt;Backpack&lt;/a&gt; to store ideas to write about.  When I get an idea, but can't write about it immediately, I send my Backpack a note. All my great ideas are carved into a safe place.&lt;/p&gt;  &lt;p&gt;Momentum gathering, feeling free-er.  Anthing else? What else can get scrubbed?  What about all those lists I read everyday?  My Gmail box has 15 filters for all the lists I don't read but wish I did.  Easily fixed - Login to Yahoo! Groups and set all but the mighty few groups to 'no more email please'. Oh ya, delete a few thousand unread emails.&lt;/p&gt;  &lt;p&gt;Speaking of blogs, I subscribe to a whole pile of them too.  Not anymore.  I had about 50 that I read (or tried to read). Whittled them down to 10.  &lt;br/&gt; &lt;/p&gt;  &lt;p&gt;Finally, I'm going document-free.  I repaved my laptop recently, and as part of that, I didn't put any of my old files back on my machine.  It turns out I'm not going back to the archives as much as I thought I would.  And I'm using text files more.  I've even been experimenting with &lt;a href="http://www.43folders.com/2005/08/17/life-inside-one-big-text-file/"&gt;one big text file&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;I'm free.  Free of the fiddling, tweaking, searching. Free of the guilt of not keeping up with all the info.  And free to fill this new blog full of content.&lt;/p&gt;  &lt;p&gt;Simpler life?  Time will tell. For now, I'm back in control.&lt;br/&gt; &lt;/p&gt;  &lt;p&gt;&lt;br/&gt; &lt;/p&gt;  &lt;p&gt;&lt;br/&gt; &lt;/p&gt;  &lt;br/&gt; &lt;p style="font-size:10px;text-align:right;"&gt;technorati tags:&lt;a href="http://technorati.com/tag/simple" rel="tag"&gt;simple&lt;/a&gt;, &lt;a href="http://technorati.com/tag/tweaking" rel="tag"&gt;tweaking&lt;/a&gt;, &lt;a href="http://technorati.com/tag/less" rel="tag"&gt;less&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-115198505433483629?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/115198505433483629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=115198505433483629' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/115198505433483629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/115198505433483629'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2006/07/simpler-life-10.html' title='The Simpler Life 1.0'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-115198167698017795</id><published>2006-07-03T22:50:00.000-04:00</published><updated>2006-07-03T22:54:37.186-04:00</updated><title type='text'>WriteRoom</title><content type='html'>&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;Would someone please create &lt;a href="http://www.hogbaysoftware.com/product/writeroom"&gt;this &lt;/a&gt;for Windows?  Now!&lt;br/&gt; &lt;p style="font-size:10px;text-align:right;"&gt;technorati tags:&lt;a href="http://technorati.com/tag/Software" rel="tag"&gt;Software&lt;/a&gt;, &lt;a href="http://technorati.com/tag/Ideas" rel="tag"&gt;Ideas&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-115198167698017795?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/115198167698017795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=115198167698017795' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/115198167698017795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/115198167698017795'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2006/07/writeroom.html' title='WriteRoom'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895290536226077</id><published>2005-09-23T21:37:00.000-04:00</published><updated>2006-05-30T21:26:20.973-04:00</updated><title type='text'>TDD and the Single Printer</title><content type='html'>&lt;span style="font-style: italic;"&gt;Disclaimer: This article does not show all code and possible refactorings. It gives just enough to show how a design for testable printing evolved.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;blockquote&gt;Credit for the ideas here go to all members of my team who paired and collaborated on the reporting story.&lt;/blockquote&gt;&lt;/span&gt;Let's get started. Of course, that's the problem, isn't it? Where do you start when you don't know where to start? Well, me, I cheat. The XP community over at Yahoo! helped kick the brain into gear with some helpful starters.&lt;br /&gt;&lt;br /&gt;So here's what I decided: For now, I'm going to focus on what I know how to test, and maybe that will help me figure out how to test the stuff I don't know how to test. Initial thinking is that I can probably figure out how to test-drive content and presentation, and maybe that will lead me to a testable design for the printing part.&lt;br /&gt;&lt;br /&gt;Ok, enough talking, lets see some tests. I'm lazy, I want to test the easiest thing - for me, that's probably the content.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class ContentTest&lt;br /&gt;{&lt;br /&gt; [Test]&lt;br /&gt; public void SeldomReferredToStats(){&lt;br /&gt;         Assert.IsTrue(false);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Red! At least the NUnit framework works. Ok, so I'm going to try to get the content for the very first section of the report.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[TestFixture]&lt;br /&gt;public class ContentTest&lt;br /&gt;{&lt;br /&gt;   [Test]&lt;br /&gt;   public void SeldomReferredToStats()&lt;br /&gt;   {&lt;br /&gt;      Content content = new Content();&lt;br /&gt;      Assert.AreEqual(670, content.YawnFactor);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now the Content class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Content&lt;br /&gt;{&lt;br /&gt;   public int YawnFactor { get {return 670;} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Green!&lt;br /&gt;&lt;br /&gt;That wasn't too interesting. But to tell you the truth, the Content class was not that exciting. In the real application, it served as a place to gather a whole bunch of different objects that were alive in the system and turn them into content. For now, let's pretend that we wrote a bunch of tests that let us develop a Content class that provides us with all the text for the Seldom Referred-to Stats section. We'll revisit this class in a bit, when it gets more interesting.&lt;br /&gt;&lt;br /&gt;Now that I've got this content, I have to do something with it. Looking at the report, it makes sense to divide the content from the way it is presented.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest&lt;br /&gt;{&lt;br /&gt; [Test]&lt;br /&gt; public void Title()&lt;br /&gt; {&lt;br /&gt;   Presentation presentation = new Presentation();&lt;br /&gt;   Assert.AreEqual("TPS Report", presentation.Title);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt;   public string Title { get {return "TPS Report";} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Great. I've got a title. Now I need that title underlined and centered. Time to stop coding for a second and think about this. Looking at the report, I can see a title, a bunch of sections, and a footer. And to me, the title and footer are just sections, so maybe the report presentation is just a bunch of sections. Enough talking, let's do some walking.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[TestFixture]&lt;br /&gt;public class SectionTest&lt;br /&gt;{&lt;br /&gt; [Test]&lt;br /&gt; public void TitleSection()&lt;br /&gt; {&lt;br /&gt;    Assert.AreEqual("TPS Report", section.Title);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt;  public string Title { get {return "TPS Report";} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That passes. And there is duplication, which reminds me that I have to actually use the new class.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest&lt;br /&gt;{&lt;br /&gt;  [Test]&lt;br /&gt;  public void Title()&lt;br /&gt;  {&lt;br /&gt;     Presentation presentation = new Presentation();&lt;br /&gt;     Assert.AreEqual("TPS Report", presentation.TitleSection.Title);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt;   private Section titleSection = new Section();&lt;br /&gt;   public Section Title { get {return titleSection;} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Looking good. I guess I could do one of two things. I could try to get the underline in there, or I could try to do the next section, because right now the Section class is pretty useless for anything but a title. Underlining interests me more, so I'm going to try that. I don't know how.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class SectionTest&lt;br /&gt;{&lt;br /&gt;   [Test]&lt;br /&gt;   public void TitleSection()&lt;br /&gt;   {&lt;br /&gt;      Section section = new Section();&lt;br /&gt;      Assert.AreEqual("TPS Report", section.Title);&lt;br /&gt;      Assert.AreEqual("-----", section.Line);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt;    public string Title { get {return "TPS Report";} }&lt;br /&gt;    public string Line { get {return "-----";} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Another pass, update the Presentation test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest&lt;br /&gt;{&lt;br /&gt;  [Test]&lt;br /&gt;  public void Title()&lt;br /&gt;  {&lt;br /&gt;      Presentation presentation = new Presentation();&lt;br /&gt;      Assert.AreEqual("TPS Report", presentation.TitleSection.Title);&lt;br /&gt;      Assert.AreEqual("-----", presentation.TitleSection.Line);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And green. If you are looking at this about now, and saying that the presentation test doesn't add much value, think again. It adds a very important piece of value. It says to me that the design is kind of wierd. I'm exposing data that maybe shouldn't be exposed. I'm not happy about that. Also, it tells me that if I keep going down this path without being careful, I may not get what I want. If that sounds kind of non-committal, it is. I don't know what is wrong, but I get the sense, even this early, that something doesn't jive. Let's keep going to see if we can find out what doesnt' work here.&lt;br /&gt;&lt;br /&gt;So, maybe now would be a good time to figure out how to print. Not quite yet. One thing that is wrong is that I have some dead code. The Section class isn't employed at the moment, and in my books, that means it either gets used, or it gets tossed. Maybe I should have waited till I needed that class. In this case it did some good to start with it because it got the juices flowing and the confidence up.&lt;br /&gt;&lt;br /&gt;Back to the code.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class SectionTest&lt;br /&gt;{&lt;br /&gt;    [Test]&lt;br /&gt;    public void TitleSection()&lt;br /&gt;    {&lt;br /&gt;       Section section = new Section();&lt;br /&gt;       Assert.AreEqual("TPS Report", section.Title);&lt;br /&gt;       Assert.AreEqual("-----", section.Line);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [Test]&lt;br /&gt;    public void ContentSection()&lt;br /&gt;    {&lt;br /&gt;       Section section = new Section();&lt;br /&gt;       Assert.AreEqual("Section Title", section.Title);&lt;br /&gt;       Assert.AreEqual("A bit of content" section.Line);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Red, of course. We need a way to set the title and content.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void ContentSection()&lt;br /&gt;{&lt;br /&gt;  Section section = new Section();&lt;br /&gt;  section.Title = "Section Title";&lt;br /&gt;  section.Line = "A bit of content";&lt;br /&gt;&lt;br /&gt;  Assert.AreEqual("Section Title", section.Title);&lt;br /&gt;  Assert.AreEqual("A bit of content" section.Line);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt;  private string title;&lt;br /&gt;  private string line;&lt;br /&gt;&lt;br /&gt;  public string Title {&lt;br /&gt;    get {return title;}&lt;br /&gt;    set {title = value;}&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string Line {&lt;br /&gt;    get {return line;}&lt;br /&gt;    set {line = value;}&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That test passes now, but the TitleSection and Presentation tests fail. One at a time:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt;  private Section titleSection = new Section();&lt;br /&gt;  public Presentation()&lt;br /&gt;  {&lt;br /&gt;      titleSection.Title = "TPS Report";&lt;br /&gt;      titleSection.Line = "-----";&lt;br /&gt;  }&lt;br /&gt;  public Section Title { get {return titleSection;}}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Presentation tests are now passing. What about that section test?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TitleSection()&lt;br /&gt;{&lt;br /&gt;  Section section = new Section();&lt;br /&gt;  section.Title = "TPS Report";&lt;br /&gt;  section.Line = "-----";&lt;br /&gt;  Assert.AreEqual("TPS Report", section.Title);&lt;br /&gt;  Assert.AreEqual("-----", section.Line);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hmmmm....&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TitleSection()&lt;br /&gt;{&lt;br /&gt;  Section section = new Section();&lt;br /&gt;  section.Title = "TPS Report";&lt;br /&gt;  section.Line = HorizontalLine;&lt;br /&gt;  Assert.AreEqual("TPS Report", section.Title);&lt;br /&gt;  Assert.AreEqual(HorizontalLine, section.Line);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private string HorizontalLine { get {return "-----";} }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Green now, and I have a new idea. There is a difference between the types of lines that are in a section. It seems as though a section can have a horizontal line or a text line. I'm going to put that on the back burner for a second or two. I think that what I really need to do is let a section have more lines.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class SectionTest&lt;br /&gt;{&lt;br /&gt; ...&lt;br /&gt;&lt;br /&gt; [Test]&lt;br /&gt; public void ContentSection()&lt;br /&gt; {&lt;br /&gt;      Section section = new Section();&lt;br /&gt;      section.Title = "Section Title";&lt;br /&gt;      section.AddLine("A bit of content");&lt;br /&gt;      section.AddLine("A bit more content");&lt;br /&gt;&lt;br /&gt;      Assert.AreEqual("Section Title", section.Title);&lt;br /&gt;      Assert.AreEqual("A bit of content" section.Lines[0]);&lt;br /&gt;      Assert.AreEqual("A bit more content" section.Lines[1]);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt;  private string title;&lt;br /&gt;  private ArrayList lines;&lt;br /&gt;&lt;br /&gt;  public string Title {&lt;br /&gt;    get {return title;}&lt;br /&gt;    set {title = value;}&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public string[] Lines { get {return (string[])lines.ToArray(typeof(string));}&lt;br /&gt;  public void AddLine(string line) { lines.Add(line); }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Of course, this passes, the other tests fail, we refactor those, and they all pass. Now I feel a little better. But I don't like this code, in the Section test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  section.AddLine(HorizontalLine);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lets make it this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  section.AddHorizontalLine();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And implement it this way&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt; ...&lt;br /&gt; public void AddHorizontalLine() { AddLine("-----"); }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Time for a recap of all the classes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt; private string title;&lt;br /&gt; private ArrayList lines;&lt;br /&gt;&lt;br /&gt; public string Title {&lt;br /&gt;    get {return title;}&lt;br /&gt;    set {title = value;}&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public string[] Lines { get {return (string[])lines.ToArray(typeof(string));}&lt;br /&gt; public void AddLine(string line) { lines.Add(line); }&lt;br /&gt; public void AddHorizontalLine() {lines.Add("-----") }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt; private Section titleSection = new Section();&lt;br /&gt; public Presentation()&lt;br /&gt; {&lt;br /&gt;   titleSection.Title = "TPS Report";&lt;br /&gt;   titleSection.AddHorizontalLine();&lt;br /&gt; }&lt;br /&gt; public Section Title { get {return titleSection;} }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Content&lt;br /&gt;{&lt;br /&gt; public int YawnFactor { get {return 670;} }&lt;br /&gt; ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Time to make that Content class a full contributor. Some of my team mates would have axed it already, since it's not being used anywhere but inside of a test.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest&lt;br /&gt;{&lt;br /&gt; [Test]&lt;br /&gt; public void TPSReport()&lt;br /&gt; {&lt;br /&gt;    Presentation presentation = new Presentation(new Content());&lt;br /&gt;    Assert.AreEqual("TPS Report", presentation.TitleSection.Title);&lt;br /&gt;    Assert.AreEqual("-----", presentation.TitleSection.Lines[0]);&lt;br /&gt;    Assert.AreEqual("Seldom Referred-to Stats",         presentation.SeldomReferredToStatsSection.Title);&lt;br /&gt;    Assert.AreEqual("Yawn Factor:\t\t\t\t\t670",    presentation.SeldomReferredToStatsSection.Lines[0]);&lt;br /&gt;    Assert.AreEqual("Average of a Bunch of Averages:\t\t\t\t\t1", presentation.SeldomReferredToStatsSection.Lines[1]);&lt;br /&gt;    Assert.AreEqual("Sleep Quotient\t\t\t\t\tPurple", presentation.SeldomReferredToStatsSection.Lines[2]);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt; private Section titleSection = new Section();&lt;br /&gt; private Content content;&lt;br /&gt; public Presentation(Content content)&lt;br /&gt; {&lt;br /&gt;     titleSection.Title = "TPS Report";&lt;br /&gt;     titleSection.AddHorizontalLine();&lt;br /&gt;     this.content = content;&lt;br /&gt; }&lt;br /&gt; public Section Title { get {return titleSection;} }&lt;br /&gt;&lt;br /&gt; public Section SeldomReferredToStatsSection&lt;br /&gt; {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;      Section section = new Section();&lt;br /&gt;      section.Title = "Seldom Referred-to Stats";&lt;br /&gt;      section.AddLine("Yawn Factor:\t\t\t\t\t" + content.YawnFactor);&lt;br /&gt;      section.AddLine("Average of a Bunch of Averages:\t\t" + content.AverageOfAverages);&lt;br /&gt;      section.AddLine("Sleep Quotient\t\t\t\t\t" + content.SleepQuotient);&lt;br /&gt;      return section;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That passes. Notice I took out the title test in the Presentation test. It wasn't needed anymore. While doing that last test, I noticed some more refactoring that could be done. That silly titleSection needs to go:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt; private Content content;&lt;br /&gt; public Presentation(Content content)&lt;br /&gt; {&lt;br /&gt;     this.content = content;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Section Title {&lt;br /&gt; {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        Section section = new Section();&lt;br /&gt;        section.Title = "TPS Report";&lt;br /&gt;        section.AddHorizontalLine();&lt;br /&gt;        return section;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Section SeldomReferredToStatsSection&lt;br /&gt; {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        Section section = new Section();&lt;br /&gt;        section.Title = "Seldom Referred-to Stats";&lt;br /&gt;        section.AddLine(content.YawnFactor);&lt;br /&gt;        section.AddLine(content.AverageOfAverages);&lt;br /&gt;        section.AddLine(content.SleepQuotient);&lt;br /&gt;        return section;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's a little better. But why am I distiguishing between the Title section and the SeldomReferredToStatsSection? It's an iffy call. I could leave it because it is readable. But I have an idea about how to refactor my tests that I can't give up:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt;  private Content content;&lt;br /&gt;  public Presentation(Content content)&lt;br /&gt;  {&lt;br /&gt;       this.content = content;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public Section[] Sections {&lt;br /&gt;      get{return new Section[] {Title, SeldomReferredToStatsSection};}&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Section Title {&lt;br /&gt;     get&lt;br /&gt;     {&lt;br /&gt;        Section section = new Section();&lt;br /&gt;        section.Title = "TPS Report";&lt;br /&gt;        section.AddHorizontalLine();&lt;br /&gt;        return section;&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Section SeldomReferredToStatsSection{&lt;br /&gt;      get&lt;br /&gt;      {&lt;br /&gt;          Section section = new Section();&lt;br /&gt;          section.Title = "Seldom Referred-to Stats";&lt;br /&gt;          section.AddLine("Yawn Factor:\t\t\t\t\t" + content.YawnFactor);&lt;br /&gt;          section.AddLine("Average of a Bunch of Averages:\t\t" + content.AverageOfAverages);&lt;br /&gt;          section.AddLine("Sleep Quotient\t\t\t\t\t" + content.SleepQuotient);&lt;br /&gt;          return section;&lt;br /&gt;       }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now I can do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest&lt;br /&gt;{&lt;br /&gt; Section sectionUnderTest;&lt;br /&gt;&lt;br /&gt; [Test]&lt;br /&gt; public void TPSReport()&lt;br /&gt; {&lt;br /&gt;    Presentation presentation = new Presentation(new Content());&lt;br /&gt;&lt;br /&gt;     sectionUnderTest = presentation.Sections[0];&lt;br /&gt;     CheckTitle("TPS Report");&lt;br /&gt;     CheckLine(0, "-----");&lt;br /&gt;&lt;br /&gt;     sectionUnderTest = presentation.Sections[1];&lt;br /&gt;     CheckTitle("Seldom Referred-to Stats");&lt;br /&gt;     CheckLine(0, "Yawn Factor:ttttt670");&lt;br /&gt;     CheckLine(1, "Average of a Bunch of Averages:tt1");&lt;br /&gt;     CheckLine(2, "Sleep QuotienttttttPurple");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void CheckTitle(string expected)&lt;br /&gt; {&lt;br /&gt;     Assert.AreEqual(expected, sectionUnderTest.Title);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void CheckLine(int line, string expected)&lt;br /&gt; {&lt;br /&gt;     Assert.AreEqual(expected, sectionUnderTest.Lines[line]);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I like that! Now that I have a bit of a report, it's time, I think, to start printing. This is where I step back and say, what do we have and where can we go?&lt;br /&gt;&lt;br /&gt;Well, printers print reports, don't they? I guess we could start there. First, I need a printer. I still don't know how to test printing, so I'm going to start by creating my own printer that's not really a printer.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class MyOwnPrinterTest&lt;br /&gt;{&lt;br /&gt;   private string expectedReport = "TPS Report\r\n" +&lt;br /&gt;                                   "-----\r\n" +&lt;br /&gt;                                   "Yawn Factor\t\t\t\t\t670\r\n" +&lt;br /&gt;                                   "Average of a Bunch of Averages:\t\t1\r\n" +&lt;br /&gt;                                   "Sleep Quotient\t\t\t\t\tPurple";&lt;br /&gt;&lt;br /&gt;   [Test]&lt;br /&gt;   public void PrintReport()&lt;br /&gt;   {&lt;br /&gt;       Presentation presentation = new Presentation(new Content());&lt;br /&gt;       MyOwnPrinter printer = new MyOwnPrinter();&lt;br /&gt;       printer.Print(presentation);&lt;br /&gt;       Assert.AreEqual(expectedReport, printer.PrintedPage);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyOwnPrinter&lt;br /&gt;{&lt;br /&gt; public StringWriter Page;&lt;br /&gt;&lt;br /&gt; public void Print(Presentation presentation)&lt;br /&gt; {&lt;br /&gt;    foreach (Section section in presentation.Sections)&lt;br /&gt;    {&lt;br /&gt;          Page.WriteLine(section.Title);&lt;br /&gt;          foreach (string line in section.Lines)&lt;br /&gt;               Page.WriteLine(line);&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I knew that putting all the sections into an array would make sense. So there we have it. We have our report printing to a fake printer. But we aren't done yet. We need this to go to a real printer. The reason I made the fake printer first was simple - make sure the report can be printed. If a fake printer can define an interface to print the report to a stream, then the real printer can implement the same interface and change the underlying mechanics.&lt;br /&gt;&lt;br /&gt;But, once again, I'm not happy. We are so close, but the interface doesn't seem right. For instance, how can I draw '-----' as a nice horizontal line on a real printer? I probably have the interface wrong. Let's think about this for a minute.&lt;br /&gt;&lt;br /&gt;What does a printer do? It prints reports, right? Well, that was my first thought. But really, a printer is just like a monitor or even a web browser. And all those things provide services for rendering images. If that's the case, then maybe I have the relationship between Presentation and MyOwnPrinter backwards. Maybe Presentation draws itself using the services that MyOwnPrinter offers.&lt;br /&gt;&lt;br /&gt;Lets try that. I feel a big refactoring coming on.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class MyOwnPrinterTest&lt;br /&gt;{&lt;br /&gt;  private string expectedReport = "TPS Report\r\n" +&lt;br /&gt;                                  "-----\r\n" +&lt;br /&gt;                                  "Yawn Factor\t\t\t\t\t670\r\n" +&lt;br /&gt;                                  "Average of a Bunch of Averages:\t\t1\r\n" +&lt;br /&gt;                                  "Sleep Quotient\t\t\t\t\tPurple";&lt;br /&gt;&lt;br /&gt;   [Test]&lt;br /&gt;   public void PrintReport()&lt;br /&gt;   {&lt;br /&gt;       Presentation presentation = new Presentation(new Content());&lt;br /&gt;       MyOwnPrinter printer = new MyOwnPrinter();&lt;br /&gt;       presentation.To(printer);&lt;br /&gt;       Assert.AreEqual(expectedReport, printer.PrintedPage);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt;   private Content content;&lt;br /&gt;   public Presentation(Content content) {&lt;br /&gt;        this.content = content;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public Section[] Sections {&lt;br /&gt;      get{return new Section[] {Title, SeldomReferredToStatsSection};}}&lt;br /&gt;&lt;br /&gt;   public void To(MyOwnPrinter printer)&lt;br /&gt;   {&lt;br /&gt;      foreach (Section section in Sections){&lt;br /&gt;         printer.Render(section.Title);&lt;br /&gt;         foreach (string line in section.Lines)&lt;br /&gt;            printer.Render(line);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private Section Title {&lt;br /&gt;      get{&lt;br /&gt;         Section section = new Section();&lt;br /&gt;         section.Title = "TPS Report";&lt;br /&gt;         section.AddHorizontalLine();&lt;br /&gt;         return section;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   private Section SeldomReferredToStatsSection {&lt;br /&gt;      get{&lt;br /&gt;         Section section = new Section();&lt;br /&gt;         section.Title = "Seldom Referred-to Stats";&lt;br /&gt;         section.AddLine("Yawn Factor:ttttt" + content.YawnFactor);&lt;br /&gt;         section.AddLine("Average of a Bunch of Averages:tt" + content.AverageOfAverages);&lt;br /&gt;         section.AddLine("Sleep Quotientttttt" + content.SleepQuotient);&lt;br /&gt;         return section;&lt;br /&gt;         }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyOwnPrinter&lt;br /&gt;{&lt;br /&gt;  public StringWriter Page;&lt;br /&gt;&lt;br /&gt;  public void Render(string line)&lt;br /&gt;  {&lt;br /&gt;     Page.WriteLine(line);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I like that better. MyOwnPrinter now doesn't know about Presenation or it's structure. It just knows how to print stuff. Cool. But that still doesn't fix the problem of printing horizontal lines on a real printer. So maybe a service that the printer offers is to print horizontal lines. Before I even write a test for that, I don't want to have a bunch of 'if string == '-----' ' code everywhere. Maybe it's time to go back to that idea of lines.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class HorizontalLineTest&lt;br /&gt;{&lt;br /&gt; [Test]&lt;br /&gt; public void HorizontalLine()&lt;br /&gt; {&lt;br /&gt;    MyOwnPrinter printer = new MyOwnPrinter();&lt;br /&gt;    printer.Render(new HorizontalLine());&lt;br /&gt;    Assert.AreEqual("-----", printer.Page);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will seem a little tricky, but here is what is in my head - the printer knows how to draw a horizontal line, and the HorizontalLine class uses the printer. Watch:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class HorizontalLine&lt;br /&gt;{&lt;br /&gt; public void Draw(MyOwnPrinter printer)&lt;br /&gt; {&lt;br /&gt;     printer.RenderHorizontalLine();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and then add the service to the printer:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class MyOwnPrinter&lt;br /&gt;{&lt;br /&gt;  public StringWriter Page;&lt;br /&gt;&lt;br /&gt;  public void Render(string line)&lt;br /&gt;  {&lt;br /&gt;    Page.WriteLine(line);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void RenderHorizontalLine()&lt;br /&gt;  {&lt;br /&gt;    Render("-----");&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And we have a green bar. And I love that design, because now I can see how to extend it a little and make any line draw itself by extracting the Draw method into an abstract class.&lt;br /&gt;&lt;br /&gt;Let's refactor a bit to use that concept:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt; private string title;&lt;br /&gt; private ArrayList lines;&lt;br /&gt;&lt;br /&gt; public string Title {&lt;br /&gt;    get {return title;}&lt;br /&gt;    set {title = value;}&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Line[] Lines { get {return (Line[])lines.ToArray(typeof(Line));}&lt;br /&gt; public void AddLine(string line) { lines.Add(new TextLine(line)); }&lt;br /&gt; public void AddHorizontalLine() {lines.Add(new HorizontalLine()) }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The test can no longer access the data directly because it is nice and encapsulated. Remember that test refactoring earlier? Maybe that can help:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public void TestHelper&lt;br /&gt;{&lt;br /&gt; Section sectionUnderTest;&lt;br /&gt; MyOwnPrinter printer;&lt;br /&gt;&lt;br /&gt; [SetUp]&lt;br /&gt; public void SetUp()&lt;br /&gt; {&lt;br /&gt;    printer = new MyOwnPrinter();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;  protected void CheckTitle(string expected)&lt;br /&gt;  {&lt;br /&gt;     sectionUnderTest.Title.Draw(printer)&lt;br /&gt;     Assert.AreEqual(expected, printer.Page );&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  protected void CheckLine(int line, string expected)&lt;br /&gt;  {&lt;br /&gt;     sectionUnderTest.Lines[line].Draw(printer)&lt;br /&gt;     Assert.AreEqual(expected, printer.Page);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Use it in the Section Test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class SectionTest : TestHelper&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void TitleSection()&lt;br /&gt;{&lt;br /&gt;   Section section = new Section();&lt;br /&gt;   section.AddTitle("TPS Report");&lt;br /&gt;   section.AddHorizontalLine();&lt;br /&gt;   sectionUnderTest = section;&lt;br /&gt;   CheckTitle("TPS Report");&lt;br /&gt;   CheckLine(0, HorizontalLine);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Test]&lt;br /&gt;public void ContentSection()&lt;br /&gt;{&lt;br /&gt;   Section section = new Section();&lt;br /&gt;   section.AddTitle("Section Title");&lt;br /&gt;   section.AddLine("A bit of content");&lt;br /&gt;   section.AddLine("A bit more content");&lt;br /&gt;   sectionUnderTest = section;&lt;br /&gt;   CheckTitle("Section Title");&lt;br /&gt;   CheckLine(0, "A bit of content");&lt;br /&gt;   CheckLine(1, "A bit more content");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private string HorizontalLine { get {return "-----";} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the Presentation test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class PresentationTest:TestHelper&lt;br /&gt;{&lt;br /&gt;[Test]&lt;br /&gt;public void TPSReport()&lt;br /&gt;{&lt;br /&gt;    Presentation presentation = new Presentation(new Content());&lt;br /&gt;    sectionUnderTest = presentation.Sections[0];&lt;br /&gt;    CheckTitle("TPS Report");&lt;br /&gt;    CheckLine(0, "-----");&lt;br /&gt;&lt;br /&gt;    sectionUnderTest = presentation.Sections[1];&lt;br /&gt;    CheckTitle("Seldom Referred-to Stats");&lt;br /&gt;    CheckLine(0, "Yawn Factor:ttttt670");&lt;br /&gt;    CheckLine(1, "Average of a Bunch of Averages:tt1");&lt;br /&gt;    CheckLine(2, "Sleep QuotienttttttPurple");&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and refactor the classes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt; private Content content;&lt;br /&gt; public Presentation(Content content)&lt;br /&gt; {&lt;br /&gt;    this.content = content;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Section[] Sections {&lt;br /&gt;   get{return new Section[] {Title, SeldomReferredToStatsSection};}}&lt;br /&gt;&lt;br /&gt; public void To(MyOwnPrinter printer)&lt;br /&gt; {&lt;br /&gt;   foreach (Section section in Sections) {&lt;br /&gt;       printer.Render(section.Title);&lt;br /&gt;       foreach (Line line in section.Lines)&lt;br /&gt;          printer.Render(line);&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Section Title {&lt;br /&gt;     get {&lt;br /&gt;        Section section = new Section();&lt;br /&gt;        section.AddTitle("TPS Report");&lt;br /&gt;        section.AddHorizontalLine();&lt;br /&gt;        return section;&lt;br /&gt;     }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private Section SeldomReferredToStatsSection{&lt;br /&gt;      get {&lt;br /&gt;        Section section = new Section();&lt;br /&gt;        section.AddTitle("Seldom Referred-to Stats");&lt;br /&gt;        section.AddLine("Yawn Factor:ttttt" + content.YawnFactor);&lt;br /&gt;        section.AddLine("Average of a Bunch of Averages:tt" + content.AverageOfAverages);&lt;br /&gt;        section.AddLine("Sleep Quotientttttt" + content.SleepQuotient);&lt;br /&gt;        return section;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt; private ArrayList lines;&lt;br /&gt; public Line[] Lines { get {return (Line[])lines.ToArray(typeof(Line));}&lt;br /&gt; public void AddTitle(string title){ AddLine(title)};&lt;br /&gt; public void AddLine(string line) { lines.Add(new TextLine(line)); }&lt;br /&gt; public void AddHorizontalLine() {lines.Add(new HorizontalLine()) }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And done. Well almost, I need to add a real printer. I think it's safe to extract an interface from my own printer:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class Printer&lt;br /&gt;{&lt;br /&gt; public virtual void Render(string text){}&lt;br /&gt; public virtual void RenderHorizontalLine(){}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Any printer that implements that interface can now be used to print presentations. You will see the final refactoring in the class recap at the end. We've finally come down to the very bottom - using a real printer to print. And really, it shouldn't be too hard to create a class that represents the real printer. In this case, I'm going to show you the class first, pseudo-coded:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class RealPrinter : Printer&lt;br /&gt;{&lt;br /&gt; Graphics printerSurface;&lt;br /&gt; public override Render(string text)&lt;br /&gt; {&lt;br /&gt;    printerSurface.DrawString(text);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public override RenderHorizontalLine()&lt;br /&gt; {&lt;br /&gt;    printerSurface.DrawLine(...)&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Pass that to the Presentation class, and everything should draw. But where is the test? Ok, this may seem a little anti-climactic, but to test this, we write a very simple test:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[TestFixture]&lt;br /&gt;public class RealPrinterTest&lt;br /&gt;{&lt;br /&gt;[Test]&lt;br /&gt;public void LookAtTPSReport()&lt;br /&gt;{&lt;br /&gt;   Presentation presentation = new Presentation(new Content());&lt;br /&gt;   LookAt(presentation);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private void LookAt(Presentation presentation)&lt;br /&gt;{&lt;br /&gt;   presentation.To(printer);&lt;br /&gt;   System.PrintPreview();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(GRIN) I hope you haven't gotten this far, expecting an out of this world way of testing the actual printed page, only to feel like strangling me because I am using the print preview capabilities of the .Net framework (which I have pseudo-coded here because it is really not important to understanding the design).&lt;br /&gt;&lt;br /&gt;However, the reason we feel confident doing this is because we have used TDD to drive every single part of the design right down to the interface to the printer. So we know that the lines, sections, and layout work properly before putting ink on the paper. By the time we get to the printer, we are dealing with a thin wrapper around graphics calls. And we don't test low level graphics calls to the OS because we won't be changing that stuff. What we have done is isolated the printer as much as possible so that we could have confidence in every other piece of the design.&lt;br /&gt;&lt;br /&gt;So that's it for now. I'm not going to publish how we put the graphics or tabular elements on the page, or how we did the footers, or pagination, or any other detail. I have to maintain some element of mystery. And I need to keep my job, so I can't give away all our secrets to the public :) Look me up on the XP mailing list if you have questions about those things (hint - it's the same as all the other lines).&lt;br /&gt;&lt;br /&gt;Here is a recap of all classes, sans tests:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public abstract class Printer&lt;br /&gt;{&lt;br /&gt; public virtual void Render(string text){}&lt;br /&gt; public virtual void RenderHorizontalLine(){}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class RealPrinter : Printer&lt;br /&gt;{&lt;br /&gt; Graphics printerSurface;&lt;br /&gt; public override Render(string text)&lt;br /&gt; {&lt;br /&gt;   printerSurface.DrawString(text);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public override RenderHorizontalLine()&lt;br /&gt; {&lt;br /&gt;   printerSurface.DrawLine(...)&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyOwnPrinter : Printer&lt;br /&gt;{&lt;br /&gt; public StringWriter Page;&lt;br /&gt;&lt;br /&gt; public override void Render(string line)&lt;br /&gt; {&lt;br /&gt;    Page.WriteLine(line);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public override void RenderHorizontalLine()&lt;br /&gt; {&lt;br /&gt;    Render("-----");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Presentation&lt;br /&gt;{&lt;br /&gt; private Content content;&lt;br /&gt; public Presentation(Content content)&lt;br /&gt; {&lt;br /&gt;   this.content = content;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Section[] Sections {&lt;br /&gt;   get{return new Section[] {Title, SeldomReferredToStatsSection};}}&lt;br /&gt;&lt;br /&gt; public void To(Printer printer)&lt;br /&gt; {&lt;br /&gt;    foreach (Section section in Sections) {&lt;br /&gt;         printer.Render(section.Title);&lt;br /&gt;         foreach (Line line in section.Lines){&lt;br /&gt;            printer.Render(line);&lt;br /&gt;         }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private Section Title {&lt;br /&gt;    get {&lt;br /&gt;       Section section = new Section();&lt;br /&gt;       section.AddTitle("TPS Report");&lt;br /&gt;       section.AddHorizontalLine();&lt;br /&gt;       return section;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private Section SeldomReferredToStatsSection {&lt;br /&gt;    get {&lt;br /&gt;       Section section = new Section();&lt;br /&gt;       section.AddTitle("Seldom Referred-to Stats");&lt;br /&gt;       section.AddLine("Yawn Factor:ttttt" + content.YawnFactor);&lt;br /&gt;       section.AddLine("Average of a Bunch of Averages:tt" + content.AverageOfAverages);&lt;br /&gt;       section.AddLine("Sleep Quotientttttt" + content.SleepQuotient);&lt;br /&gt;       return section;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Section&lt;br /&gt;{&lt;br /&gt; private ArrayList lines;&lt;br /&gt; public Line[] Lines { get {return (Line[])lines.ToArray(typeof(Line));}&lt;br /&gt; public void AddTitle(string title){ AddLine(title)};&lt;br /&gt; public void AddLine(string line) { lines.Add(new TextLine(line)); }&lt;br /&gt; public void AddHorizontalLine() {lines.Add(new HorizontalLine()) }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class HorizontalLine&lt;br /&gt;{&lt;br /&gt; public void Draw(Printer printer)&lt;br /&gt; {&lt;br /&gt;    printer.RenderHorizontalLine();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Content&lt;br /&gt;{&lt;br /&gt;...&lt;br /&gt;public int YawnFactor { get {return 670;} }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895290536226077?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895290536226077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895290536226077' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895290536226077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895290536226077'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/09/tdd-and-single-printer.html' title='TDD and the Single Printer'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895286639838614</id><published>2005-09-21T21:34:00.000-04:00</published><updated>2006-05-30T20:40:15.916-04:00</updated><title type='text'>TDD and the Single Printer: Introduction</title><content type='html'>&lt;p&gt;Test-driving some development is impossible.  Ok, really hard.  Alright, not hard, but really tedious.  Ok, not tedious, but...&lt;/p&gt; &lt;p&gt;I find myself going through that loop of thought when I don't know how to start using TDD on a programming problem. The catch is, TDD is perfect for &lt;i&gt;exactly&lt;/i&gt; this type of development!&lt;/p&gt; &lt;p&gt;And so, I present my latest problem to which I thought I could not possibly use TDD, but ended up using it to create a very nice design, the likes of which will not be rivalled in all of programming lore! Ok, that's stretching it a bit, but the design made me happy, and in my books that counts for something.&lt;/p&gt; &lt;p&gt;Here's the story: Print an unmodifiable report to the printer when the user hits the 'Print' button. Use the following mockup as a guide:&lt;/p&gt; &lt;p&gt;&lt;img src="http://www.agilelectric.com/images/PrinterTDD.jpg" mce_src="http://www.agilelectric.com/images/PrinterTDD.jpg" /&gt;&lt;/p&gt; &lt;p&gt;The mockup above contains all the elements of the real report that we were asked to add to the real product. And so the story begins...&lt;/p&gt; &lt;p&gt;Next time:  Where do we start?!?! &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895286639838614?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895286639838614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895286639838614' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895286639838614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895286639838614'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/09/tdd-and-single-printer-introduction.html' title='TDD and the Single Printer: Introduction'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895275137477623</id><published>2005-08-12T21:32:00.000-04:00</published><updated>2006-05-30T20:39:37.233-04:00</updated><title type='text'>Incentive To Change</title><content type='html'>&lt;p&gt;Summer's a good time to catch up on some reading.  A &lt;a href="http://www.amazon.com/exec/obidos/tg/detail/-/006073132X/qid=1123822715/sr=8-1/ref=pd_bbs_1/002-9630442-0355202?v=glance&amp;s=books&amp;amp;n=507846" mce_href="http://www.amazon.com/exec/obidos/tg/detail/-/006073132X/qid=1123822715/sr=8-1/ref=pd_bbs_1/002-9630442-0355202?v=glance&amp;s=books&amp;amp;n=507846"&gt;book&lt;/a&gt;, a &lt;a href="http://www.xprogramming.com/xpmag/AgileTopDown.htm" mce_href="http://www.xprogramming.com/xpmag/AgileTopDown.htm"&gt;blog&lt;/a&gt;, and a &lt;a href="http://www.cbc.ca/ideas/" mce_href="http://www.cbc.ca/ideas/"&gt;radio program&lt;/a&gt; have reshaped some of my thinking about agility.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.freakonomics.com/" mce_href="http://www.freakonomics.com/"&gt;Freakonomics &lt;/a&gt;is one of those books that makes you want to ask questions.  Deep questions.  Economist &lt;a href="http://pricetheory.uchicago.edu/levitt/home.html" mce_href="http://pricetheory.uchicago.edu/levitt/home.html"&gt;Steven Levitt&lt;/a&gt;, one of books authors, makes the claim that he wants to shed light on the ideal world and expose the actual world. And he does this brilliantly by asking the right types of questions and not shying away from the results. From why drug dealers live with their mothers, to real estate agents and their ability to move homes, right down to the way people name their children, there is usually and answer different from the one that you expected to get. The thing that was striking, and that started me down a path of thought, was that all of those unexpected answers had a common flavour - they all focused on &lt;a href="http://en.wikipedia.org/wiki/Incentive" mce_href="http://en.wikipedia.org/wiki/Incentive"&gt;incentive structures&lt;/a&gt; that cause people to do the things that they do.&lt;/p&gt; &lt;p&gt;More on that in a minute.  &lt;/p&gt; &lt;p&gt;Ron Jeffries wrote a &lt;a href="http://www.xprogramming.com/xpmag/AgileTopDown.htm" mce_href="http://www.xprogramming.com/xpmag/AgileTopDown.htm"&gt;great piece&lt;/a&gt; about what he would do if he were an executive and wanted to make 'software cheaper, sooner, and with fewer defects.' How does he do it? Well, read the article for all of the details. My learning from Ron's article, and viewed in the glow of a dimn enlightenment, was that if an executive is able to change the incentive structures in an organisation, then the people in that organisation will change their behaviour in response to those changes. It seems like common sense. In Ron's case, as the executive of a company, he made it clear that he wanted a simple measurement of project success: &lt;a href="http://www.xprogramming.com/xpmag/jatRtsMetric.htm" mce_href="http://www.xprogramming.com/xpmag/jatRtsMetric.htm"&gt;Running tested software&lt;/a&gt; delivered this month that customers are either saying they want, or are using now. Ron goes on to say that he doesn't expect to have to change incentive programs. However, he is changing incentive structures - his people now have to report something very specific to make Ron happy. Happy Ron - happy subordinate-to-Ron. &lt;/p&gt; &lt;p&gt;Moving along, the final twist came when I was listening to the &lt;a href="http://www.cbc.ca/" mce_href="http://www.cbc.ca"&gt;CBC&lt;/a&gt;, which plays a top-notch show called &lt;a href="http://www.cbc.ca/ideas" mce_href="http://www.cbc.ca/ideas"&gt;Ideas&lt;/a&gt;.  This episode happened to be about the cost of adopting environmentally-sound products and practices.  One of the guests, &lt;a href="http://www.chass.utoronto.ca/%7Ejheath/" mce_href="http://www.chass.utoronto.ca/~jheath/"&gt;Joseph Heath&lt;/a&gt;, a professor of philosophy at the University of Toronto, spoke about, you guessed it, incentives. Dr. Heath spoke about crisis and values. &lt;/p&gt; &lt;p&gt;Crisis leads to action because there is an incentive to do so. One example is a natural disaster. Disaster alters social structures, and brings about communal cooperation. People behave differently because the incentive is there to rebuild as quick as possible and return to a state of normal. Once normal is achieved, these same people tend to go back to a more individual style of living. Crisis is not always about disaster, sometimes it is about radical change. For example, revolutionary change in a country's government via coup or mass revolt. People band together and get swept up in the crisis-atmosphere, the incentive being that change will bring about a better life. However, as has been in seen in many examples world-wide, once the revolution is over, the atmosphere of revolution rarely continues, because people go back to what they know - the daily grind.&lt;/p&gt; &lt;p&gt;Changing values, beliefs, and world-views is not an effective means of change without revisions to supporting incentive structures, says Dr. Heath. An example of this is seen in towns with watering bans. Much effort is placed on educating citizens about the need to conserve water, the positive effects of conservation on the environment, and individual contribution to a greater good. One would expect a substantial decrease in water usage, given a majority of residents had changed their way of thinking. Water usage did not decline substantially. Did a change in thinking not occur? Well, it was shown that campaigns were effective in changing views on conservation, however, people also believed that the greater good was accomplished by the community as a whole, and that sneaking out to water a lawn at midnight wasn't going to be the tipping point towards an outright drought. Even though the values, beliefs, and world-views of the citizenry had changed,there was no supporting incentive to prevent them from actually watering their lawns. Yet, when municipalities started installing water meters in new homes and existing residences, water usage dropped 40 percent because residential water usage was now monitored and billed according to usage (and I believe, availability). Once a new incentive structure was introduced to 'support' the new values and beliefs of the citizenry, all was well.&lt;/p&gt; &lt;p&gt;Enough rambling about the obtuse. Many companies turn to agile and XP when software crisis hits (software is more expensive, delivering later, with more defects). Lots of practices aren't adopted by management or by programmers or by customers. Companies have problems trying to bring agile and XP online. And the adoption of XP and agile isn't moving forward as expected.&lt;/p&gt; &lt;p&gt;Agile and XP are about values and principals and practices. People can buy into them, but still have no incentive to put them into practice. So maybe the future of agility is about exploring the incentives that are in non-agile organisations, and replacing them with incentive structures that allow agility to work, or making agility work in existing incentive structures. &lt;/p&gt; &lt;p&gt;What do I mean by this?  Perhaps it's best illustrated with a few examples:&lt;/p&gt; &lt;p&gt;We all love pair programming. Once programmers get used to it, the incentive to pair is stronger than the incentive to not pair because the benefits are obvious - two brains, two sets of eyes, lots of collaboration, etc. But in some companies, the incentive to continue pair programming as a regular practice isn't all that obvious - two programmers == twice the cost for the same number of features. Without properly demonstrated benefits - economically feasible level of defects, same number of features as two programmers - there is no incentive to adopt pairing.&lt;/p&gt; &lt;p&gt;Evolutionary design is another. How do we convince companies that heavy requirements gathering and upfront design complete with docs is not the way to go? Well, the convincing is easy. Changing the need is the hard part. The incentive to produce these artifacts is great - show us the design, get it approved, and we'll give you money to proceed. Documents and approved design == confidence. Changing this incentive to a structure where funding is based on confidence in agile techniques is the real challenge.&lt;/p&gt; &lt;p&gt;So that's my thinking, as of now. To really make a company agile, the incentives to become agile and remain agile need to be created throughout the entire organisation. From an incentive-based stance, that means looking at the ideal world that you work in, ask some pointed questions, and expose the actual world that you work in. There's great incentive to do just that.&lt;/p&gt; &lt;p&gt;I'm going to explore some practices and look at them from an incentive point-of-view over the next few postings. I hope you will find them useful. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895275137477623?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895275137477623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895275137477623' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895275137477623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895275137477623'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/08/incentive-to-change.html' title='Incentive To Change'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895271465946840</id><published>2005-06-14T21:31:00.000-04:00</published><updated>2006-05-30T20:38:49.203-04:00</updated><title type='text'>Text and Code Don't Mix</title><content type='html'>&lt;p&gt;I just got over a nasty case of string duplication. My current code was littered with strings that represented tags, attributes, and XPath queries, and over a very short time, the buildup resulted in some big time duplication of text.&lt;/p&gt; &lt;p&gt;No problem, right? Wrong! What I thought would take me an hour or so to remove ended up taking my pair and me around 6 hours! Like code duplication, I've learned that string duplication can be everywhere - embedding common string patterns in other strings, and then extracting them later, is a huge pain in the neck. Using the same string in 15 different places is a big headache. And don't get me started on using hardcoded strings in tests. Before you know it, they are everywhere.&lt;/p&gt; &lt;p&gt;Here is what we learned as we refactored to remove hardcoded string duplication:&lt;br /&gt;1) It's boring and mind-numbing.&lt;br /&gt;2) Microsoft doesn't have any nice tools to help us .NET programmers yet. Search and Replace is primitive, and unless you are a regular expression guru, forget about using this tool effectively.&lt;br /&gt;3) Make expression builders as early as you can to help build compound expressions where only part of the text in the string varies.&lt;/p&gt; &lt;p&gt;I'd give some code examples, but I've had  a long day of removing hardcoded strings, and I'm going to bed. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895271465946840?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895271465946840/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895271465946840' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895271465946840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895271465946840'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/06/text-and-code-dont-mix.html' title='Text and Code Don&apos;t Mix'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895265324695479</id><published>2005-05-25T21:29:00.000-04:00</published><updated>2006-05-30T20:37:45.810-04:00</updated><title type='text'>My Favourite Smells</title><content type='html'>&lt;p&gt;Hi, I'm Chris, and I've written smelly code. In fact, I still do it. I mean, you can't remove code smells if you don't put them there in the first place.&lt;/p&gt; &lt;p&gt;A recent conversation around &lt;a href="http://www.jamesshore.com/Blog/DrawingTheLineOnContinuousDesign.html" mce_href="http://www.jamesshore.com/Blog/DrawingTheLineOnContinuousDesign.html"&gt;continuous design&lt;/a&gt; and &lt;a href="http://xprogramming.com/xpmag/acsActionList.htm" mce_href="http://xprogramming.com/xpmag/acsActionList.htm"&gt;refactoring towards a good design&lt;/a&gt; got me thinking along a tangent- are there smells that I have a hard time escaping? What are the ones that I can't seem to shake, no matter how hard I try?&lt;/p&gt; &lt;p&gt;So I came up with one that I am especially fond of, and that special place in my heart goes to primitive obsession. This is the smell that I use all the time. What is it? Well, it's when you use primitive types, like doubles, strings, ints, to represent ideas that really deserve their own type. &lt;/p&gt; &lt;p&gt;My brain doesn't always detect the need for a new type. Sometimes I am perfectly content to represent something, say, like the concept of Mass, as a double. All over the system, I merrily use my 'Mass is a double' notion, until....&lt;/p&gt; &lt;p&gt;I need a tolerance!?! You never told me I needed a tolerance! Now I need to go around the system and change all my Masses to use a tolerance... ok, done, not so bad... What!? I need to convert my Mass to Energy?!?! Why didn't you tell me?! Ok, why is this not working? Those darn tolerances are messing everything up! &lt;/p&gt; &lt;p&gt;This is a semi-regular occurence, and I personally find two things interesting about primitive obsession.&lt;/p&gt; &lt;ul&gt;&lt;li&gt;It is easy to live with for a while, sometimes a long while. But it tangles itself into a system nice and tight, and is one of the hardest smells to remove.&lt;/li&gt;&lt;li&gt;If avoided, your code is so easy to change and you get some nice side benefits.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Let me give an example of the latter point. I work on a system that has a mixture of one-based indices and zero-based indices (legacy, gotta live with it). As we built the new stuff, we decided everything would be one-based. However, there were all kinds of errors popping up. Anyone who has programmed for more than a week will concur (hopefully, or I'm the only fool who can't add and subtract) that &lt;b&gt;i + 1&lt;/b&gt; and&lt;b&gt; i - 1&lt;/b&gt; all over the place get's really hard to figure out after a while, and your head spins while trying to convert zero-based and one-based indices back and forth. Never mind all the guess work when trying to figure out if a parameter to a method was zero or one based.&lt;/p&gt; &lt;p&gt;Well, I tried something. I made a OneBasedInt and a ZeroBasedInt value type and began using them everywhere. Soon, there were all kind of cool things happening:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;People suddenly knew the type of index they should pass into a method&lt;/li&gt;&lt;li&gt;Things started to fail fast - I could no longer assign zero to a one-based index, and I could tell exactly when I was doing that!&lt;/li&gt;&lt;li&gt; No more &lt;b&gt;index + 1&lt;/b&gt; and&lt;b&gt; index - 1&lt;/b&gt; all over the code.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;So much for my weakness(es). I see the benefits of avoiding primitive obsession, but continue to get stung by it often. Maybe I will be &lt;a href="http://xprogramming.com/" mce_href="http://xprogramming.com"&gt;old and wise&lt;/a&gt; one day.  For now, I will relish my small victories.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895265324695479?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895265324695479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895265324695479' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895265324695479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895265324695479'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/05/my-favourite-smells.html' title='My Favourite Smells'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895252534306922</id><published>2005-05-07T21:28:00.000-04:00</published><updated>2006-05-30T20:26:53.523-04:00</updated><title type='text'>Architecture &amp; Agility: Strange Bedfellows?</title><content type='html'>&lt;p&gt;Raise your hand if you have heard the following: An astounding 65% of defects are traced back to requirements and the cost of repairing defects grows exponentially throughout the software development lifecycle.&lt;/p&gt; &lt;p&gt;Sound familiar?  Many companies that sell requirements training services use a similar sounding pitch, and it's supported by a&lt;a href="http://portal.acm.org/citation.cfm?id=986727&amp;CFID=43586133&amp;amp;amp;CFTOKEN=25316555" mce_href="http://portal.acm.org/citation.cfm?id=986727&amp;CFID=43586133&amp;amp;CFTOKEN=25316555"&gt; gaggle of studies&lt;/a&gt;. The numbers, the adjectives, and adverbs vary, but the message is the same: bad or changing requirements lead to costly changes down the road.&lt;/p&gt; &lt;p&gt;I've been down that road. After enough projects, you realize that getting a requirement wrong can have costly after-effects. So the answer is to nail down those requirements right away, right?&lt;/p&gt; &lt;p&gt;Well, not entirely. The loathing a programmer has towards changing and incorrect requirements began so long ago that it's almost genetic. Before studies and companies told us that there would be bad and changing requirements, we created protection mechanisms. That protection mechanism (and not coincidently, the dirtiest two word combination in the entire agile software universe) is software architecture. &lt;/p&gt; &lt;p&gt;I and countless others have built software architectures to try to anticipate the unexpected. What will they ask for next? I know the requirements will likely be incorrect, so I'll build for every possibility. And of course, this was costly, because my deck of software tarot cards was always missing a card or two, so I couldn't accurately predict everything, and would have to munge, hack and kludge new features, all the while pointing out how great my architecture was.&lt;/p&gt; &lt;p&gt;Along comes agility and XP. Concerning requirements, XP says we know that the requirements will change, according to changing business needs and priorities, but, we won't build our software with any type of prognostication or speculation. Test-driven development, refactoring, and simple design will ensure our code will be supple and changeable to support those changing business needs when those future needs turn into present reality.&lt;/p&gt; &lt;p&gt;This approach works. I'll be the first to stand up and say it works because I've tried it. And for the requirements that you know about and take into consideration, you have a fantastic chance for success. &lt;/p&gt; &lt;p&gt;The kicker is the part about 'requirements that you know about and take into consideration'. Onsite customers take lot's of things into consideration. Usability studies, product identity, prioritized features. For standalone products that have no inter-relation with other products in a company, this is about all the onsite customer needs to consider. But this isn't so with an increasing amount of software products.&lt;/p&gt; &lt;p&gt;That's were I posit that there are more requirements than meets the eye of the typical (product manager or domain expert) onsite customer. And sometimes we incorrectly label these unseen requirements as speculative or forward-looking, when reality says these requirements are present, but hidden. After some reflecting, I think that there are domain, economic, process, social and technological requirements that go beyond what the traditional onsite customer has the capability to see.&lt;/p&gt; &lt;p&gt;Let's take a look at an experience I've had. I'll change the name of all the components to protect the identity of the products I work on, but the essentials are the same.&lt;/p&gt; &lt;p&gt;Before adopting XP, a colleague of mine and I began designing the next generation of our platform. This platform would serve as the foundation for a number of products that would be created over the next few years. In our domain, there are entities known as &lt;a href="http://en.wikipedia.org/wiki/Pixies" mce_href="http://en.wikipedia.org/wiki/Pixies"&gt;Pixies&lt;/a&gt;, &lt;a href="http://www.imdb.com/title/tt0087363/" mce_href="http://www.imdb.com/title/tt0087363/"&gt;Gremlins&lt;/a&gt;, &lt;a href="http://freemasonry.bcy.ca/brownies/brownies.html" mce_href="http://freemasonry.bcy.ca/brownies/brownies.html"&gt;Brownies&lt;/a&gt;, and &lt;a href="http://en.wikipedia.org/wiki/Imp" mce_href="http://en.wikipedia.org/wiki/Imp"&gt;Imps&lt;/a&gt;. Every application that is built in our domain is concerned with Pixies, Gremlins, Brownies, and Imps. What we knew about each application, even though they weren't built yet, was that even though each application uses these entities, they each assume different things about the relationships between them. For instance, our PixieDust application assumes that there are PixieBags filled with Pixies. Our PixieMagic application, however, assumes that Pixies come in PixieBoxies, and specific Pixies are kept in PixieCompartments in the PixieBoxies. We also had to build the platform so that other, external companies could build their own applications. &lt;/p&gt; &lt;p&gt;What we realized is that each application had it's own domain, and used it's own language. And what each language boiled down to was a domain specific query for Pixies, Gremlins, Brownies, and Imps. So, the known requirement was to build a platform that could support domain specific queries. This was a domain requirement that spanned a number of products. We also felt that using a service-oriented architecture would allow us to build a system that could change over time without affecting the innumerable applications that were built at external companies.&lt;/p&gt; &lt;p&gt;When we started XP, we were told to throw away that architecture. We'd be building a domain model for the PixieDust application, and since the PixieMagic, FurryGremlin, and ImpishDelight apps weren't starting for another six months, we'd worry about them at that point. That domain would serve as the start of the platform, and when other applications started, they would help drive the application.&lt;/p&gt; &lt;p&gt;So we went full force down that path. We were already convinced that XP would help us keep the model supple and light enough to change.&lt;/p&gt; &lt;p&gt;Fast forward 18 months later. The ImpishDelight and FurryGremlin applications started up at the same time, and the definite start date for PixieMagic was in three months. The two new project teams saw the platform domain model that had been built declared that it was only good for the PixieDust application, and the way it assumed Pixies and Brownies were stored and the containment relationships made no sense in their domains.&lt;/p&gt; &lt;p&gt;At that point, onsite customers and programmers from each of the project teams got together and came to the conclusion, independent of our earlier forays, that the best way to handle multiple applications was a service-oriented architecture that allowed applications to submit domain specific queries.&lt;/p&gt; &lt;p&gt;The work to convert the PixieDust application to use a new platform model that we are still trying to develop is now more expensive than if we'd started along that path oh so many months ago.&lt;/p&gt; &lt;p&gt;Some would say thats ok. The onsite customer knew all the costs associated with their decision and are willing to live with it. Pish posh say I. I say I work for the same company, and expensive decisions affect my bonus and retirement plans, so I'm not willing to live with it.&lt;/p&gt; &lt;p&gt;Some would say, well, if you really knew, you should have started building towards it. To that I say the culture of XP labels anything that is not in the present 'speculation'. And speculation is a mortal sin in XP, and you are cast out like a leper if you suggest it.&lt;/p&gt; &lt;p&gt;Of course, that's a bit of an exaggeration, but it also isn't far from the truth. But looking back, and seeing what we knew for sure, there were business requirements that went far beyond the 'I need to see that the Pixie Dust created is 99% pure and has no Impish Influence'. That type of business requirement is hard to see for an onsite customers, but easy to see from the perspective of the technical staff.&lt;/p&gt; &lt;p&gt;As I said before, I believe that there are domain, economic, process, social and technological requirements on projects that need consideration. Make no mistake, these are also business requirements because they are related to building what is needed. I've already explored the domain requirements and alluded to the fact that there were technological requirements - &lt;a href="http://www.google.ca/search?hl=en&amp;q=service+oriented+architecture&amp;amp;btnG=Google+Search&amp;meta=" mce_href="http://www.google.ca/search?hl=en&amp;amp;q=service+oriented+architecture&amp;btnG=Google+Search&amp;amp;meta="&gt;service oriented architectures&lt;/a&gt; are based on &lt;a href="http://www.sei.cmu.edu/str/descriptions/momt_body.html" mce_href="http://www.sei.cmu.edu/str/descriptions/momt_body.html"&gt;message-oriented systems&lt;/a&gt; and I won't go into the technical details here. Other technical requirements were the ability to communicate and integrate with external systems and platforms. For instance, we were building on .Net, but a growing base of our customers are moving to open source tools like Java, Eclipse, Linux, Ruby, Python. Shipping a .NET only platform would isolate them. Once again, this was a requirement that was technical, outside the domain of Pixies and Imps. Some would argue, and rightly so, that a proxy customer in tune with the market would not be a stranger to these facts.&lt;/p&gt; &lt;p&gt;How about an economic requirement? Each time a change was made to the domain model, multiple projects would feel the impact in terms of lost time converting vertical applications to use the new domain changes. The ImpishDelight team shielded itself from these changes by maintaining an anti-corruption layer (a facade that sat on top of the platform). PixieDust was more exposed to the platform, and so any change to the domain model would ripple through the entire code base.&lt;/p&gt; &lt;p&gt;Change was costly. As more projects came online, and began putting design pressure on the platform, changes to the domain model would ripple through more and more projects. The question surrounding this requirement was one of direct cost: How could the natural change associated with developing an ever-changing domain model be kept low? This requirement, of course, was met by the eventual solution.&lt;/p&gt; &lt;p&gt;Which brings about another requirement - process. Following XP, teams practice evolutionary development, simple design, and refactoring. However, these practices are about incremental steps. Experience thus far, with all other forces considered, is that the design oscillates rather than evolves. It jumps from one state to another to another, without settling into a mode where it grows. As new projects are added, there seemed to be no end to the flip-flopping from one design to the next, in search of one that will accommodate all domains. Once again, it seemed as though we were trying to do the impossible - find an object-oriented domain model that accommodated all applications being built. Eric Evans wrote about subdomains and the like in his book Domain Driven Design. We tried some of those techniques, but fell short each time. Guess what? The SOA solution worked here too.&lt;/p&gt; &lt;p&gt;And that brings us to the final requirement - social. Each team believes that they are correct in their assumptions about their domain. Rightfully so, each team defends their viewpoints, fearing that important details may be left out if a compromise was made and a more general model was built. Vigorous defence of specific views of the world have been exhibited by all teams - PixieDust’s defence of the United Order of Pixies standard as the domain language. ImpishDelight's stance on Imps and ImpJobs. PixieMagic's zeal over not using PixieID's. FurryGremlin's insistence on using PixieID's. The list goes on and on. Whoever concedes loses a precious piece of their domain.&lt;/p&gt; &lt;p&gt;Another piece of the social requirement that drives domain development is the fact that multiple teams need to work on the platform at once. However, because of distance, unfamiliarity, and fear that one team will undo the changes of another, this is was not a generally accepted practice. All changes to the platform had to be funnelled through the PixieDust team for approval, putting an extra burden on one team to coordinate the work of multiple teams.&lt;/p&gt; &lt;p&gt;After all that, where do we go now?  Ok, here goes - software architecture.  Before you pull the pin on the &lt;a href="http://www.clubi.ie/exalted/holyhand.htm" mce_href="http://www.clubi.ie/exalted/holyhand.htm"&gt;Holy Hand Grenade of Antioch&lt;/a&gt; and lob it my way, please listen!&lt;/p&gt; &lt;p&gt;I'm not talking about the traditional software architect who hands out designs from the Ivory Tower while making sure they don't mess their white robes with the mundane task of coding. No, my view is that software architecture is important to agility because it really defines a technical customer role who is also a programmer on the team. The requirements are not easy to get right, we know that, and XP does a good job at managing the change associated with changing business requirements. What an agile architect does is guide the discovery of those hidden requirements that the business facing customer doesn't always see and that the programmers naively label as speculative. &lt;/p&gt; &lt;p&gt;The agile architect helps to define the requirements for things that won't happen for a while (but definitely will happen), and will be more costly to implement if they are forgotten altogether. So why do I call this software architecture? Because ultimately, I think it's these types of requirements that help draw a better, bigger picture of the software that is being produced. And that better, bigger picture is the context in which simple design, refactoring, and TDD happen. Without that picture, refactoring and simple design is just a bunch of quantum, unrelated steps within the framework of current software features without a goal that says 'hey, wait, there are things that you need to consider here because they are happening or will be happening with 100% certainty'. &lt;/p&gt; &lt;p&gt;I don't think that my colleague and I came up with a design that just happened to be the same as the one that was independently discovered over a two year period. I'm not that lucky. It really boils down to the fact that agile teams need architectural guidance just as much as they need business guidance.&lt;/p&gt; &lt;p&gt;Ok, count to three, no more, no less, and lobbest thou thy &lt;a href="http://www.clubi.ie/exalted/holyhand.htm" mce_href="http://www.clubi.ie/exalted/holyhand.htm"&gt;Holy Hand Grenade of Antioch&lt;/a&gt; towards thou foe. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895252534306922?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895252534306922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895252534306922' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895252534306922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895252534306922'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/05/architecture-agility-strange.html' title='Architecture &amp; Agility: Strange Bedfellows?'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895247022470632</id><published>2005-04-21T21:27:00.000-04:00</published><updated>2006-05-30T20:26:06.273-04:00</updated><title type='text'>People Come, People Go</title><content type='html'>&lt;p&gt;A friend of mine, Gunjan, came by today. He's visiting for a day or two and we both realised he hadn't seen our project area in close to a year. As we were chatting, I introduced him to some of the new team members and something dawned on me - of the original 6 programmers on the team, only one other programmer and myself were left, and our programming complement had increased to 8!&lt;/p&gt; &lt;p&gt;That meant that over the course of a year, we had welcomed 6 new programmers and said goodbye to 4. Those four are coaching other projects now - we were the first to use XP in our shop, and so naturally, the people on it were used to help other projects use XP too.&lt;/p&gt; &lt;p&gt;Getting back to the people changes... I've been on lots of projects, and rarely does this type of turnover happen without turbulence. This was a different experience. &lt;/p&gt; &lt;p&gt;As we got word that someone new was joining us, we made no special preparations. When the day arrived, it was business as usual. Pairs formed, they picked tasks, and went to work. No one was assigned to pair with our new team mate, the pairs just formed naturally. No special orientation happened - the pair would talk about things that seemed different or strange to our XP neophyte, but that was the extent of the training. All necessary skills and habits were gained by observation, lore, and participation.&lt;/p&gt; &lt;p&gt;This worked incredibly well. All who have joined are solid in the team's practices. And they in turn have been instrumental at bringing new people onto the team.&lt;/p&gt; &lt;p&gt;Change doesn't always come in the form of evolving software requirements. Most times it comes in ways that we can't anticipate. A year ago, Gunjan told me that agility means adapting to all kinds of change. If I'd been told that meant that we'd have 6 new people who had no XP experience in exchange for 4 productive, XPerienced programmers, I would have said that our project was doomed.&lt;/p&gt; &lt;p&gt;Good thing I kept my mouth shut, because I couldn't have been more wrong. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895247022470632?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895247022470632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895247022470632' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895247022470632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895247022470632'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/04/people-come-people-go.html' title='People Come, People Go'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895242072375055</id><published>2005-04-11T21:26:00.000-04:00</published><updated>2006-05-30T20:25:39.713-04:00</updated><title type='text'>Project:1, Integration Machine: 0</title><content type='html'>&lt;p&gt;Something cool happened on our project today.  The &lt;a href="http://c2.com/cgi/wiki?IntegrationMachine" mce_href="http://c2.com/cgi/wiki?IntegrationMachine"&gt;integration machine&lt;/a&gt; died.  Hard-drive just up and kicked the bucket.  &lt;/p&gt; &lt;p&gt;Why is this cool? Because 2 minutes later, we had a new integration machine! In the past, the build machine dying would have been a major catastrophe. All the special little tools, the wierd paths and environment variables, the database setup, the special security settings... oh, the headache after hours of reinstallation and twiddling to make it all work again.&lt;/p&gt; &lt;p&gt;That was the past.  Today, we just went over to another machine, deleted the project directory, did a CVS get, &lt;a href="http://nant.sourceforge.net/" mce_href="http://nant.sourceforge.net/"&gt;nant&lt;/a&gt;, and presto!  New build machine!  And not a headache in sight.&lt;/p&gt; &lt;p&gt;This is due to one important practice: we maintain our build as diligently as any of our code. We make sure all the tools are in CVS, all the code in CVS is in pristine, shippable condition, and all tests are running green. And every member of the team knows how to modify the build scripts. No buildmaster is needed. &lt;/p&gt; &lt;p&gt;We also make sure that the build can run fresh, on any machine at anytime. So next time you have to spend a few hours reconfiguring a new build (or integration) machine, consider whether you can spend some time making your build run anywhere, anytime, anyhow, by anyone. You'll be thankful you did. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895242072375055?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895242072375055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895242072375055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895242072375055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895242072375055'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/04/project1-integration-machine-0.html' title='Project:1, Integration Machine: 0'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895236769290665</id><published>2005-04-05T21:25:00.000-04:00</published><updated>2006-05-30T20:25:12.556-04:00</updated><title type='text'>Commitment</title><content type='html'>&lt;p&gt;After talking about Points vs. Pair Hours for a bit, I was surprised by the web of topics that are entangled with estimation and planning. Here's a brief synopsis of some of my thoughts around just one of those knots - commitment.&lt;/p&gt; &lt;p&gt;Commitment is a strange beast. I've heard it said more than once that on XP and agile projects we don't make commitments, that they are 'specious as best' as one well-known agilista says. I don't agree. Here's why.&lt;/p&gt; &lt;p&gt;Commitment makes a few other words come to mind - dedication, trust, integrity, honesty, promise. When my team makes a plan on Friday afternoon for the following week, all those things come into play as we strive to meet that plan. We are effectively making a promise to deliver working, tested, features, and we do all we can to deliver those working, tested features.&lt;/p&gt; &lt;p&gt;Along the way, the various facets of commitment come alive. The team is dedicated to completing the stories. All members work together with stories on the plan as the focal point. We remove obstacles that get in the way of delivering, whether they are technical, process, or social roadblocks. The teams actions reflect the commitment to do what they said they would do. And when anything goes awry of the plan, complete and full disclosure occurs.&lt;/p&gt; &lt;p&gt;This is commitment.  This delivers on promises.  This builds trust.&lt;/p&gt; &lt;p&gt;Where I think people get tripped up is when they think that commitment only means a number on a schedule. The past was painful, for everyone - programmers, customers, managers. It was every (wo)man for themself. Meet the schedule, or suffer. And do it by any means possible. That was commitment. Commit to a number. &lt;/p&gt; &lt;p&gt;I've seen teams move beyond this definition of commitment, and into a productive, trust-building definition of commitment. It requires dedication, and it requires vulnerability on each team members part. In the end, it's worth it. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895236769290665?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895236769290665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895236769290665' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895236769290665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895236769290665'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/04/commitment.html' title='Commitment'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895191400001566</id><published>2005-04-02T21:17:00.000-05:00</published><updated>2006-05-30T20:24:01.516-04:00</updated><title type='text'>Some more points about 'Points' (and pair hours)</title><content type='html'>&lt;p&gt;My post about NUTs vs. pair hours generated some response and some further questions.  Here are a selection of those comments.&lt;/p&gt; &lt;p&gt;&lt;a href="http://bossavit.com/thoughts/" mce_href="http://bossavit.com/thoughts/"&gt;Laurent Bossavit&lt;/a&gt; writes:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt; I would think that any story estimates can come from:&lt;br /&gt;estimates made at the start of the project, when we estimate all  stories for the purpose of making a Release Plan&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;One thing I didn't mention is that we estimated all stories in team&lt;br /&gt;weeks at the beginning of a project, not NUTs.  That's how we were&lt;br /&gt;taught.  The theory is that at the beginning of a project you don't&lt;br /&gt;have the same learning, experience, and facts about the story as you&lt;br /&gt;do when you decide to begin the story.  The further you are away from&lt;br /&gt;completion, the less time and effort you spend on estimating, and&lt;br /&gt;therefore, the courser the estimate is.&lt;/p&gt; &lt;p&gt;&lt;a href="http://bossavit.com/thoughts/" mce_href="http://bossavit.com/thoughts/"&gt;Laurent &lt;/a&gt;continues:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;Did you at any time re-estimate the entire collection of (not yet completed) stories in the project ?&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;All the time.  We do this because we learn new things about the&lt;br /&gt;software that we are building, and things that we do over time could&lt;br /&gt;potentially make things easier or harder to do.  My experience is&lt;br /&gt;that, over time, we tend to get better estimates when we are&lt;br /&gt;revisiting the stories.&lt;/p&gt; &lt;p&gt;Another benefit of revisiting the stories is that the stories change.&lt;br /&gt;We may think of new, cheaper alternatives to give the customer what is&lt;br /&gt;needed. Or sometimes user feedback has been received and we no longer&lt;br /&gt;need a story, or need to change it.&lt;/p&gt; &lt;p&gt;There are so many factors that affect what a story is and how long it&lt;br /&gt;will take to complete that we've found it beneficial to constantly&lt;br /&gt;revisit the release plan.  The time you spend doing this pays for&lt;br /&gt;itself.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.industriallogic.com/company/coaches/index.html" mce_href="http://www.industriallogic.com/company/coaches/index.html"&gt;Joshua Kerievsky&lt;/a&gt; got in on the action:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;Can you further explain what you mean by "we measure the hours that were spent programming, and the number of hours that were accepted as stories." A few examples would help.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Sure... Let's say I had 5 stories, and we said each would take 5&lt;br /&gt;hours.  At the end of the iteration, two stories took 10 hours, and&lt;br /&gt;were finished.  3 stories took 4 hours, and were finished, but not&lt;br /&gt;accepted (for whatever reason).  We would say that 22 hours were spent&lt;br /&gt;programming, and 10 hours were accepted by customers.  We also track&lt;br /&gt;the number of stories accepted.  In this case, 2 stories were&lt;br /&gt;accepted, 3 weren't.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.industriallogic.com/company/coaches/index.html" mce_href="http://www.industriallogic.com/company/coaches/index.html"&gt;Joshua &lt;/a&gt;continues:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;I'm just not clear on what the "budget" is per iteration and how that budget changes from iteration to iteration.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;The budget is the number of hours spent programming.  In the example&lt;br /&gt;above, the budget would be 22 hours. We use the capacity of the&lt;br /&gt;programmers to set the budget.  All other numbers are used to find&lt;br /&gt;areas where we need to improve.&lt;/p&gt; &lt;p&gt;More &lt;a href="http://www.industriallogic.com/company/coaches/index.html" mce_href="http://www.industriallogic.com/company/coaches/index.html"&gt;Josh&lt;/a&gt;? Ok, one more:&lt;/p&gt; &lt;blockquote&gt;&lt;p&gt;I'd also like to know what you do mid-iteration, when a story is clearly going to go over its estimate. What kind of budgeting or negotiation happens then?&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We do what we always did - talk to the customer, renegotiate, drop&lt;br /&gt;stories if needed.  If a 5 hour story looks like it will take 10, we&lt;br /&gt;ask the customer where they want to re-adjust to compensate for the&lt;br /&gt;extra 5 hours (drop a 5 hour story, get something simpler, etc).  We&lt;br /&gt;then retrospect on the story in iteration planning.&lt;/p&gt; &lt;p&gt;The whole discussion can be read on the &lt;a href="http://groups.yahoo.com/group/extremeprogramming/" mce_href="http://groups.yahoo.com/group/extremeprogramming/"&gt;Extreme Programming group over at Yahoo!  &lt;/a&gt;Thanks for all who got involved - I learned a lot!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895191400001566?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895191400001566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895191400001566' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895191400001566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895191400001566'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/04/some-more-points-about-points-and-pair.html' title='Some more points about &apos;Points&apos; (and pair hours)'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-28171798.post-114895165643625534</id><published>2005-03-28T21:12:00.000-05:00</published><updated>2006-05-30T19:59:07.283-04:00</updated><title type='text'>Using the Right Measure: Pair Hours vs. NUTs</title><content type='html'>&lt;p&gt;What's the best way for a team to estimate work? I'm not sure there is one correct answer that fits every team. Some advocate a point system, others say using actual time is the best way. Kent Beck, in XPE2 seems to come out in favour of the latter. I've tried both and want to share some of my learning.&lt;/p&gt; &lt;p&gt;But first, a little story. I like to build things out of wood whenever I get some free time. I'm no craftsman, by any stretch. But I like the diversion from my usual work. And I get to turn perfectly good pieces of wood into large piles of useless scrap, with the occasional by-product looking sort of like what I intended it to be.&lt;/p&gt; &lt;p&gt;This past Christmas, I built two toolboxes, one for each of my wife's brothers. By some strange alignment of the planets, they came out looking pretty much exactly the same, and I had some fun building them.&lt;/p&gt; &lt;p&gt;All kidding aside, over the years I've discovered that a few things are important when building things out of wood. You need sharp tools. Good lighting is very helpful. But most important, you need a good measure. Whether it's a tape measure, a builder's square, or a length of scrap, consistent measurement is your best friend when producing, and reproducing, good quality pieces.&lt;/p&gt; &lt;p&gt;Software development is the same way. Good estimation, and delivering according to those estimates, is important for a software team. Imagine a team that says they will deliver something, and then delivers it. And now imagine that team could do that consistently, week after week. This is, after all, what XP and all the agile schools try to get teams to do, isn't it? Make a promise and then keep a promise.&lt;/p&gt; &lt;p&gt;The story I tell is about my team. I think that my team is much like any other. We come from a company who didn't always deliver software when it promised, and when that software was delivered, it didn't have all the features in it that were originally promised either. Trust was not that high. Morale wasn't much higher.&lt;/p&gt; &lt;p&gt;When we learned how to estimate, XP style, the NUT (Nebulous Unit of Time) was used. What is a NUT, you may ask? Well, the snappy answer is 'smaller than a coconut, bigger than a walnut'. What that really means is that the NUT (or story point, if that's what you are used to) is an arbitrary unit used for one purpose - planning. I've been asked many times to explain exactly what a NUT is, to be more specific than the snappy answer I gave earlier. And as I have learned more about estimating with NUTs, I've formed a loose definition. &lt;/p&gt; &lt;p&gt;To me, a NUT is a unit that encapsulates a few elements related to planning. It consists of equal parts time, effort, and gut feeling. Notice that time is only one part of a NUT and is equal to the other ingredients. &lt;/p&gt; &lt;p&gt;There was an initial adjustment period when we began using NUTs for planning. Programmers were not used to using NUTs and always tried to attach a time value to the NUT. For instance, some would say - 'I think one NUT is about half a day of work, so 3 NUTs is about one and a half days worth of work'. But our mentors insisted that this was no different than using 'days' to estimate.&lt;/p&gt; &lt;p&gt;A bit of a breakthrough came when we began to see that NUT's were used to make relative estimates. Using a baseline, such as adding an OK button to dialog box, other stories could be estimated based on how much more or less time/effort/gut feeling it would take to accomplish. If the OK button story was one NUT, then adding a new options dialog might be two NUTs, if it felt twice as 'big' and changing the text on a dialog may be half a NUT, because it wasn't nearly as hard as adding an OK button.&lt;/p&gt; &lt;p&gt;Customers weren't used to using NUT's either. To be completely honest, I'm not sure that they ever got comfortable with the notion. But they tried. Similar to programmers, they would ask how to relate NUTs back to real time. 3 NUTs, that's about 2 days, right? That question was typical in our iteration planning. But like programmers, they began to see that NUTs were used to make relative estimates and soon were questioning (rightfully so) why one story was twice the NUT value as another story.&lt;/p&gt; &lt;p&gt;As time went on, we started to feel as though we got the hang of NUTs. A few simple rules were instituted. First, stories that were more than 5 NUTs were probably too big. That meant they needed to be broken down into smaller stories. Second, stories were broken into engineering tasks so that programmers could estimate more accurately by considering the steps in a story. Programmers would estimate individual engineering tasks in NUTs and add them up to get a NUT estimate for the entire story. Third, customers would use the velocity from the last iteration as the budget for the current iteration. And 4th, only stories that were accepted, by customers, as complete counted towards the velocity calculation. &lt;/p&gt; &lt;p&gt;The velocity calculation was interesting. Let's suppose that an iteration only had two stories, each with a value of 4 NUTs. The team finishes one story, and it takes 4 NUTs. The other story is not completed, but 3 NUTs were spent nonetheless. The budget of the next iteration would be 4 NUTs, not 7. Madness, you may say, but nay! There was a reasonfor this rule. &lt;/p&gt; &lt;p&gt;For our team, the most important thing was the delivery of finished stories. Therefore, we would only take credit for finished stories. Velocity, for us, was the number of NUTs it took us to finish stories. Suppose we got into a scenario where we had 4 stories, all 4 NUTs in size, and on each story we got 3 NUTs done, but no story was accepted ( we came close to this once or twice!!). Should we say our velocity was 12? It's a hard call, and some would say yes. But to us, not delivering a story meant you didn't get credit for it.&lt;/p&gt; &lt;p&gt;One last note on the structure of our iterations. Each iteration was 2 weeks in length, starting on Wednesday and ending two Wednesdays later. More on this later.&lt;/p&gt; &lt;p&gt;As time went on, the arbitrary value of the NUT started to have an effect on us. During iterations, we had a rule that we would negotiate if a story was going over the original estimate. If the story my pair and I were working on was estimated at 3 NUTs, but we felt that it was going to go over 3 NUTs, we'd let the customer know. At that point, we'd tell them how many more NUTs we'd need, say two NUTs in this case. The customer would then have some choices. They could remove a story from the iteration, valued at 2 or more NUTs. They could simplify the story. They could cancel the story altogether, and use the remaining story budget to add another, equally valued story. In the end, whatever the customer chose, the budget for the iteration could not be exceeded.&lt;/p&gt; &lt;p&gt;So where did the arbitrary nature of the NUT kick in? Well, it kicked in when the programmers tried to figure out how many extra NUTs they would need. 'We need two more NUTs', they'd say. 'How do you know that?', customers would ask. 'Because we have used two NUTs already, and think we'll need two more like those two', programmers respond. And customers would give them the NUTs needed. And the programmers would be done an hour or two later, and count the two NUTs in the final estimate.&lt;/p&gt; &lt;p&gt;But wait! Customers would say that the first two NUT's took a day to complete, and the next two only took a couple of hours! How can this be?!? &lt;/p&gt; &lt;p&gt;Well, say the programmers, NUTs aren't all about time. This was a tough story, and the effort was really high. So two extra NUTs is about right.&lt;/p&gt; &lt;p&gt;Over time, these conversations happened often. Almost daily. Customers eventually got weary - sure, take whatever extra you need. And programmers would justify less and less their need for extra budget - hey, it's just turning out to be harder than what we expected.&lt;/p&gt; &lt;p&gt;From iteration to iteration, velocity would fluctuate. Up, then down, then up again. And it never seemed to plateau. What was the cause? &lt;/p&gt; &lt;p&gt;A number of things, actually. First, NUTs are not constant. There is no frame of reference over time. Remember the story I told about making toolboxes out of wood? There was a reason. Imagine if I had a ruler that changed the size of an inch every time I need to make a measurement. I'd never be able to build the toolbox because the measurements would all be wrong. Similar thing with stories. The size of the NUT expands and contracts over the course of iterations. A 3 NUT story in iteration 10 is not always (rarely is) the same size as a 3 NUT story in iteration 20. &lt;/p&gt; &lt;p&gt;With no constant frame of reference, NUTs facilitated a lack of accountability. Didn't hit the velocity this iteration? Oh well, we finish what we finish. We didn't realise that this story would be 6 NUTs, not 3, it just wasn't the same as that other 3 NUT story that seemed so similar.&lt;/p&gt; &lt;p&gt;With no constant frame of reference, customers and programmers couldn't negotiate. Once again, the units on the measuring tape seemed to change each time it was picked up.&lt;/p&gt; &lt;p&gt;Another thing, not related to NUTs, was the iteration length and the start and end days of the iteration. Two weeks seemed like a long time. Most work didn't happen until the second week because the end was not in sight until later in the iteration. Starting on a Wednesday was also not helpful. After iteration planning, a bit of work would happen on Wednesday afternoon. Thursday, everyone was winding down the week, but the iteration was just beginning. Friday morning was hard, because the team used Friday afternoon as research time. Monday would come, and that would feel like the first day of the iteration, when in reality, it wasn't. Work all week. Hit the following Monday, and then rush to finish those stories because on Tuesday at 5PM, we ended the iteration.&lt;/p&gt; &lt;p&gt;It didn't feel natural.  None of it.&lt;/p&gt; &lt;p&gt;Now this happened over the course of 13 months. But we wanted to give this an honest chance. Not to mention we felt that this was the right thing to do. We did all sorts of things to tweak the way we worked. We tried to break stories into smaller pieces. But how could you define small when you couldn't figure out the unit of measure? Small was relative. We tried different negotiation strategies, different ways of picking stories. All roads came back to NUTs. &lt;/p&gt; &lt;p&gt;The catalyst came in the form of XP Explained v.2. Reading about using pair hours, and one week iterations, the concepts seemed right. You could measure hours with a clock. You could tell what the halfway point was in an iteration and in a story. So, as a team, we decided to try it.&lt;/p&gt; &lt;p&gt;It wasn't met with open arms by everyone. There were fears. Wouldn't we be micro managing by tracking every hour? Couldn't you measure the productivity of individual programmers with pair hours? We can't start on Mondays, no one likes to do iteration planning on Monday morning.&lt;/p&gt; &lt;p&gt;But we pushed forward. To the teams credit, we just tried it. With one small modification. Instead of iteration planning on Monday morning, we have iteration planning at 10:30AM on Friday morning. We finish at lunch. And then we have research time. Monday morning we begin fresh on new stories. This had an unforeseen (and welcome) side effect - nobody went home on Friday worrying about a story that was unfinished.&lt;/p&gt; &lt;p&gt;So, what happened? Well, immediately we made two rules. No story could exceed 6 hours. And every story had to have business value to our customer. The second rule we had always followed. But when making very small stories, this rule had to be followed strictly. It meant challenging the entire team to barter for simple solutions, and to think about delivering the software in small increments of business value.&lt;/p&gt; &lt;p&gt;As we followed these rules, we saw that iteration planning was much quicker. Small stories are easier to estimate. And there was much less bickering and much more creative problem solving to get the largest amount of value for the least amount of hours.&lt;/p&gt; &lt;p&gt;The iteration became more productive. Stories would start in the morning and finish by days end, with customer acceptance shortly after. Programmers could easily track how long they took on a story, measuring the exact number of hours they spent on a story with a clock. It was easy to tell how a story was progressing, and negotiating changes with customers was more meaningful with a new and consistent currency - the pair-hour.&lt;/p&gt; &lt;p&gt;Finishing stories has a great motivating effect on the whole team. And finishing stories each day pushes the team to finish more stories. Fears that were expressed earlier never came to pass. No one is measured, no one is micro managed, and Mondays are a thing to look forward to.&lt;/p&gt; &lt;p&gt;That doesn't mean that we are entirely without challenges. Moving to one-week iterations, with small stories, put a large burden on our domain experts. It forced us to consider delivering a wider range of unrelated stories each iteration as opposed to a narrow scope of related stories in order to spread the work around.&lt;/p&gt; &lt;p&gt;We also noticed that we need more involvement from our testers in the definition of stories. There is just not the luxury of using long exploratory testing cycles to find defects. In fact, we have become increasingly intolerant of defects found in exploratory testing, especially if those defects are found after a story has been accepted by a customer.&lt;/p&gt; &lt;p&gt;Now, the way we measure velocity has changed. We still use yesterdays weather. But yesterdays weather is made up of more than just one measurement. It wasn't just 30 Celsius, it was also partly cloudy with a south wind blowing at 15km/h. With that in mind, we measure the hours that were spent programming, and the number of hours that were accepted as stories. Why do we do this? Well, velocity, as it was traditionally measured, seemed to only care about programmer performance. But if programmers finished all of their work, but the customers couldn't accept all the stories because of an abundance of meetings during the iteration, wouldn't that be interesting to know? And why not try to fix that issue instead of applying penalties to team? To that end, we always use the number of hours spent programming as our velocity, and do all that we can to make sure that the number of accepted story hours is equivalent. Perhaps this is not the most ideal definition of velocity, but it works for us now!&lt;/p&gt; &lt;p&gt;Most importantly though, as a whole team, we are now completely accountable for each story. In each iteration planning session, we retrospect on stories that were significantly over or significantly under the original estimates. Programmers and customers can give a complete accounting of why things happened the way they did, see what we need to improve so that we can get better at delivering stories each iteration.&lt;/p&gt; &lt;p&gt;In all, we've had a lot of good learning around using both systems of measurement. Our abandoning the point method of estimation in favour of the pair-hour system of estimation is not because one is superior to the other. We just found that the latter enabled the team to think of delivering business value in a different way. In so doing, we are making promises and keeping those promises - we deliver more consistently. And because we are using a good measuring tape in the shop, the pile of scrap stories isn't growing the way it used to. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28171798-114895165643625534?l=chriswheeler.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://chriswheeler.blogspot.com/feeds/114895165643625534/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=28171798&amp;postID=114895165643625534' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895165643625534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/28171798/posts/default/114895165643625534'/><link rel='alternate' type='text/html' href='http://chriswheeler.blogspot.com/2005/03/using-right-measure-pair-hours-vs-nuts.html' title='Using the Right Measure: Pair Hours vs. NUTs'/><author><name>Chris Wheeler</name><uri>http://www.blogger.com/profile/06095430428479838851</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
