Red Squirrel Reflections
Dave Hoover explores the psychology of software development

Dave Hoover
dave.hoover@gmail.com

Categories
All [Atom]
Craftsmanship [Atom]
Dynamic [Atom]
Intersection [Atom]
Learning [Atom]
Links [Atom]
Polyglot [Atom]
Projects [Atom]
XP [Atom]
Old Blog

Obtivian Blogs

Andy Maleh
Colin Harris
Fred Polgardy
Jim Breen
Kevin Taylor
Todd Webb
Turner King
Tyler Jennings

Archives

March 2009 (1)
January 2009 (1)
December 2008 (1)
October 2008 (3)
September 2008 (1)
June 2008 (4)
April 2008 (3)
March 2008 (1)
February 2008 (1)
August 2007 (1)
July 2007 (1)
June 2007 (1)
May 2007 (4)
April 2007 (3)
March 2007 (5)
February 2007 (6)
January 2007 (6)
December 2006 (10)
November 2006 (5)
October 2006 (8)
September 2006 (8)
August 2006 (5)
July 2006 (12)
June 2006 (7)
May 2006 (5)
April 2006 (5)
March 2006 (4)
February 2006 (2)
January 2006 (5)
December 2005 (5)
November 2005 (3)
October 2005 (3)
September 2005 (6)
August 2005 (4)
July 2005 (7)
June 2005 (14)
May 2005 (6)
April 2005 (8)
March 2005 (9)
February 2005 (11)
January 2005 (16)
Old Archives

 

Tue, 22 Feb 2005

Next Big Thing: Craftsmanship

Uncle Bob is ranting about the next big thing...
"I, for one, hope there isn't a next big thing. Or rather, I hope the next big thing is the big thing that we've needed for the last thirty years and haven't had the guts to actually say. I hope the next big thing is professionalism. Perhaps a better word would be Craftsmanship."

[/links] permanent link

Sun, 20 Feb 2005

Emphasize Learning

Jason posted a great quote from an interesting article about Low Teck Seng (gotta love that name) and what he's doing with Problem-Based Learning at Republic Polytechnic in Singapore.
"At the rate that content and knowledge are developed today, I think we must emphasize the process of learning, rather than content delivery."
This reminds me of my favorite quote from A Pattern Language:
"In a society which emphasizes teaching, children and students -- and adults -- become passive and unable to think or act for themselves. Creative, active individuals can only grow up in a society that emphasizes learning instead of teaching." p. 100

[/links] permanent link

Sat, 19 Feb 2005

The Quest of the Apprentice

In the summer of 2002 I read Software Craftsmanship. That book has had a huge influence on my career as a software developer, perhaps even more than Extreme Programming Explained or The Psychology of Computer Programming. While Beck provided me with a vision for how I wanted to do my job and Weinberg showed me that programming has a timeless quality, it was Pete McBreen that fundamentally altered the course of my career. Upon reading his book, my quest to apprentice under a master craftsman began. It has been a rewarding journey.

Having no formal training in computer science, or anything close to it, like say, math, physics, or electrical engineering, I was at a loss for how I was ever going to get ahead in the world of programming. The concept of craftsmanship, though, gave me hope. It's simple: software developers are not scientists or engineers, they are craftsmen and craftswomen...um, craftsfolk. Regardless of whether that is true for you, it is true for me. Because dude, I am not, nor will I ever be, a scientist or an engineer. But I could be a craftsman.

The fundamental learning situation is one in which a person learns by helping someone who really knows what he is doing. --A Pattern Language, p. 413
Since 2002, I have worked hard to put myself into situations where I could help someone who really knew what he was doing. I am thankful that I have been able to accomplish this feat more than once in that time. Like any learning experience, the more you learn, the more you realize how much more you have yet to learn. Working with master craftsfolk will introduce you to your ignorance in short order. But do not despair! This is an opportunity to advance within your craft...if only you can humble yourself to take on the role of the apprentice.

[/craftsmanship] permanent link

Wed, 16 Feb 2005

The Journey of a Blog Post

Last month I blogged about ping-pong programming. That post spawned some online conversations that kicked off a series of unexpected events. One of these events was StickyMinds asking me to re-write the blog post as a column. I was surprised at their level of interest in such a simple, extreme-programming-specific, practice.

StickyMinds asked me to provide them with a head shot and a bio. As I was looking at the other columnists for the site, I quicky became aware of my relative lack of experience. My first instict was to get sarcastic...

