C. Keith Ray

C. Keith Ray writes about and develops software in multiple platforms and languages, including iOS® and Macintosh®.
Keith's Résumé (pdf)

Friday, November 19, 2010

Going meta sometimes helps.

One of the sessions I participated in at AYE 2010 was Steve Smith's session on "Power, Authority, and Teams". We did an exercise where we individually tried to solve a problem (on paper, a problem whose answer can be scored as to correctness) and then we were divided into four teams, with one observer each, to try to solve the same problem.

The team that I was on, started with some direct actions by individuals: deciding we would use a flip-chart on an easel, and moving the flip-chart away from another team (which, funnily enough, put us next to another flip-chart on an easel, so we could have avoided carrying that easel around). One partipant decided to act as scribe, and off we want, problem-solving. I'm sure it looked like pure chaos. And it was. (But not necessarily in a bad way.)

As we worked through the problem, sometimes we'd ask for a show of hands in favor of a particular part of the solution. We never actually formalized how voting would work, and certainly never had more than one third of the group raising their hands. I think a lot of us in the group were trying to gauge agreement by audio and visual cues, but we didn't go "meta" long enough to formalize how we would signal agreement. We didn't appoint roles, or vote on a leader.

You have to remember that a many of us were coaches. "Agile coaches," even. We know about dot-voting (I almost suggested it once during this chaos), consensus/thumb-voting. We know about team-formation, Satir change model, self-organizing teams. Some of us had been through Problem-Solving Leadership training. We didn't use any of those tools. I know I could have, but I didn't, and I expect some others were thinking the same thing.

After struggling with one problem solving technique for a while, one of the Europeans in the group complained that our usage of American-style measurements was making it hard for him to contribute. I wrote "1 meter ≈ 3 feet" on the flip-chart to help him out, but another member of the team proposed working in relative sizes instead of absolute sizes. We transitioned to that problem-solving style and, after a little confusion, the team seemed to jell.

We had come to an agreement on process, though never explicitly agreeing on process through any formal means.

Later, we could compare our individual scores on the answering the problem with our team scores. Our team scored higher than any of our individual scores, though we were in third-place compared to the other three teams. The team with the highest score had chosen to use computers to solve the problem. Imagine! A bunch of software people using computers to solve the problem! :-)

OK, our team had a moment of conversation, early on, where some of us agreed not to use computers for the problem-solving, as that seemed against the spirit of the simulation. Again, not a formally-ratified agreement, but no one in our team went against that decision during the simulation. We were supposed to be learning about power: how people interact with each other, so we didn't bring computers into the simulation.

The team in fourth place had a score that was lower than any of their individual scores. I really wish I could have observed what was going on in that team. Somehow their collective problem-solving skill went completely opposite of their individual skills.

I think that even if we had formalized how our team made its decisions, we probably would have come to the same solution that we did in our informal way. We might have done so faster. We might have included insights from some of the quieter members of the team, and might done a little bit better.

It's also possible that if we had been too formal, we could have done worse, perhaps much worse.

Two points I want to mention.

In a team-building exercise at AYE 2005, my team happened to be all PSL graduates and no one in the other team was a PSL graduate. My team quickly jelled. We decided to enjoy ourselves and not stress out. The other team, across the room, we could see going through "forming, storming, norming, performing" stages. We ended up coming in second-place (of two teams)... but each team was working on a different LEGO model, so the difference in performance could be the difference in complexity of the models, or differences in our LEGO-building skills. As much as we would like to have simple causes-and-effects, sometimes effectiveness in one area doesn't mean success in another.

The other point I want to mention, I just read in The Gift Of Time, in the chapter written by Willem van den Ende: "Solving the Groupthink Problem". He quotes a diagram-of-effects by Jerry Weinberg (Quality Software Management vol. 3) Rather than re-print the image, I'll try to describe it.

"Effectiveness of meta-planning" directly affects "Effectiveness of strategic planning".

