A Story Isn’t a Feature

Your business doesn’t want to hear about how you “delivered stories.”

story
Image source: AJC1, Flickr

The business wants you to deliver value, in the form of what they most likely refer to as features.

A story should trigger a conversation around a feature request. Your software product doesn’t contain stories, it contains features that help users accomplish goals.

Wikipedia describes a user story as “one or more sentences” that capture a user’s needs. Those sentences represent a brief description or summary based on the planning and preparation so far, and they are reminders of the conversation that you must continue to have around the feature you will build. The sentences are not the feature. They aren’t even the full story, in most cases, as it often takes the requester several more oral sentences or even paragraphs to say what they really want.

Why Does This Matter?

If you think about stories as a simplified form of a requirements document (“a couple sentences”), you’re constraining agile to be a small tweak to how you did things before, with a trendier name. That diminishes the most important aspect of how agile can help you deliver software successfully: Through continual collaboration and oral communication. Without understanding of the importance of collaboration, stories end up being another waterfall-like hand-off document.

On another front: Too many teams waste time over the wording of a story. It’s a discussion point, not an artifact. The “standard” story template should help you clarify feature requests by asking you to think about who needs them and why. But otherwise spending time on precise word-smithing of a story writeup misses the point. You’re going to discuss it, and you can iron out any ambiguities as you do.

Stories provide only minimal value as an artifact. The ensuing discussion is where the details come out. Unfortunately, most of us can’t fully recall details from a conversation a half year prior, or even a half month prior. Granted, the details end up embodied in the software, but source code more often than not does a poor job of expressing such details concisely and simply. Even programmers must sometimes spend hours to determine just what the code does for the smallest of features.

We need a better artifact than the few sentences that represent a story. Many successful teams have found that the best way to capture system behaviors is in the form of acceptance tests, best derived by use of acceptance test-driven development (ATDD) or BDD. The acceptance tests supplant the nebulous conversation with real meat. A test’s name describes the goal it accomplishes, and its narrative provides an example that quickly relates the details of system behavior. The set of test names describes the complete set of designed system behaviors. The tests capture the important detail behind the discussion triggered by the story.

A story is not a feature, and it should always trigger a conversation.

Posted in acceptance testing, agile, ATDD, BDD, stories, TDD, test-driven development | 3 Comments

C++11: Using Lambdas to Support a Times-Repeat Loop

lambdaMore often than not, explicit for loops that execute n times usually need to know what what n is. Sometimes they don’t, in which case the for-loop structure seems mildly tedious. It’s certainly idiomatic, and you’ve seen ‘em all a million times:

for (auto i = 0; i < 5; i++) {
   // code to repeat here
}

Still, having seen them all a million times, you’ve encountered many either accidental or purposeful variations:

for (auto i = 1; i < 5; i++) {
   // code to repeat here
}
for (auto i = 0; i <= 5; i++) {
   // code to repeat here
}
for (auto i = 5; i >= 0; i++) {
   // code to repeat here
}
for (auto i = 0; i < 5; i += 2) {
   // code to repeat here
}

The result: A for loop is something you must always pause to inspect. It’s too easy for your brain to miss one of these variants.

I’d much rather be able to simply send a timesRepeat message to an integer, as I can do in Smalltalk (Ruby has a similar message, times):

   5 timesRepeat: [ "code to repeat here" ]

In C++, overloading the multiplication operator would allow me to code something like:

   5 * [] { /* code to repeat here */ }

(The [] is the capture clause, which indicates that the following expression is a lambda, and also specifies how variables might be captured. In this case, the empty capture clause [] indicates that no variables are captured.)

A lambda function is of type function, therefore I can support the times-repeat expression with the following:

int operator*(int number, function<void (void)> func) {
   for (int i = 0; i < number; i++) func();
}

In other words, simply translate the lambda into the corresponding for loop, iterating number times, and calling func() each time, where number represents the receiver (5 in the example usage above).

A simple test demonstrates that the construct works:

unsigned int totalGlobal;

TEST(Lambdas, CanSupportedASimplifiedTimesRepeatLoop) {
   5 * [] { totalGlobal++; };

   ASSERT_THAT(totalGlobal, Eq(5));
}

(Use of the global variable totalGlobal allows writing the lambda expression without requiring variable capture.)

The resulting expression is succinct and even better than idiomatic, it’s obvious. I don’t think I’d go as far as to say that you should replace all for loops, just ones where access to the index is not required.

