Sunday, December 28, 2008

Thoughts on being an apprentice

This summer, I wrote a small data importer program. I decided to do it non-Test-Driven-Development (TDD) to check some statements I'd been making. For example, I frequently assert that, once you get into the swing of TDD, it actually speeds you up. Well, it had been a while since I did something non-TDD, so I figured I'd try it out.

What happened? Well, for one, it took me a really long time. I kept IRB open, so I could load my module and try out the import routines. The manual aspect of the testing really slowed me down.

Another insight came out of the experience: the code looked similar to code I would have written with TDD. There were several small, task-oriented methods that a large, coordinating method gluing them together. I could see there were a few things that would have been different with TDD, but the general pattern was there. Until today, I wrote it off as just an item of interest.

In his book, The Craftsman, Richard Sennett makes a very thought-provoking point when talking about Correct vs Practical: the idea of quality vs functionality. As craftsmen, we strive for quality in our code. We look to use proper practices and techniques, such as abstraction layers, single responsibility, etc. However, when the time comes, we sometimes resort to cutting corners for production of functionality. Focusing entirely on quality, we would never allow an abstraction to leak or for a unit to have more than one responsibility/reason to change. In reality, though, we sometimes allow these, knowing that we can deal with them through another technique. Sennett's example is a woodworker who, focusing on an absolute measure of quality, "will shave a mortise-and-tenon joint until the two pieces are completely rigid, needing no screws." Focusing on the measure of functionality, the carpenter will "curb worry about each detail, knowing that small defects can be corrected by hidden screws."

How does my experiment with non-TDD relate to what Sennett is saying? The answer is in the shape and form of the code I wrote that was non-TDD. Over the past few years, I've practiced with isolation/mock frameworks that help me find the appropriate abstraction layers by virtue of 'Programming by Wishful Thinking.' TDD has led me to focus on small methods that have a single responsibility, as they are much easier to test. Due to the years of rigorously applied TDD, I find that I make more unconscious decisions about when to apply these patterns. So, when I take away the 'training wheels' of TDD, my designs are better than they would be if I hadn't practiced those techniques.

I've talked to people lately about the idea of being a journeyman. How do you know when you are ready? I don't have the answer, by any stretch, but I have an idea. During the apprentice phase, a person is busy learning. They are practicing specific techniques, rigorously applying rules and procedures. Over time, having been influenced by many mentors, an apprentice starts to develop their own toolbox, the set of practices that they systematically apply. These practices form a basis for further development, a core that an apprentice can build upon.

While discussing what makes a craftsman at the software craftsmanship summit a couple weeks ago, someone mentioned having a core set of techniques/principles that a person applies in their work; I like this idea more and more. A craftsman is mindful of his work, not just the end result, but during the act of creation, as well. The principles that hold to are not static; they can and should change as they are exposed to different ideas. However, there must be a basis to build upon. This foundation is what is established during an apprentice stage.

Just my thoughts.

[Update: Mark Needham referred me to developing a Personal Practices Map. Wonderful! That's just what I'm thinking of.]


(feel free to leave a comment here or put a link to your response on your blog)

2 comments:

  1. I read this post last night, then this morning realized what seemed so strange to me: it's your remark about how testing with irb was slower than your typical approach.

    I *live* in the interpreter; most of my coding is copy-pastes from the interpreter into my code.

    Can you explain a little more about what made it so slow to use irb?

    ReplyDelete
  2. Well, I ordinarily would write an executable spec for the code. As a side-effect, I have a set of tests that verify all previous behaviors.
    Manually verifying in IRB slowed me down, since it is much quicker to just run code that verifies/validates your work.
    So, the main difference was time(manual) > time(executable)

    ReplyDelete

Please don't post anonymously; I won't use your name/url for anything insidious.
Comments by Anonymous are not guaranteed to make it through moderation.
Constructive comments are always appreciated.