Summary
Debate sprang up at JAOO '07 around Bob Martin's assertion that "nowadays it is irresponsible for a developer to ship a line of code he has not executed in a unit test." In this InfoQ video, he debated with Jim Coplien on this and other topics, including Design by Contract vs. TDD and how much up-front architecture is needed to keep a system consistent with the business domain model.
Bio Bob Martin is an Agile Manifesto author, and author of books on Agile Programming, XP, UML, O-O Programming, and C++. He is CEO and president of Object Mentor www.objectmentor.com/ Jim Coplien is a software pioneer in o-o programming and C++ and multi-paradigm design. He appreciates the human side of design, and has written critically acclaimed books on design and development.
Introduction by Floyd Marinescu
I am here with Jim Coplien and Bob Martin at the JAOO Conference and here we have 2 very interesting divergent opinions on what is the value of TDD. So let's open up the floor and let each one have a couple of minutes of say. Let's hear you guys talk about it!
Bob Martin: First thing I need to say is: I am sitting here next to one of my heroes. I read Jim's book in 1991-1992, changed the way I thought about software, changed the way I thought about C++ in particular, so it's a great honor for me to be here. I think we have a disagreement - I'm not sure, possibly, it may a difference in perspective - but my thesis is that it has become infeasible, in light of what's happened over the last 6 years, for a software developer to consider himself "professional" if he does not practice test driven development.
Bob Introduces his 3 laws of TDD and Jim Responds
Alright. But you do not accept the thesis that the practice of test driven development is a pure requisite to professional behavior in 2007.
I absolutely do not accept that.
OK. So we can come back to that one because I think that is an interesting topic, just on the topic of professionalism, but before we do that: there has been a feeling in the Agile community since about '99 that architecture is irrelevant, we don't need to do architecture, all we need to do is write a lots of tests and do lots of stories and do quick iterations and the code will assemble itself magically, and this has always been horse shit. I even think most of the original Agile proponents would agree that was a silliness. I think if you went and talked to Kent now he would be talking about what he always talked about: metaphor, whatever the heck that was.
Sure. OK. But now let me come back and throw a different light on this. I think architecture is very important, I've written lots of articles and books about architecture, I am the big architecture freak. On the other hand I don't believe architecture is formed out of whole cloth. I believe that you assemble it one bit at a time, by using good design skills, by using good architectural skills, over the weeks and months of many iterations. And I think that some of the architectural elements that you create, you will destroy; you will experiment in a few iterations with different forms of architecture. Within 2 or 3 iterations you will have settled into the architecture you think is right and then be entering into a phase of tuning. So my view of that is that the architecture evolves, it is informed by code that executes, and it is informed by the tests that you write.
So I would say that a little differently, and take exception to some of it. I would not very likely fill in the interfaces with abstract member functions or defunct member functions. I might create objects that will fill the place of interfaces. So, in Java terms, I might have an "interface-something" with nothing in it, but I am not going to load it with a lot of methods that I think might be implemented one day. That is something that I am going to let my tests drive, the requirements drive, and I am going to be watching it like a hawk to see if there is any kind of architectural friction that would cause me to split that interface.
You want to have something there to give the object meaning.
OK. And I would even agree with that. You need something there to give the object meaning. I am going to be really minimal about that.
Me too. No disagreement.
And then I am going to let executing code inform my future decisions, so I am not going to create a massive architecture, or a huge system based on speculation.
That is right. No disagreement.
So back to the beginning: how long would you spend before you started writing executable code. Let's say, on a system that will eventually wind up being two million lines of code?
So, 2 million is, in my experience, pretty small. I am working with hundreds of millions. Before the first executing code... it depends a lot on the individual system, but let's say I were building a simple telecom system. What I would probably do, let's say I am doing it in C++, I would have at least constructors and destructors in place and be able to start to wire up important relationships between the objects and...
Would you have tests, testing those wirings?
I would have tests for those wirings. An obvious test is to make sure, when the system comes up and goes down, that the memory is clean, for example. Half an hour.
Excellent. So where is our disagreement? Perhaps our disagreement is on the notion of TDD and professionalism. That was the second part.
They practice TDD? (smiling)
Yeah, I know, and I am going to push on that one, because I think that something our industry has lacked is a standard of professionalism.
We'll take your definition as a starting point and then we can talk about that.
But that is not actually my definition, I was joking. I think that nowadays it is irresponsible for a developer to ship a line of code that he has not executed in a unit test, and one of the best ways to make sure that you have not shipped a line of code that you have not tested is to practice TDD.
Now, Bertrand Meyer actually taken this further and he has something called CDD, which is Contract Driven Development, where what he does is: he takes contracts and he kind of feeds random numbers at them and if they don't meet the preconditions you don't run them because you know that test will fail, but he tests if the post conditions hold after you run the test, and if they don't it's a bug. And they have actually done this. They have a tool that automatically runs tests. They have done this on the Eiffel library and they ran it about a week, they found 7 bugs in the 20 year old Eiffel library - that is kind of interesting. But it comes from a part of the code where you are expressing intentionality in a way that has hope of being traced back to something of business importance, and the problem about TDD, as most people practice it down at the class level, is that it's really, really difficult to trace those APIs at a class level sometimes all the way up to business significance.
So I am having trouble with that. As I remember Eiffel - and I actually thought this discussion was put to bed a long time ago - as I remember Eiffel and "design by contract," you specify preconditions, post conditions and invariants around every method and around your class, the invariants of your class. Test driven development, or a suite of unit tests, virtually does the same thing, it specifies a set of incoming checks on the arguments, outgoing checks on the returned values, explores the state space, as you said, of the methods. So I always thought that they were one-to-one, you could always transform contracts into unit test or transformunit tests into contracts, with the exception that the direction of the dependencies is different, and you know that I am a big dependency freak. Unit tests depend on code, on production code, which I think is good, production code doesn't depend on unit tests; whereas contracts are smeared through the code, which bothers me.
I think you are creating a dualism that needn't be created, in that there is one thing, which is the code, the code is the design, it's what's delivered, anything else is not Lean. In typical projects that use unit testing, the code mass is about the same as a test mass, and where there is code, there's bugs. You cut your velocity in half. There is well known examples, the most famous example is the ADA compiler where actually, use of test driven development increased the number of bugs in the code, because your code mass increases, because you have more tests. If you are using assertions you have this nice coupling, that is essential coupling, between the semantics of the interface and the code itself. Whereas with the tests the coupling is a lot messier and hard to manage. There was another point you've made I was going to react to...
I am surprised that you think the code mass is different.
J: In my experience, it is. If I look at how people actually use this: I like it when I see a JUnit spec that looks like assertions, but a lot of the time it isn't.
I agree with that: there are messy tests, but there is messy code. I don't like arguments that the "tool is easy to abuse therefore you shouldn't use it," that would invalidate almost everything...
That isn't my argument. My argument is: how I am seeing this being used in broad practice - and they are not getting it.
Well, OK. And do you see contracts being abused in broad practice?
First of all, they are not being used enough...
Right! By the way, since we've just got a couple of minutes left, just a trivia question - and I don't know the answer. Who is it that first used "DD" with some letter in front of it? We've got CDD now, we've got BDD, TDD and I don't know what else and the earliest one I can remember is Rebecca Wirfs-Brock, Responsibility Driven Design. Was there an earlier one?
DD ... was a UNIX command to do disk dump... but that probably doesn't count. Thank you Bob, good seeing you again.
|