You could, of course. Here’s the result, a couple small tweaks to the first solution.

int operator*(int number, function<void (int)> func) {
   for (int i = 0; i < number; i++) func(i);
}

TEST(Lambdas, CanUnnecessarilyReplaceAForLoop) {
   unsigned int total{0};

   4 * [&] (int i) { total += i; };

   ASSERT_THAT(total, Eq(0 + 1 + 2 + 3));
}

The operator overload now simply passes the loop index i to the function being executed.

In the test, I now choose to use a local variable to capture the total. The capture clause of [&] indicates that any variable accessed by the lambda function is captured by reference. The function now also includes a parameter list, specifying in this case the index parameter of i.

I doubt I’d promote use of the second form (lambda with indexer) in production code. The first (times repeat) seems succinct and appropriate.

Posted in C++, programming, TDD, test-driven development, test-focused learning, TFL, unit testing | Leave a comment

C++ Via TFL: The Range-Based For Loop

C++ catches up with constructs common in Java and C# with the new range-based for loop.

catInfinity
Image source: normanack, Flickr

A simple example should suffice to demonstrate its common use.

TEST(ARangeBasedForLoop, CanIterateOverAVector) {
   vector collectedNumbers;
   vector numbers{1, 2, 3};

   for (int each: numbers)
      collectedNumbers.push_back(each);

   ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}

It’s always nice to know the appropriate names for the syntactical parts of things. Here’s an overly clever test that shows what things are called in the range-based for loop.

TEST(ARangeBasedForLoop, HasAppropriateNamesForItsSyntacticalParts) {
   typedef int type_specifier_seq;
   vector expression{1, 2, 3, };
   vector collectedNumbers;
   auto statement = [&] (int each) -> void { collectedNumbers.push_back(each); };

   for (type_specifier_seq simple_declarator: expression) statement(simple_declarator);

   ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}

The range-based for loop supports iterating over a number of common things. Here are a few.

TEST(ARangeBasedForLoop, CanIterateOverAnArray) {
   vector collectedNumbers;
   int numbers[]{1, 2, 3};

   for (int each: numbers)
      collectedNumbers.push_back(each);

   ASSERT_THAT(collectedNumbers, Eq(vector { 1, 2, 3}));
}

TEST(ARangeBasedForLoop, CanIterateOverAMap) {
   map dictionary{{1, "uno"}, {2, "dos"}};
   map collectedDictionary;

   for (pair pair: dictionary)
      collectedDictionary[pair.first] = pair.second;
   ASSERT_THAT(collectedDictionary, Eq(map{{1, "uno"}, {2, "dos"}}));
}

TEST(ARangeBasedForLoop, CanIterateOverAString) {
   vector collectedChars;

   for (char each: "abc")
      collectedChars.push_back(each);

   ASSERT_THAT(collectedChars, Eq(vector{ 'a', 'b', 'c', 0 }));
}

I’ve shown explicit type information in the prior examples. You should usually prefer auto, instead.

TEST(ARangeBasedForLoop, ShouldPreferAnInferencingTypeSpecifier) {
   vector collectedNumbers;

   for (auto each: {1, 2, 3})
      collectedNumbers.push_back(each);

   ASSERT_THAT(collectedNumbers, Eq(vector{ 1, 2, 3}));
}

If you want to update each element as you iterate…

TEST(ARangeBasedForLoop, CanAccessEachElementByReference) {
   vector strings{"a", "b"};

   for (auto& each: strings)
      each += each;

   ASSERT_THAT(strings, Eq(vector{ "aa", "bb" }));
}

Or if you want better performance, but want to disallow modifying each element.

TEST(ARangeBasedForLoop, CanAccessEachElementByConstReference) {
   vector strings{"a", "b"};
   vector collectedStrings;

   for (const auto& each: strings)
      // each += each; // fails compilation
      collectedStrings.push_back(each);
     

   ASSERT_THAT(strings, Eq(vector{ "a", "b" }));
}

The range-based for loop can iterate over anything that implements begin() and end() (each returning an iterator object–something that implements operator!=, operator*, and operator++). Here is a simple class that supports iteration across a range of numbers, (also supporting the ability to skip elements).

class IntSequence {
public:
   IntSequence(int start, int stop, int by=1): start_{start}, stop_{stop}, by_{by} {}