Uncle Rico Dave Hoover has been developing software for 15 minutes.
He used to have a respectable job as a family therapist.
Dave still wonders how he got here.
Alas, that's not what I sent StickyMinds, but it is generally an accurate reflection of how I view myself relative to my fellow ThoughtWorkers. Taking yet another step into the surreal, StickyMinds wants me to write N columns for developers who think of their work as a craft. Whoa. When it comes to software craftsmanship, I feel like I'm an apprentice. Maybe on good days, a journeyman. Nevertheless, this is a great opportunity, and I'm going to take advantage of it.

Look for more blog posts under the general category of craftsmanship. I'm going to start by focusing on the concepts of beginner's mind, humility, and the search for a master craftsman. If you're a blogger, blog on those topics so I can steal your ideas. Or at least link to them. ;-)

[/craftsmanship] permanent link

Sun, 13 Feb 2005

Unfavourable Conditions

I'm reading a collection of sermons given by C. S. Lewis during World War II. My favorite thus far has been "Learning in War-Time." I think I must have written down half the content of the sermon as I transcribed my favorite quotes. Here's one:
"If we let ourselves, we shall always be waiting for some distraction or other to end before we can really get down to our work. The only people who achieve much are those who want knowledge so badly that they seek it while the conditions are still unfavourable. Favourable conditions never come." p. 60

[/learning] permanent link

Fri, 11 Feb 2005

Test interactions, Not state

Dave Astels has a new blog. He has this to say about unit tests that test the private internals of an object:
"This is evidence of a serious misunderstanding of how you should be writing tests: they should be based on behaviour and function.. not state. An objectís state is simply an implementation detail, and should not be exposed to, or relied upon by, other objects. Guess what.. that means no getters & setters, either. But thatís a discussion for another time."

[/links] permanent link

Thu, 10 Feb 2005

How to Stifle Creativity

From Bill:
"If you want to stifle participation and creativity focus on threats obligations and punishments in your communications with others. Make sure they are much more afraid of doing the wrong thing than they are of eager to do the right thing."

[/links] permanent link

Mon, 07 Feb 2005

Quotes Manager Project -- Day 13

An ongoing pet project blog...

As I explore my solution, I see that subsequent calls to add a new quote actually append the entire document to the previous document. That's obviously not what I want.

Ah, solved the problem I found yesterday. The hasText method in PersonPanel was messed up. Just an annoying firstName.getText() != "" vs. !"".equals(firstName.getText()) issue. I didn't think I would need to write a test for something that trivial. Stupid mistake.

Now for the second problem... Before the XML output is generated from the Document, I need to somehow empty the file. Not sure how to do that off the top of my head with only an OutputStream. Exploring...grrr, can't do it. I really don't want XmlQuoteListener to have to use files becauses that makes the testing harder.

I'm feeling stuck, so I'll write a failing test...

    public void testShouldWriteDocumentToFileWhenUpdatedMultipleTimes() throws Exception {
        Document document = parseStub();

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        QuoteListener listener = new XmlQuoteListener(document, output);

        assertEquals("", output.toString());

        listener.update();
        listener.update();

        String expectedOutput = documentToText(document);
        assertEquals(expectedOutput, output.toString());
    }
OK, this test fails and identifies the problem. If I can get it to pass, I'll be in good shape. Maybe I'll have more clarity on the way home...we're pulling into Chicago...

How about a factory to provide OutputStreams? That way we could get a new one with every call to update...

    public void testShouldWriteDocumentToFileWhenUpdatedMultipleTimes() throws Exception {
        Document document = parseStub();

        ByteArrayOutputStream output = new ByteArrayOutputStream();

        Mock factory = mock(OutputFactory.class);
        OutputStream ignored = new ByteArrayOutputStream();
        factory.expects(atLeastOnce()).method("newOutput")
               .will(onConsecutiveCalls(returnValue(ignored), returnValue(output)));

        QuoteListener listener = new XmlQuoteListener(document, (OutputFactory) factory.proxy());

        assertEquals("", output.toString());

        listener.update();
        listener.update();

        String expectedOutput = documentToText(document);
        assertEquals(expectedOutput, output.toString());
    }
This won't compile and I'll take a couple biggish steps to make it pass...
public class XmlQuoteListener implements QuoteListener {
    private final Document document;
    private final OutputFactory factory;

    public XmlQuoteListener(Document document, OutputFactory factory) {
        this.document = document;
        this.factory = factory;
    }