"Effectiveness of strategic planning" directly affects "Effectiveness of action planning".

"Effectiveness of action planning" directly affects "Quality of Action".

"Other Factors" directly affects "Effectiveness of action planning" and also directly affects "Quality of Action".

"Change Artistry" directly affects "Quality of Action".

That may or may not speak for itself. (Probably not, but it's getting late.) Willem has a paper here that shows other diagrams of effects. Diagramming systems can be useful.

And one last thing... In a discussion with the "power" team, I listed a bunch of different ways we can have power (or influence): speaking louder, speaking more persuasively, deciding on a process for the team to follow, holding the marker and thus acting as scribe...

That last idea had some impact on the participant who had been acting as scribe. She had been thinking that by acting as scribe, she would be avoiding taking power in this simulation. But the scribe can influence what gets recorded, who gets listened to. It's a position of power. We coaches who sometimes facilitate meetings by acting as scribe, should keep that in mind.

There are all sorts of power.

Thursday, November 4, 2010

Beautiful tools

Go to this page and scroll down to Figure 4. Apple's IDE graphically shows you the results of static analysis by drawing arrows over the code.

Beautiful.

Wednesday, November 3, 2010

Virtual Functions in C++ make TDD easier, but at what cost?

Virtual functions in C++ make allow us to do mocking, stubbing, and faking, which helps us test code in isolation in TDD or just microtesting in general. A frequent objection to virtual functions is that it "costs more" than simple function calls.

The cost of a virtual function is hardly ever important, unless the function is being called in a very tight loop, or it is being called at interrupt-time, and even then compiler / linker optimization might overcome the "extra" table-lookup that makes a function virtual.

In C, calling a function through a function-pointer is equivalent to calling a virtual function in C++. Implementations of file-systems APIs in Linux and BSD Unix use function-pointers internally to do what C++ does with virtual functions.

The cost of cache-misses (on-chip cache and off-chip cache) is a LOT bigger than the cost of virtual function pointers. Incorrect branch-prediction is also a CPU-level cost that people fail to understand.

The best way to know if making a function virtual has any user-apparent cost, is to measure the application via some tool that does non-invasive timing. Often, there will be no visible effect if you make some functions virtual that were non-virtual before. (I'm talking about REAL applications, not benchmarks.)

Saturday, October 23, 2010

Summoning the Demon: Testing

James Bach, advocate of exploratory testing and one of the founders of context-driven testing movement. He gave a keynote in Sweden. Watch it here:


He says at one point "You got to get good at what you're doing, and you got to get credit for what you're doing." But they don't blog about it, don't talk about it on the internet. They don't establish a reputation. Then they don't have a fall-back position if they disagree with some stupid metrics that their managers want to impose on the testers.

There are lots of programmers out there blogging about their craft. Percentage-wise its probably a small number, but it seems like it's even fewer testers that do so.

James also says "I have a bunch of Google alerts. If you use certain code words in your blog I will read them. [...] If you say the name of the demon, he shall appear: and read your blog."

:-)

Hi James!

Saturday, October 2, 2010

My Response on TDD list on the "excessive cost" of unit testing


And also, and excessive focus on unit testing inhibits refactoring.

Not in my experience.

What if I split C into two or more classes?

So C was too large and now you're fixing it, splitting it into C and CPrime. Great! Good unit tests for C insure this refactoring doesn't break desired behaviors. If a test fails, it's doing its job, warning you that you did part of the refactoring wrong. Because unit tests are closer to code they test than integration tests, it will be easier to locate the problem and fix it.

Do I have to rework and split all its tests, so that they'll be one-class unit tests on the new classes?

No. If you do refactorings in small steps as described in Martin Fowler's book, the tests don't change (except for name and signature changes that I hope you are using a refactoring tool to do.) CPrime is indirectly tested by the tests for C.

AFTER I've split the class, and the tests are still passing, I may move some tests to directly test CPrime.

Further refactorings might remove some of the forwarding-functions from C that you would have created if you were doing the Extract Class/Move Method refactorings as per Fowler.

What if I decide to combine C and D into a single class. What will that do to my tests?

You change change all users of C and D appropriately, whether those users are tests or production code. Refactoring tools make that easy.

Avoiding the code smell "Duplicated Code" in both your test code and your production code insures that all the changes required for a Merge Class Refactoring are minimal.

And how will my tests help me determine if the new combined class is correct? If I have only unit tests for C and D, then really I won't have any way to determine if the new combined "CD" class is working correctly?

There are three activities in TDD: (1) writing a test (usually one that fails first), (2) writing code to pass a test, (3) refactoring to clean up code smells.

By separating #3 from #2, you are much less likely to have problems. You do need new skills: refactoring is not the same as "rewriting"; you can't let code smells go unfixed for very long; and you need to know how (and when) to use fakes and mocks to test how objects collaborate and also break dependencies to test in isolation.

Because tests in TDD _do_ allow collaboration between multiple objects, they might not fit your definition of a "unit test". That's one reason we at Industrial Logic call them "microtests". Each microtest only tests a single behavior, setting up the class under test with real or fake collaborators as needed.

You might find IL's courses athttp://elearning.industriallogic.com/gh helpful for learning Refactoring, Code Smells, TDD, etc.

Hope this helps,
C Keith Ray

Tuesday, September 7, 2010

What's the most important thing?

For Agile software development, what is the one thing that's so important that anything else is a distraction?

short answer: Working software

slightly longer answer: Being able to reliably build working software.

Thoughts?

Friday, April 23, 2010

Mind Heart Body 2

  1. Mind: More time creating value!
  2. Heart: No more debugging!
  3. Body: Test-drive new features.

Mind Heart Body 1

  1. Mind: More time creating value!
  2. Heart: No more debugging!
  3. Body: Write microtests whenever you find bugs.

Friday, April 16, 2010

Test-Driving C++

Reprinted from my blog in 2007.Jan.31 Wed

In most languages, when I'm using Test Driven Development to create a class, I only put into that class those methods or fields that I needed to pass a test. C++ has some exceptions to that, given how the compiler will generate aspects of a "canonical c++ class" for you.

I should explain the idea of a "Canonical C++" class. Imagine that I have this code:

class Buddy
{
public:
Icon* myIcon;
std::string myName;
};

Now, I didn't write a constructor, destructor, nor an assignment-operator, but the compiler did create those for me. It's as if I really wrote the following code:

class Buddy
{
public:
Icon* myIcon;
std::string myName;

Buddy() // default constructor
: myName() // invokes std::string's default constructor
{ // myIcon is not initialized, it probably has a garbage value here.
}

Buddy(const Buddy& other) // copy constructor
: myIcon( other.myIcon ) // copy the variable's value
, myName( other.myName ) // invokes std::string's copy constructor
{
}

~Buddy() // destructor
{
} // invokes std::string's destructor for myName.

Buddy& operator=(const Buddy& other)
{ // assignment operator
myIcon = other.myIcon; // copy the variable's value
myName = other.myName; // call std::string's assignment operator
}
};

A "canonical" C++ class has default constructor (and/or other constructors), copy constructor, destructor, and assignment-operator. These may be defined by the programmer or created by the compiler.

And this invisible compiler-generated code can be wrong, particularly if ownership of pointers or other resources is involved. Let's say that I test-drive a default constructor that sets up myIcon to point to a newly-created Icon object, and write the corresponding destructor code to delete the Icon object. It's hard to verify the "state" of an object after its destructor is called ('cuz it's GONE), but there are a few tricks to verify the behavior of a destructor that I won't get into here.

class Buddy
{
public:
Icon* myIcon;
std::string myName;

Buddy()
: myIcon( NULL )
, myName( "no name" )
{
myIcon = new Icon(Icon::DEFAULT_ICON);
}

~Buddy()
{
delete myIcon;
}
};

SPECIFY_(Context,BuddyHasDefaultNameAndIcon)
{
Buddy* aBud = new Buddy;
VALUE( aBud->myName ).SHOULD_EQUAL( "no name" );
VALUE( aBud->myIcon ).SHOULD_NOT_EQUAL( NULL );
delete aBud;
}

This test will pass. (by the way, I'm using "ckr_spec" here, a Behavior-Driven-Design framework I've written in C++ in my spare time. I'll publish more about ckr_spec one of these days.) However, this test doesn't exercise the compiler-created copy-constructor and assignment operators. AND THOSE ARE WRONG. Nothing (besides self-discipline) prevents anyone from writing the following (crashing) code:

void crashingCode()
{
Buddy keith;
Buddy keithClone(keith); // calls compiler-created copy constructor
Buddy anotherKeith;
anotherKeith = keith; // calls compiler-created assignment operator

// destructors are called invisibly here - crash deleting the same Icon
// object 3 times. (Also leaks an Icon object, too.)
}

The compiler-created copy constructors just copy the pointer to the Icon object. They don't create a NEW Icon object. So in "crashingCode" above, the Icon object created in the constructor of "keith" gets deleted three times, when the destructors for the "keith", "keithClone", and "anotherKeith" objects get called at the end of the function.

Therefore, when I'm test-driving a C++ class, very early on, I make a decision. Is this a "value" class that is going support the copy-constructor and assignment-operator, or an "entity" class that should never be copied because the instance represents something with a persistent identity? (These are some over-simplified ideas from Domain-Driven Design.) I can change my mind later, of course.

If my class is going to allow "value" semantics, then I'll need to write some tests to assure that the copy-constructor and assignment operator function correctly, whether I've written them, or the compiler has generated them.

If I'm not going to allow "value" semantics, then I need to signal to the compiler and to my fellow programmer not to generate or use the copy-constructor and assignment-operator. Declaring them private and unimplemented is how to do that.

class Buddy
{
public:
Icon* myIcon;
std::string myName;

Buddy()
: myIcon( NULL )
, myName( "no name" )
{
myIcon = new Icon(Icon::DEFAULT_ICON);
}

~Buddy()
{
delete myIcon;
}

private:
Buddy(const Buddy& other);
// don't implement copy constructor

Buddy& operator=(const Buddy& other);
// don't implement assignment operator
};
// the crashingCode example will not compile now.

For entity objects, quite often I don't want to allow the default constructor either, so I would declare that private and unimplemented as well.

The moral of the story is that in C++, sometimes you have to write code to prevent the compiler from writing the code for you. Just add that to your TDD/BDD development process.

Sunday, September 13, 2009

Lean in a Nutshell

(originally posted 2003 Dec. 31 on my old blog-site)

On the Lean Development mailing list, John Roth summarizes the heart of Lean: (my emphasis)

Lean is the opposite of fat, and the only basic principle of lean thinking is to eliminate waste. As the saying goes, all else is commentary, or at least an elaboration on that one basic thought: find a faster, better, cheaper way to add value to the product, and do it at all scales, from the total, end to end process on down to the individual practices used to construct it.

[...] In manufacturing, the first exercise is to squeeze as much in-process inventory out of the value chain as possible. Doing that will force a huge number of other beneficial changes.

In Lean Software Development, the first exercise is to go to fixed length iterations, where you have a production quality deployable piece of software at the end of each iteration. Doing that one thing will force a huge number of other changes in the process. I can't emphasize those two criteria too much: production quality and deployable. If you need further testing, signoffs or work from some other department after the iteration ends, you don't have it.[...]


Let's look at the seven core principles of lean, and compare them to Extreme Programming practices:

1. Eliminate Waste. Waste is anything that doesn't contribute to adding value to the product. One-hour status meetings waste time, so XP has 15-minute daily standup meetings instead. Excessive documentation and planning wastes time and energy, so XP recommends creating just enough documentation and planning, at appropriate levels of detail. Debugging is a waste, so XP does test-driven-development, which reduces debugging time and reduces time spent manually testing. Slow communication to/from the Customer/Domain Expert is a waste, so XP recommends having the Customer on-site.

2. Amplify Learning. Creating software is a 'learning' process. Typically developers do this learning alone, and that knowledge doesn't spread around to other developers very quickly. XP does coding in pairs to spread knowledge around faster. (Code reviews can also do this, but with higher overhead.) The Customer/Domain Expert is learning, too, as he sees his ideas implemented and developers ask him for details needed for acceptance tests. XP teams often do Retrospectives each iteration to bring learning to the whole team. Having all the developers and Customer working in a "war-room" also helps the whole team learn together.

3. Decide as Late as Possible. In Release Planning, stories are "promises for future communication" - the Customer/Domain Expert describes each feature/story in just enough detail for the engineers to provide a rough estimate. In Iteration Planning, the Customer describes the story in more detail, so the engineers can break it down into a task-list and/or rough design. The Customer/Tester specifies acceptance tests, which test the "what" not the "how", and then the programmers do test-driven-development where "what" tests and "how" coding are interleaved in a short cycle.

4. Deliver as Fast as Possible. "Simple Code" does everything necessary for the current requirements, but not more, so the team can ship it quickly. Keeping the quality high via test-driven-development avoids coding on an unstable foundation, wasting time debugging. Having the Customer/Domain Expert within range of spoken questions allows getting requirements into code in a minimum of time.

5. Empower the Team. XP encourages all team members to design, test, and code, instead of restricting those activities to a few. It encourages people to sign up for tasks, instead of assigning them to those who may not be as willing or as able. Anyone can improve code at any time, as long as the tests continue to run - allowing new learnings to be reflected in the code/design.

6. Build Integrity In. With short iterations where something isn't "done" until it passes (automated and manual) acceptance testing, and test-driven-development insuring that every line of code is tested, the XP team always builds on top of working software. In every iteration, all automated testing is repeated frequently, detecting as soon as possible any "breakage" of code. Any bugs found by manual testing or automated tests failing are not just fixed, but also force the creation of new automated tests.

7. See the Whole. Before beginning the first iteration, the XP team has a release plan. The Customer/Domain Expert has created that plan with the development team, and together they maintain and update the plan throughout the project, measuring and charting progress. Code is only implemented according to the current and previous requirements, so there is no work wasted on a requirement that may be dropped... however, engineers are not going to write code that will be difficult to adapt to that future requirement in that future comes around. To use the (not very good bridge-building analogy): the draw-bridge may not have motors in this iteration, but the builders are not going to build the bridge out of stone in this iteration, ignoring the need for mobility in the future.

By the way, I hate the "bridge-building" analogy, because writing software isn't building, it is designing. Running the compiler and linker associated "build" scripts, etc., are the "building". If building a bridge were as quick and easy as running a compiler and linker, we'd build bridges multiple times in order to test them. Any comparison of software development to bridges should be to designing bridges - an iterative, learning activity where multiple solutions are designed and tested (via mathematical/computer models or physical models) until all the requirements are met.

Wednesday, June 10, 2009

Measured Throughput - Where are your bottlenecks?

Agile Hiring: see Esther Derby's article on Hiring for a Collaborative Team.

Johanna Rothman writes about an organization where the number of requirements implemented are always significantly less than the number of requirements requested. They've been blaming the testers (!) and not seeing that their throughput through the whole system is limited. Given the numbers that she posts, I compute that the through-put is about 3.4 completed requirements per week for project 1, 3.7 per week for project 2, and 2.3 per week for project 3.

If that company accepted that they're only going to get about 3 requirements per week implemented, no matter how long the project is, they could avoid waste in the "requirements/design" phase by not specifying more requirements than their measured through-put allows.

They could also use that figure to move to incremental development as well, and maybe learn practices that would improve throughput as well. If they used XP or another appropriate set of agile practices, they could maximize information-flow and minimize delays between requirements, implementation, and testing.

To find the bottle-neck, we need to know who's waiting on whom in this organization. Is there a queue of finished-but-not-tested work piling up in front of the testers? Then it could be that the testers are the bottleneck. One way to deal with a bottleneck is to change to a "pull system". In this case, a programmer only does implementation when tester is about to be ready to test their work. That leaves the programmers idle, but then they could use that "idle" time to improve their development process (code reviews, unit testing, etc.)

In this system, the bottleneck is the programmers. The "pull system" can extend to the requirements: don't specify a requirement until a programmer about to be ready to implement it and a tester is about ready to test it. To move out of phasist approach, work on testing (creating automated tests) and implementation can overlap -- or (the XP way) the programming can actually follow the creation of tests and use the tests as an executable version of the specification.

(Reposted: original was posted 2004.Apr.05 Mon)

Wednesday, May 27, 2009

Test Driven Dialog

repost of my blog entry of 2003, April 08:

This is a dialog I created in July 2002, from messages on the Test Driven Development mailing list -- an email conversation between Robert Blum and Roger Lipscombe. After my editing, they didn't recognize their words, so perhaps I did a little more writing than editing. Any similarity to my favorite comics characters is purely coincidence.

Calvin: I don't do test driven programming. I write some code, and test it manually, walking through it in the debugger, checking that it works.

Hobbes: How do you know that it works? Could you express that as a test before you run your code?

Calvin: I can't think about the test until I've thought about the code

Hobbes: Changing the habit of writing code before the test can be a bother. Try this: Write the code on paper. Then write a test.

Calvin: Paper?!

Hobbes: What if you just outlined the code on a whiteboard and didn't write any real code? Could you still write a test first?

Calvin: I'd feel silly doing that alone.

Hobbes: What if you explained it to your pair partner? Could you still write a test first, or maybe have your pair partner write the test?

Calvin: That could work.

Hobbes: I notice you're refactoring without tests. What's that like?

Calvin: After I refactor, I have to test it again manually, in case I make a mistake doing the refactoring.

Hobbes: Wouldn't it be easier to run some quick automated tests before and after you refactor?

Calvin: Well duh! If I had the tests.

Hobbes: If you do test-driven development, your tests drive the design of your code, and as a side-benefit, you get a nice small set of automated tests that make refactoring safe.

Calvin: Perhaps if I start by thinking about the interfaces that my class or module would need to support, I would be able to write the unit test first? Does this sound like the kind of thing I should be trying more of?

Hobbes: I think so. If I were given just the task of writing a class or module, my first test would describe a very simple case of what I wanted from that code. The interface of this not-yet-written code is evolving, of course, during my test-driven development process. Since I'm calling the functions that I need at each moment, without a care how (or if) they are implemented, it tends to create a pretty useable interface.

Calvin: So you find out quickly if your interface functions are no good when you're forced to use them as client, in the tests, before investing a lot of work at writing them!

Hobbes: Now you're getting it.

Calvin: Tell me, when you think of a feature, is your next thought about testing or implementation?

Hobbes: I just think about using that feature. Which leads me to an example of how I would use it. Now I just write down the results I expect from that, and voila, there's the test. I'm not even actively thinking 'I have to write a test now. How can I test it?'. My main focus is how I can use it.

J. B. Rainsberger added a few more lines:

Hobbes: Have you written tests for that code yet?

Calvin: Tests?! TESTS?! I don't need tests! What do I need tests for? Don't you think I can write something this simple without using tests as a crutch?! I know it's right!

Hobbes: (rolling his eyes) Of course you do.

Calvin: Right. Now help me fix these last four bugs.

And Laurent Bossavit capped it off:

Hobbes: I thought I was doing that.