   class iterator {
   public:
      iterator(int value, int by) : current_(value), by_(by) {}

      bool operator!=(const iterator& rhs) const {
         return current_ <= rhs.current_;
      }

      int& operator*() { return current_; }

      iterator operator++() {
         current_ += by_;
         return *this;
      }
   private:
      int current_;
      int by_;
   };
   iterator begin() { return iterator(start_, by_); }
   iterator end() { return iterator(stop_, by_); }

private:
   int start_;
   int stop_;
   int by_;
};

And here’s an example demonstrating use:

TEST(ARangeBasedForLoop, CanIterateOverAnythingImplementingBeginAndEnd) {
   vector collectedNumbers;
   int start{3};
   int stop{10};
   int by{2};
   IntSequence sequence(start, stop, by);

   for (auto each: sequence)
      collectedNumbers.push_back(each);

   ASSERT_THAT(collectedNumbers, Eq(vector{3, 5, 7, 9}));
}

(This example could be construed as contrived: Why not simply use a regular ol’ for loop? Most of the time, that idiom is simple and probably preferred, but you might find value in the ability to pass around a sequence concept to other functions, or to serialize it, or otherwise use it where having an object abstraction might simplify code.)

Note: Code build using gcc 4.7.2 under Ubuntu.

Posted in C++, programming, TDD, test-driven development, test-focused learning, TFL, unit testing | Leave a comment

C++11: Sum Across a Collection of Objects Using a Lambda or a Range-Based For Loop

I’m always disappointed when my Google search doesn’t turn up useful results on the first page. Often, an answer to a C++ question takes me to a StackOverflow page, or to a cplusplus page, where I usually find what I want. This time I didn’t find what I want on the first page (the stackoverflow link wasn’t quite it), hence this blog post.


Image source: jfgormet, Flickr

I’ve just started a series on test-focused learning (TFL) for the new C++11 features. I’m jumping the gun a little here since I’ve not yet covered lambdas (or auto, or the range-based for, or rvalue references), but I wanted searchers to have (what I think is) a better answer on the first page.

The story: Iterate a vector of objects, adding into a sum by dereferencing a member on each object, then return the sum from a function.

Here’s the declaration for the Item class:

class Item {
public:
   Item(int cost) : cost_{cost} {}
   int Cost() { return cost_; }
private:
   int cost_;
};

Here’s the assertion (I coded this test-first, of course):

ASSERT_THAT(
   TotalCost({Item(5), Item(10), Item(15)}),
   Eq(5 + 10 + 15));

You can get this test to pass in three statements using the range-based for loop.

int TotalCost(vector<Item>&& items) {
   int total{0};
   for (auto item: items) total += item.Cost();
   return total;
}

That’s clean and simple to understand, explanation of syntax barely needed.

Here’s the implementation using accumulate and a lambda.

int TotalCost(vector<Item>&& items) {
   return accumulate(items.begin(), items.end(), 0,
     [] (int total, Item item) { return total + item.Cost(); });
}

That’s it. If you’re not familiar with accumulate (I wasn’t, hence my Google search), it takes a range, an initial value, and a function. If you’re not familiar with using lambdas to declare functions:

  • [] declares that the lambda requires no capture of other variables
  • between () is the parameter list for the arguments that accumulate passes to the function
  • between the {} is the function’s implementation

Which do you prefer (or neither), and why?

Posted in C++, TDD, test-driven development, test-focused learning, TFL, unit testing | Tagged , , , , | 5 Comments

C++11 Via TFL (Test-Focused Learning): Uniform Initialization

I’ve been working with the new features of C++11 for many months as I write a book on TDD in C++. The updates to the language make for a far-more satisfying experience, particularly in terms of helping me write clean code and tests.

I haven’t written any Java code for over a half-year, and I don’t miss it one bit (I do miss the IDEs a bit, although I’m enjoying working in vim again, particularly with a few new tips picked up from Use Vim Like a Pro and Practical Vim). New language features such as lambdas and type inferencing represent a leap-frogging that shine a little shame on Oracle’s efforts.

source: Naval History and Heritage Command
Image source: Naval History & Heritage Command

Over a series of upcoming blog entries, I will be demonstrating many of the new language features in C++ via test-focused learning (TFL). This entry: uniform initialization, the new scheme for universally-consistent initialization that also simplifies the effort to initialize collections, arrays, and POD types.