    public void update() {
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            Source source = new DOMSource(document);
            Result result = new StreamResult(factory.newOutput());
            transformer.transform(source, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
OK, I've pushed the responsibility down. I have yet another anonoymous class (this time an OutputFactory) in my main method...
    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame(TITLE);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        final String xmlFile = "play.xml";
        Document document = builder.parse(xmlFile);

        QuoteListener listener = new XmlQuoteListener(document, new OutputFactory() {
            public OutputStream newOutput() throws IOException {
                return new FileOutputStream(xmlFile);
            }
        });

        QuoteManager manager = new XmlQuoteManager(document);
        manager.addListener(listener);

        QuoteManagerPanel quoterManager = new QuoteManagerPanel(manager);
        frame.getContentPane().add(quoterManager);

        frame.pack();
        frame.setVisible(true);
    }
I'll run the app and to see if that fixes my problem. I have an uneasy feeling about creating a new OutputStream every time... But hey, it worked like a charm! Time for a check in.

So let's look back at the initial goals and see where we are...

  • I want to change the quotes page to be sorted by quoter author. DONE. There should be a list of all the quoters at the top. NOT DONE. Their names should link down to their quotes on the page below. NOT DONE.
  • I want to use a simple Swing application to manage the quotes. FIRST RELEASE: ADD QUOTE
  • The Swing app will store the quotes in XML. DONE. The app will have the ability to transform this XML into HTML via XSLT. NOT DONE.
Cool, this is good. If you don't mind, I'm going knock out the rest of this stuff without writing it up. I need to be moving onto other commute-time activities ASAP. Like reading and finding more quotes...and using my new software.

If anyone is interested, I'll post the final source code when I'm finished.

[/projects/quotes] permanent link

Thu, 03 Feb 2005

Screw the A-List

I came to ThoughtWorks hoping to learn more about software development. And I have. It's been almost a year and I haven't been disappointed. But the most valuable lesson I've learned doesn't have anything to do with software. I have learned what I need in order to do my best work. Crammed right between sleep and spirituality is my connection with my wife and children. When that connection is broken, I'm not doing my best. Probably not even close.

I learned this lesson while I was at AYE. I have Alan to thank for my time there. And today I'm thanking Alan again for posting this snippet from The Age:

"Australian managers are good at making clear to men that any worker who modifies his schedule to accommodate new fatherhood will be struck off the A-list for the crime of being inadequately 'serious'."
Not that I was ever on the A-list, but if putting my family first means I'm not adequately 'serious' about my career, then that's a list I'd rather not appear on. Ironically, I've learned that the most likely way for me to attain that status involves a strong connection with my family.

[/links] permanent link

Wed, 02 Feb 2005

More on Ignorance

Laurent is running a dojo. Christophe, one of the members of the dojo, made this comment about why the dojo is different than work:
"At work, it's not well seen to voluntarily uncover whole zones of our own ignorance."
As a family therapist I was taught to throw off the notion that I had expert knowledge about other peoples' lives. To approach people with a not knowing stance. This is a hard pill to swallow, whether you're a newbie therapist or newbie programmer. Your instincts tell you to hide your ignorance, to feign expert knowledge, but this only stunts your growth and inhibits the work you are trying to accomplish.

Taking this lesson with me from one career into another has served me well. I've actually grown attached to feeling ignorant on a daily basis, it lets me know I'm in the right place.

"Everybody on an XP team should feel like an idiot regularly." --Extreme Programming Applied: Playing to Win, p. 62, Ken Auer and Roy Miller

[/links] permanent link

Quotes Manager Project -- Day 12

An ongoing pet project blog...

I am almost finished with the task of adding a quote to an XML file. I have the GUI. I have the class that updates the XML Document. Now what I need is a class that loads the file into a Document and writes the updated Document back to the file.

But how does this class interact with XmlQuoteManager? It should probably be listening for calls to XmlQuoteManager.newQuote in order to know when to write to the file. Maybe I'll just start with that...

    public void testShouldInformListenerOnCallsToNewQuote() throws Exception {
        Mock listener = mock(QuoteListener.class);
        listener.expects(once()).method("update");

        QuoteManager manager = new XmlQuoteManager(parseStub());
        manager.addListener((QuoteListener) listener.proxy());
        manager.newQuote(new Quote("ingored", new Person("joe", "schmoe")));
    }
This leads me to (allow IDEA to) create the QuoteListener interface and add a new method to the (recently renamed) QuoteManager interface and XmlQuoteManager. I run the test and jMock tells me that my QuoteListener interface doesn't have an update method. I'm feeling confident this morning so I'll take a big step and get to green right away...
public class XmlQuoteManager implements QuoteManager {
    private final Document document;
    private final Collection<QuoteListener> listeners = new HashSet<QuoteListener>();

    public XmlQuoteManager(Document document) {
        this.document = document;
    }

    public void newQuote(Quote quote) {
        Node quoteNode = document.createElement("quote");

        quoteNode.appendChild( createTextNode(quote) );

        if (quote.source != NO_SOURCE)
            quoteNode.appendChild( createSourceNode(quote) );

        for (Person author : quote.authors)
            quoteNode.appendChild( createPersonNode(author, "author") );

        if (quote.quoter != NOBODY)
            quoteNode.appendChild( createPersonNode(quote.quoter, "quoter") );

        document.getDocumentElement().appendChild( quoteNode );

        for (QuoteListener listener : listeners)
            listener.update();
    }

    public void addListener(QuoteListener quoteListener) {
        listeners.add(quoteListener);
    }

    ...
}
But that won't even compile. I still need to add the update method to the QuoteListener interface. F2, then ALT-ENTER, then run tests and we're green.

At this point I could actually code the rest up in the main method. I think I'll do that just to see everything in action...

package com.redsquirrel.quotes.gui;

import com.redsquirrel.quotes.xml.QuoteListener;
import com.redsquirrel.quotes.xml.XmlQuoteManager;
import org.w3c.dom.Document;

import javax.swing.JFrame;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.FileOutputStream;

public class MAIN {
    private static final String TITLE = "Quote QuoteManager";

    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame(TITLE);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        final String xmlFile = "play.xml";
        final Document document = builder.parse(xmlFile);

        QuoteManager manager = new XmlQuoteManager(document);
        manager.addListener(new QuoteListener() {
            public void update() {
                try {
                    Transformer transformer = TransformerFactory.newInstance().newTransformer();
                    Source source = new DOMSource(document);
                    Result result = new StreamResult(new FileOutputStream(xmlFile));
                    transformer.transform(source, result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        QuoteManagerPanel quoterManager = new QuoteManagerPanel(manager);
        frame.getContentPane().add(quoterManager);

        frame.pack();
        frame.setVisible(true);
    }
}
I entered a quote and it updated the XML! But there were a few issues...it added an extra (empty) author and an empty quoter. Rather than fix it now, with the new code in the main method, I'll extract it and test. Or more accurately, test then extract.

Starting with an empty test, I pause for a moment to consider what this listener will need in order to do its work... Oh, that's easy, just check for which variables had to be declared final in the main method...the file name and the Document. But I don't want to use a file name, I'd rather stick with streams. So I'll do it like this...

package com.redsquirrel.com.quotes.xml;

import org.jmock.MockObjectTestCase;
import org.w3c.dom.Document;
import com.redsquirrel.quotes.xml.QuoteListener;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.ByteArrayOutputStream;

public class XmlQuoteListenerTest extends MockObjectTestCase {
    public void testShouldWriteDocumentToFileWhenUpdated() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse("stub.xml");

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        QuoteListener listener = new XmlQuoteListener(document, output);
    }
}
I'll start by just getting that to compile. I'm tired and I'm not ready to think of any asserts yet. Here is the glorious class in it's primordial state:
package com.redsquirrel.com.quotes.xml;

import com.redsquirrel.quotes.xml.QuoteListener;
import org.w3c.dom.Document;

import java.io.OutputStream;

public class XmlQuoteListener implements QuoteListener {
    public XmlQuoteListener(Document document, OutputStream output) {
    }

    public void update() {
    }
}
OK, no more laziness, time for an assert (or two)...
    public void testShouldWriteDocumentToFileWhenUpdated() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse("stub.xml");

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        QuoteListener listener = new XmlQuoteListener(document, output);

        assertEquals("", output.toString());

        listener.update();

        String expectedOutput = documentToText(document);
        assertEquals(expectedOutput, output.toString());
    }
And that fails. I'll extract the stuff in the main method and see if that does it... Yep, back to green. Here it is...
public class XmlQuoteListener implements QuoteListener {
    private final Document document;
    private final OutputStream output;

    public XmlQuoteListener(Document document, OutputStream output) {
        this.document = document;
        this.output = output;
    }

    public void update() {
        try {
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            Source source = new DOMSource(document);
            Result result = new StreamResult(output);
            transformer.transform(source, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Hmm, so all my tests are passing, but the problem still persists with the extra XML elements. I'm going to explore for a while to see where the problem is...

[/projects/quotes] permanent link


powered by blosxom