One goal of this blog series is to see how well the tests can communicate for themselves. Prepare for a lot of test code (presume they all pass, unless otherwise noted) and little blather. Please feel free to critique by posting comments; there’s always room for improvement around the clarity of tests (particularly regarding naming strategy). Note that TFL and TDD have slightly different goals; accordingly, I’ve relaxed some of the TDD conventions I might otherwise follow (such as one assert per test).

The Basics

Note: Your compiler may not be fully C++11-compliant. The examples shown here were built (and tested) under gcc 4.7.2 under Ubuntu. The unit testing tool is Google Mock (which supports the Hamcrest-like matchers used here, and includes Google Test).

TEST(BraceInitialization, SupportsNumericTypes) {
   int x{42};
   ASSERT_THAT(x, Eq(42));

   double y{12.2};
   ASSERT_THAT(y, DoubleEq(12.2));
}

TEST(BraceInitialization, SupportsStrings) {
   string s{"Jeff"};
   ASSERT_THAT(s, Eq("Jeff"));
}

TEST(BraceInitialization, SupportsCollectionTypes) {
   vector<string> names {"alpha", "beta", "gamma" };
   ASSERT_THAT(names, ElementsAre("alpha", "beta", "gamma"));
}

TEST(BraceInitialization, SupportsArrays) {
   int xs[] {1, 1, 2, 3, 5, 8};
   ASSERT_THAT(xs, ElementsAre(1, 1, 2, 3, 5, 8));
}

Those tests are simple enough. Maps are supported too:

TEST(BraceInitialization, SupportsMaps) {
   map<string,unsigned int> heights {
      {"Jeff", 176}, {"Mark", 185}
   };

   ASSERT_THAT(heights["Jeff"], Eq(176));
   ASSERT_THAT(heights["Mark"], Eq(185));
}

Explicit initialization of collections isn’t nearly as prevalent in production code as it is in tests. I’m tackling uniform initialization first because I’m so much happier with my resulting tests. The ability to create an initialized collection in a single line is far more expressive than the cluttered, old-school way.

TEST(OldSchoolCollectionInitialization, SignificantlyCluttersTests) {
   vector<string> names;

   names.push_back("alpha");
   names.push_back("beta");
   names.push_back("gamma");

   ASSERT_THAT(names, ElementsAre("alpha", "beta", "gamma"));
}

No Redundant Type Specification!

Uniform initialization eliminates the need to redundantly specify type information when you need to pass lists.

TEST(BraceInitialization, CanBeUsedOnConstructionInitializationList) {
   struct ReportCard {
      string grades[5];
      ReportCard() : grades{"A", "B", "C", "D", "F"} {}
   } card;

   ASSERT_THAT(card.grades, ElementsAre("A", "B", "C", "D", "F"));
}
TEST(BraceInitialization, CanBeUsedForReturnValues) {
   struct ReportCard {
      vector<string> gradesForAllClasses() {
         string science{"A"};
         string math{"B"};
         string english{"B"};
         string history{"A"};
         return {science, math, english, history};
      }
   } card;

   ASSERT_THAT(card.gradesForAllClasses(), ElementsAre("A", "B", "B", "A"));
}
TEST(BraceInitialization, CanBeUsedForArguments) {
   struct ReportCard {
      vector<string> subjects_;

      void addSubjects(vector<string> subjects) {
         subjects_ = subjects;
      }
   } card;

   card.addSubjects({"social studies", "art"});

   ASSERT_THAT(card.subjects_, ElementsAre("social studies", "art"));
}

Direct Class Member Initialization

Joyfully (it’s about time), C++ supports directly initializing at the member level:

TEST(BraceInitialization, CanBeUsedToDirectlyInitializeMemberVariables) {
   struct ReportCard {
      string grades[5] {"A", "B", "C", "D", "F"};
   } card;

   ASSERT_THAT(card.grades, ElementsAre("A", "B", "C", "D", "F"));
}

Class member initialization essentially translates to the corresponding mem-init. Be careful if you have both:

TEST(MemInit, OverridesMemberVariableInitialization) {
   struct ReportCard {
      string schoolName{"Trailblazer Elementary"};
      ReportCard() : schoolName{"Chipeta Elementary"} {}
   } card;

   ASSERT_THAT(card.schoolName, Eq("Chipeta Elementary"));
}

Temporary Type Name

TEST(BraceInitialization, EliminatesNeedToSpecifyTempTypeName) {
   struct StudentScore {
      StudentScore(string name, int score)
         : name_(name), score_(score) {}
      string name_;
      int score_;
   };
   struct ReportCard {
      vector<StudentScore> scores_;
      void AddStudentScore(StudentScore score) {
         scores_.push_back(score);
      }
   } card;

   // old school: cardAddStudentScore(StudentScore("Jane", 93));
   card.AddStudentScore({"Jane", 93});

   auto studentScore = card.scores_[0];
   ASSERT_THAT(studentScore.name_, Eq("Jane"));
   ASSERT_THAT(studentScore.score_, Eq(93));
}

Be careful that use of this feature does not diminish readability.

Defaults

TEST(BraceInitialization, WillDefaultUnspecifiedElements) {
   int x{};
   ASSERT_THAT(x, Eq(0));

   double y{};
   ASSERT_THAT(y, Eq(0.0));  

   bool z{};
   ASSERT_THAT(z, Eq(false));

   string s{};
   ASSERT_THAT(s, Eq(""));
}
TEST(BraceInitialization, WillDefaultUnspecifiedArrayElements) {
   int x[3]{};
   ASSERT_THAT(x, ElementsAre(0, 0, 0));

   int y[3]{100, 101};
   ASSERT_THAT(y, ElementsAre(100, 101, 0));
}
TEST(BraceInitialization, UsesDefaultConstructorToDeriveDefaultValue) {
   struct ReportCard {
      string school_;
      ReportCard() : school_("Trailblazer") {}
      ReportCard(string school) : school_(school) {}
   };

   ReportCard card{};

   ASSERT_THAT(card.school_, Eq("Trailblazer"));
}

Odds & Ends

TEST(BraceInitialization, CanIncludeEqualsSign) {
   int i = {99};
   ASSERT_THAT(i, Eq(99));
}

… but why bother?

It’s always nice when a new language feature makes it a little harder to make the dumb mistakes that we all tend to make from time to time (and sometimes, such dumb mistakes are the most devastating).

TEST(BraceInitialization, AvoidsNarrowingConversionProblem) {
   int badPi = 3.1415927;
   ASSERT_THAT(badPi, Eq(3));

   int pi{3.1415927}; // emits warning by default
//   ASSERT_THAT(pi, Eq(3.1415927));
}

Running the AvoidsNarrowingConversionProblem test results in the following warning:

warning: narrowing conversion of ‘3.1415926999999999e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]

Recommendation: use the gcc compiler switch:

-Werror=narrowing

…which will instead cause compilation to fail.

Use With Auto

TEST(BraceInitialization, IsProbablyNotWhatYouWantWhenUsingAuto) {
   auto x{9};
   ASSERT_THAT(x, A<const initializer_list<int>>());
   // in other words, the following assignment passes compilation. Thus x is *not* an int.
   const initializer_list<int> y = x;
}

The Most Vexing Parse?

It’s C++. That means there are always tricky bits to avoid.

TEST(BraceInitialization, AvoidsTheMostVexingParse) {
   struct IsbnService {
      IsbnService() {}
      string address_{"http://example.com"};
   };

   struct Library {
      IsbnService service_;
      Library(IsbnService service) : service_{service} {}
      string Lookup(const string& isbn) { return "book name"; }
   };

   Library library(IsbnService()); // declares a function(!)
//   auto name = library.Lookup("123"); // does not compile

   Library libraryWithBraceInit{IsbnService()};
   auto name = libraryWithBraceInit.Lookup("123");

   ASSERT_THAT(name, Eq("book name"));
}

All the old forms of initialization in C++ will still work. Your best bet, though, is to take advantage of uniform initialization and use it at every opportunity. (I’m still habituating, so you’ll see occasional old-school initialization in my code.)

Posted in C++, TAD, TDD, test-focused learning, TFL, unit testing | 6 Comments

A TDD Bag of Tricks

Over the many years I’ve been practicing TDD, I’ve sought to incorporate new learning about what appears to work best. If you were to look at my tests and code from 1999, you’d note dramatic differences in their structure. You might glean that my underlying philosophy had changed.

Yet my TDD practice in 2012 is still TDD. I write a test first, I get it to pass, I clean up the code. The core goal of sustaining low-cost maintenance over time remains, and I still attain this goal by crafting high quality code–TDD gives me that opportunity.

TDD is the same, but I believe that new techniques and concepts I’ve learned in the interim are helping me get better. My bag of TDD “tricks” acquired over a dozen years includes:

There is of course no hard data to back up the claims that any of these slightly-more-modern approaches to TDD are better. Don’t let that stop you–if you waited for studies and research to proceed, you’d never code much of anything. The beauty of TDD is that its rapid cycles afford many opportunities to see what works best for you and your team.

(But remember: You only really know how good your tests and code are when others must maintain them down the road.)

Should you push more in the direction of one assert per test? Is Given-When-Then superior to Arrange-Act-Assert? Those are entertaining things to debate, but you’re better off finding out for yourself what works best for your team. Also, be willing to accept that others might have an even better approach.

I recommend investigating all of the above ideas and more–keep your eyes open! Just don’t get hung up on any one or assume it’s the One True Way. The practice of TDD is a continual voyage of exploration, discovery, and adaptation.

Posted in coding, design, programming, TDD, test abstraction, test-driven development, unit testing | 1 Comment

The Consulting Legitimacy Cycle

Pausing to determine where you’ve come from and why you are where you are can be a valuable exercise in knowing where you want to go, as well as in understanding the value you can provide. An exploration of my past dozen consulting years taught me that taking an occasional break from consulting has improved my ability to better understand and help subsequent customers.consulting :-)

In 1998, after over 15 years of professional development, I thought I’d be Mr.-Retire-From-Big-Corporation-With-Nice-Pension. In 1998, I had just reached my five-year mark with MCI. When WorldCom came around seeking souls to connect to the hive mind, my research determined that they didn’t provide such a hot place to work. I was in the 1% of shareholders who voted against it. Sometimes being right isn’t gratifying (what a debacle).

I moved on to a local dot-com, the now-defunct ChannelPoint. In 1999, I learned about XP** and did what of it I could–mostly some TDD–while isolated as a professional services developer in my shared office. Cool stuff!

In 2000, with ChannelPoint’s collapse pending, I excitedly began my foray into the world of consulting. I worked proudly for Object Mentor for two years. I then joined an XP team and enjoyed pair-programming day-to-day as “just another developer” for seven months.

An important revelation hit me: Promoting XP as a consultant and practicing XP as a team member are two completely different things. I’d preached about a few things–pairing, most significantly–that I’d done only as an external party, not as a true team member. Until you experience what pairing is like, day-in day-out for an extended period of time, you can’t possibly understand what it really feels like. And pairing felt good, but I also learned to recognize some problems with it.

I knew I had gathered very relevant information, and started consulting on my own. Cool! Well, the wife didn’t think so; she preferred me home. In 2004, I took on a local position again as a developer, and ended up a developer/manager for the better part of a year. I learned a lot more about what makes teams excel–I had a kick-ass development team, and they weren’t even doing much in an XP sense.

The call of consulting took me again on the road to help other teams learn what I’d learned. I transitioned to being an internal consultant at a large shop pushing the mandate of Thou Shalt All Be Agile All At Once (it doesn’t work, believe me). I started feeling out of touch, and the unending barriers presented by a rigid corporate structure made things all the more frustrating.

I returned to development as a remote programmer for GeoLearning for most of 2010. More learning about what works and what doesn’t! After GeoLearning was bought out and all remote individuals terminated, and after a local 3-month “just a developer” contract, I figured it was time to spread words again.

(The sneaky part: Employee vacation time over the years provided an opportunity to take on short training and consulting engagements here and there, largely to give me a fix for my books and training addictions.)

I’ve been back in full consulting swing for about 15 months. I wonder when that will need to end again. Yes, I could remain a full-time consultant and still provide good value for my customers. But I’ve found I can be even better by finding a way to occasionally immerse in a development team, to remain current, open-minded, and relevant.

** Extreme Programming. That’s for you, J.B. It’s not dead yet.

Posted in agile, consulting, extreme programming, XP | Leave a comment

TDD: A Minority Report

Tim, a modern urbane developer (i.e., he willingly practices TDD), talks about the reality of living in his team, circa 2008, where his organization was attempting to grow TDD.

The Minority Report LexusI am not gonna lie, this place is crazy with buzz words and terms.  They use words like Agile and Scrum as adjectives as opposed to ways of thinking and common sense still.  I hear, “Well, we are doing Agile, so we don’t have time for design.”  My jaw hits the floor… and then I start drinking heavily.

The team I am on currently, is a “center of excellence,” so to speak. We consult development teams within the organization (along with owning development projects, components, and standards). I feel pretty confident in my abilities to help teams out. I have no problem saying that I do not think of myself as an expert or perfect (yet). I have always been of the opinion that this should be a constant learning experience. I find it difficult to relay my thoughts, though, to the rest of the guys on my team. It is basically the three of them attacking me. It’s not totally me vs. the world, but it turns into a joke. I hear:

“…so basically you want me to double my development…”
“…there is no design in TDD…”
“…there is no empirical evidence that says TDD improves code…”
“…I have beens in shops before where the test cases were bogus…”

So…I think I know how you must feel sometimes :)  I never once say that it is a silver bullet. What I tell them is I didn’t realize how little I knew until I started using it as an approach. Primarily from a design and coding point of view. Don’t get me wrong, I have some smart-ass retorts, but I am just outnumbered :)  It’s a tad difficult for me to try and persuade them, because we all are strong developers (I think) on the same hierarchy, and everyone has their own opinions.

Fortunately or unfortunately, they will have to come on board though, because this is coming from the top down. The quality of code, and overall abilities of the development teams here is borderline atrocious for most projects. At least the ones we see on a regular basis. The good teams obviously don’t need our help. To put it bluntly, I have seen one too many 5000 line classes than I care to ever see again.

I think they (my teammates) have just had either bad personal experiences with TDD/Agile/etc., or bad education on it.

A commenter on last week’s post indicated that he viewed being the minority as an opportunity to show how much better things can go with TDD–fewer defects and getting code done sooner. That’s true, and it can feel gratifying, but you’re not likely to gain new converts by effectively showing them up–at least not immediately.

In a den of dogs with old tricks, some of the smart ones eventually come around, but in the short term they’re unlikely to admit that the new tricks might be better. Tim himself was sure his old way of coding (no TDD) was better until well after I’d left his scene.

In any case, from Tim we once again hear how bad experiences with TDD and agile can sour people. (Remember, this is four years ago.) What can we do to help? Here are a couple thoughts:

  1. Don’t do agile or TDD unless you’re willing to invest in its core notion of continual, honest retrospection and adaptation. You will not succeed with it over time otherwise.
  2. Relay more stories. Is your company succeeding with TDD, or do you know of one that is? Please tell your success story, or help your buddy write a blog post telling theirs. (And hope they’re not at a place where they view TDD as a competitive advantage to be kept hush-hush.)

Related posts:

 

Posted in agile, design, retrospectives, TDD, test-driven development, unit testing | 2 Comments

TDD: No Fun, No Gain?

Party DogIn “TDD Is Not a Silver Bullet” I reproduced part of an interview with Tim, a sharp young corporate developer that I’d paired with very briefly in 2006. In 2008, he sent me an email about his experiences in the interim. Tim’s stories are what prompted me to want an interview with him. Here’s a portion of the email.

Much to my surprise, it is being conveyed from the head of our development that we need to be “stressing” TDD.  My surprise was… I thought we already were practicing TDD on an enterprise level.  Regardless, I guess they had brought in this guy named [Very Big Agile Name deleted].  (I am sure he runs in your circles.) If you are friends with him, take what I am about to say not as an insult, rather my perception; but if he would have been the expert to introduce me to TDD, I would never even thought of adopting/agreeing with it.  And, as it turns out, the teams he consulted felt the same way.

I have been telling my boss about my experiences with you, and told her it would be like night and day.  I respected the fact, that while TDD was the stressed point with you, you also provided more than just using TDD as a silver bullet per se.  Basic things… like knowing the language, having fun, communicating, and so on.

I got in touch with Tim this week to make sure he was ok with me publishing this stuff four years later. No surprise, Tim is still doing (and presumably enjoying) TDD.

If you’re a “real” programmer, you find coding fun at least some of the time. Of course, it might not necessarily be fun when the code fights you all the way, hiding bugs and throwing nasty dependencies in your path. Still, I happen to enjoy coding always, despite the challenges. Test-driving is fun, too–it’s more code, and it gives me confidence to craft cleaner code, which is even more fun.

Maybe learning doesn’t have to be fun to be effective, but I have to figure sucking the fun out of it on purpose can’t possibly work. (I’m disappointed that a Very Big Agile Guru could do that.) Yes, the sticky challenges of real code mean that you can’t teach TDD with a few trivial, “non-real-world” examples. That’s why I employ a deliberately horked-up, fairly realistic codebase in my training. But the class also learns that it’s only a challenge, not a barrier, and they quickly learn a few simple techniques to surmount the challenge. They have a good time learning TDD.

Ultimately, my students leave the classroom enthusiastic about the prospects for TDD, and confident that they at least have the basic understanding needed to begin tackling their challenges. After that, it’s a matter of their having good support for the practice. (Being the minority in a team, as I’ll relate in a later blog post through Tim’s stories, is a great way to lose that enthusiasm.) When I get to pair with developers to help them through learning TDD, both of us have a good time.

If you’re interested in coming up to speed on TDD, make sure you pick trainers and consultants who don’t suck the fun out of it.

Posted in agile, coding, pair programming, pairing, programming, TDD, test-driven development, unit testing | 2 Comments

“TDD Is Not a Silver Bullet”

As a writer, I’ve left a few incomplete efforts by the wayside, promising I would someday return and complete them. The one I feel most guilty about is an interview with a promising young developer named Tim.it's a gold bullet... get it?

In 2006, I had helped train and coach a development team in TDD at a large corporation’s headquarters in the U.S. In fact, the training and coaching were separated by a month or so. I initially worked with a cool manager who brought me in to run training, but by the time I returned for the follow-up coaching, the manager had departed. In his place was a humorless, dictatorial manager. While working with the team, I focused primarily on helping the individuals see how TDD might help them build quality software. Unfortunately the temperament of the manager meant I departed with few hopes for the success of agile or TDD at the team level.

When pairing with Tim, I could tell he was very sharp but also very skeptical and resistant to TDD. Two years later, I received an email from him. He had some interesting things to say. I asked him some questions on TDD, with the intent of shaping it into an interview I would publish. He asked that I not mention the company at that time. Well, four years later, I still think these are wonderful answers, and so I plan on publishing them over a couple or more blog entries. (Tim–sorry I took so long!)

Here’s one of the questions and answers from Tim.

Q #5. What did you finally see or recognize in TDD that you initially couldn’t see?

A.  I have somewhat of a loaded answer to this question.  I’ll preface with this… I didn’t go out and research TDD on my own.  I had never heard of it, or knew what it meant.  I wasn’t chomping at the bit to change the way I had been writing code for the 5+ years before that.  It was directed from the top down in my organization, and my team was a test bed, so to speak.  Right off the bat, I had preconceived notions as to why I had to learn this approach, but why should I?  So I had no expectations as to what I was supposed to see other than tangible things that management probably thought were the only byproduct of TDD (i.e. JUnit/HttpUnit test cases).

So I can only answer your question with what I have found with using the TDD approach.  It’s rather simple, and yet not so simple.  I have to bullet them:

  • I have found myself writing cleaner, more efficient, maintainable, and better “architected” code.
  •  I have a better understanding on topics as simple and innate as the Java specs, to more complex such as patterns, writing frameworks, and abstraction.
  • Some would laugh, but I never followed the whole “code to the interface, not the spec” statement.  I cannot imagine not following this now.
  • My skills with the editor I use have become extremely advanced.  So much so, that I would challenge anyone who claims TDD will slow you down that I can write the same thing simpler, faster, and tested.
  • I automatically have a spec/documentation (the test cases) since that’s where I start.  Any developer who says they keep up with documentation is flat out lying!
  • I have more confidence.
  • I have the feeling of “getting it”.
  • And I have more fun.

I can admit that I didn’t fully understand TDD until a couple of years ago. So if you do the math, it took @2 years to really sink in, but I know I am better now than I was then (and I thought I was pretty damn good).  I can only surmise that in 2 more years, I will feel the same compared to now.

By no means am I saying that I can solve any and every development endeavor and never get frustrated or challenged, all because of TDD. Development is challenging. If I didn’t get challenged than I would probably, really hate my job. I could do without the frustration, but that goes with the territory… and builds character.  I feel like I have to say this because (and I think I mentioned it before) some developers I talk to think that TDD is a silver bullet to solve these issues just because you have some test cases and a build server, and as you can see (I feel) it’s much more.

One thing that stands out for me is Tim’s notion that he has “more fun.” More on that in an upcoming post.

Posted in agile, junit, TDD, test-driven development, unit testing, writing | 3 Comments