I like Conway's game of life for this.
I like Conway's game of life for this.
On May 9, 2011 1:21 PM, "Nieve" <niev...@gmail.com> wrote:
--
You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
To post to this group, send email to software_cr...@googlegroups.com.
To unsubscribe from this group, send email to software_craftsma...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/software_craftsmanship?hl=en.
--
Matteo,
Thanks a lot for you thoughts!
Funny that you say that this is something you can add as a restriction to your kata- that's something I've been thinking of lately.
The way I'm starting to see this lately is that in order to respect SRP you *would* want to extract certain responsibilities from your kata object into other object that will collaborate with the object in question, so that your object would have optimally only one reason to change; I do believe the kata, as well as my TDD usage will demand me to do these extractions in the 'refactor' of my red, green, refactor, since green would be just writing the code the way it will just work. Or am I missing something here?
Regards,
n
> I expect JB Reinsberger would point out that if you *really* do the
> "refactor to remove duplication" step, you will end up separating
> responsibilities. So yes, in fact you are right; when I'm facilitating a
> dojo I often have to point out that until you remove all duplication you
> should not write the next test.
> The fact is that most TDD beginners, including at times me, don't see how
> important it is to remove every tiny bit of duplication, even when it seems
> you're overdoing it! So the reason for the additional rules is to push
> learners to strive harder. For instance, after doing the OCP Dojo for a
> while it's become more natural for me to extract functionality to new
> methods and then to new classes.
+ SomeLargeConstant;
I am continually surprised at how much improvement in my code comes
from a constant focus on removing duplication. I'm sure there are
more sophisticated checks and refactorings to be aware of, but even
those, to me, are more and more driven by simply observing and
removing duplication.
Thanks!
Ron Jeffries
www.XProgramming.com
It is better to attempt something great and fail that attempt,
than to attempt to do nothing and succeed.
--Cookie, Garden Court Chinese Restaurant, Hamburg, MI
Steve
I TDD because I can not yet reliably predict the perfect design for
the problem I'm solving. Given this handicap, I've found TDD to be the
best coping mechanism. When I can reliably and perfectly predict the
optimal design, I'll probably abandon true TDD as well.
Also, given there is no shortcut for experience, I find TDD to be the
best way for novices and advanced beginners to learn about design and
what makes a design good. That moment when you get a new requirement
and you see the code in your brain and realize all you need to do is
change this one line and the new requirement will fit right in is the
epiphany you need to get from Advanced Beginner to Proficient, IMHO at
least :)
I also get the design wrong a lot because I don't really know what's
coming next, so honing my refactoring skills seems appropriate and
necessary.
--
Curtis Cooley
curtis...@gmail.com
blog:http://ponderingobjectorienteddesign.blogspot.com
===============
Leadership is a potent combination of strategy and character. But if
you must be without one, be without the strategy.
-- H. Norman Schwarzkopf
>>I find TDD overrated because it puts refactoring on a pedestal: red -green - refactor.
--
You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
To post to this group, send email to software_cr...@googlegroups.com.
To unsubscribe from this group, send email to software_craftsma...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/software_craftsmanship?hl=en.
>>I find TDD overrated because it puts refactoring on a pedestal: red -green - refactor.I wouldn't say that I find TDD overrated, but I agree, it is good to step back and think about the problem a bit more before starting to hack away...< personal experience >I did the "alpha-end converter" kata in TDD manner. Description of the coding problem here http://www.thycotic.com/codetest.txtDuring doing this Kata I got into this "brute force coding" mode, where I didn't think too much, but I just wanted to get the test to green. It took me a while to get over this hurdle and I couldn't solve the kata in that 1 hour session...This annoyed me a lot, so I stepped back, thought about the problem on paper for a couple of minutes and had the solution there...</ personal experience >
--
You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
To post to this group, send email to software_cr...@googlegroups.com.
To unsubscribe from this group, send email to software_craftsma...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/software_craftsmanship?hl=en.
> I find that this is a problem with kata that drive towards an algorithm, wrt
> katas that push me to write a maintainable solution. I don't think that
> getting folks to recognize that we're asked to convert from base-10 to
> base-13 is particularly interesting. It gets more interesting if you start
> asking yourself: can I change base? Can I change the symbols? Can I also
> support Roman numerals? Hours and angles (base-60 but with more than one
> symbol per "digit")? Babylonian? Can I support different number systems
> in the same program? Without introducing IFs?
A kata is not a free-form fight. It is an exercise to practice one
small set of things. Some of Uncle Bob's adhere more closely to this
notion than others that are out there. I prefer to solve a small
problem in a new way, rather than repeat a solution the same way
again and again.
Strictly speaking, that sort of thing, and the ones Matteo mentions
above, are not katas, despite how enjoyable and helpful they can be.
They are more of a free-form dance with the devil in the pale
moonlight. Or something.
Ron Jeffries
www.XProgramming.com
Make it real or else forget about it -- Carlos Santana
Since I only had one real implementation, I got it mostly wrong. Not
only do I now have to rebuild the framework, I wasted a week building
it in the first place. In short, I was lazy and jumped to the
framework without at least two example implementations.
Had I been on my game, I would have hacked the feature into the new
application then refactored out the commonalities into a framework
that can be reused in the next application that requires this feature.
I tried design up front and got it terribly wrong. I wasted my time
and the company's money. No model of the two implementations would
have helped me see the common pieces, because I would have gotten the
model wrong. Only the code would have shown me the common pieces and
what could be refactored out.
You say that you don't have to get the model right the first time, so
what do you call it when you get the model a little wrong and then
have to improve the design of the existing code?
Additionally, I do not wander blindly into TDD without doing some
analysis of the problem and design of the solution. Which is why
kata's are not a great barometer of TDD's effectiveness given how a
kata is usually algorithm discovery which usually can be done on a
whiteboard.
TDD is not a silver bullet to perfect systems, but it's the best way
I've found so far to build highly maintainable, loosely couple, highly
cohesive systems. I believe that it's much more difficult to build a
tightly coupled, low cohesion system while doing TDD. As you start to
depend on more and more objects, TDD gets harder. As your class starts
to do more and more things, TDD gets harder. It has built in viscosity
that works in your favor.
After 25 years of doing this sort of thing, you have an intuition into
design that others do not. When I stare at and empty flip chart and
start to feel anxious about what to write, I can often relieve my
anxiety by writing the simplest test that fails and going from there.
> --
> You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
> To post to this group, send email to software_cr...@googlegroups.com.
> To unsubscribe from this group, send email to software_craftsma...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/software_craftsmanship?hl=en.
>
>
--
> I call it "change" or "refactoring" if you will. But it�s refactoring
> of a model first.
> So, yes, refactoring is needed as you improve your understanding of a
> problem or extend a solution.
> But this mostly happens within the model, not within the code.
I'm going to offer here a different way of looking at the model /
code question than seems to be in your text.
What seems to be missing in the above is that the code /is/ a model.
In fact it is the /only/ model that completely represents the code.
To be useful, a model, including the code, has to represent
something (I'll just say "something") that we want to think about.
Sometimes a drawing or some other form is easier, for some people,
to think about.
> With the sample I�ve given (alpha-end kata) this might not be obvious.
> I translated the model to methods.
> But that was just for simplicity�s sake and because it was an
> algorithmic problem.
I don't understand what you're saying here. It sounds to me as if
you are saying the model is real and the methods are not. The
opposite is the case. The program is what we want, the model is a
means to the program.
Can it be easier, for some people, at some times, to think in terms
of another model? Yes. Does that make it best for them or for
everyone? That's not so clear.
> Still, though, you sure will agree the methods are very small. So if
> changes are needed, not much refactoring can even take place. What do
> you want to extract from a 3 line method to go into another class?
If the program wants the other class,and we pay attention to it, it
will tell us so. If we have an idea in our head, we can put it into
the code, or extract it from the code, as seems best. If we do not
have an idea in our head, it doesn't matter what form of modeling we
use.
It is possible that thinking in some other "format" will help some
people, sometimes, get a new and better idea.
> Again: I�m not saying refactoring is not needed. I just claim (code)
> refactoring is the wrong tool to arrive at a design. Don�t use code
> refactoring to plan (!) software.
I think you're saying that for you, sometimes, you do not reach a
good design using code and refactoring. I can understand that. I can
identify with it. And yet your conclusions, "wrong" and "don't",
seem to me not to follow.
> This is not BDUF I�m talking about. No, I�m thinking very short
> iterations each starting with a "thinking phase" aka design.
> Model a little, code a little...
No one is opposed to thinking, as far as I know. Personally, I would
not think it ideal to think sometimes and then code sometimes, as
the words above suggest. It seems to me to be better to be thinking
about design all the time.
The code /is/ a model. The code is /the/ model. I find many ways of
thinking to be useful, cards, pictures, text. Of them all, I find
the code to be particularly useful, in the sense that it represents
what actually is.
I understand that it can be difficult to look at what is and think
about what ought to be. I would suggest that that's not some
inherent difference between "code" and "model", because there is no
inherent difference between "code" and "model".
What you have discovered, I suggest, is something about your own
mental skills, abilities, preferences, not something about the
universe. That's the good news. We can do something about ourselves,
adjusting and changing our skills. The universe seems not so ready
to accommodate.
For me, I find that more and more I can work in the code and get
from one design to another, often quite different (and often much
better). And I can think about the code / design / model in other
ways with my fingers still on the keyboard. That's useful, because
it tends to keep my program always working as its design improves.
And sometimes, I still like to take a few minutes and draw a
picture. I think that's about me, not about the reality of pictures
and code.
Ron Jeffries
www.XProgramming.com
Know what I pray for? The strength to change what I can, the inability to
accept what I can't and the incapacity to tell the difference. --Calvin and Hobbes
> But that to me also means: coding is not (!) designing - with regard
> to source code. Coding is translating a design for source code into
> source code.
Honestly, even if all you can do when writing code is translate an
existing design, that does not imply that is all that anyone can do.
Naturally the design we move to has to be in our head ... at least
somewhat. But for god's sake sit down with any good refactoring
person and watch what they do. They can evolve the design all over
without turning to some other form of "design".
Ron Jeffries
www.XProgramming.com
Ron gave me a good suggestion once. -- Carlton (banshee858)
> Hm... "no one is opposed to thinking" is not the reality in the shops
> I visit for consulting or when I participate in a coding dojo.
> Maybe there is no opposition to thinking before coding - but this does
> not show in the process of software development.
You are supposed to learn to think while you code. It seems that may
be the element that has eluded you so far.
> On a regular basis teams have a very hard time to not talk in terms of
> implementation details about a solution.
I don't have that difficulty. And I can talk about design by showing
code or by other means. So I know it is possible. You speak as if it
isn't. But it's not impossible if people can do it.
> They mostly lack shared concepts, methodology, and terminology to do
> other than code (and maybe draw a class diagram).
They, who? Some people you know? I'm not sure who "they" is in this
sentence.
Ron Jeffries
www.XProgramming.com
Knowledge must come through action;
you can have no test which is not fanciful,
save by trial. -- Sophocles
I use one in my courses that I haven't written up yet. Maybe I should.
Removing duplication leads to a structure emerging, then improving
names leads to moving responsibilities to more sensible places.
--
J. B. (Joe) Rainsberger :: http://www.jbrains.ca ::
http://blog.thecodewhisperer.com
Diaspar Software Services :: http://www.diasparsoftware.com
Author, JUnit Recipes
2005 Gordon Pask Award for contribution to Agile practice :: Agile
2010: Learn. Practice. Explore.
> I expect JB Reinsberger would point out that if you *really* do the
> "refactor to remove duplication" step, you will end up separating
> responsibilities. So yes, in fact you are right; when I'm facilitating a
> dojo I often have to point out that until you remove all duplication you
> should not write the next test.
In problems that want MVC-style solutions, I find that removing
duplication creates the classes to which reponsibilities will
eventually move, but improving names helps identify which
responsibilities should move where.
I don't find this pattern to match the definition of "multitasking".
0. Write out a list of things you might do.
1. Decide what to do next.
2. Do it. If, in the process, something jumps into your mind that
doesn't help with the current task, then write it down.
3. Go to 1.
I believe this is "Getting Things Done". It's also TDD.
> Hm... "no one is opposed to thinking" is not the reality in the shops
> I visit for consulting or when I participate in a coding dojo.
> Maybe there is no opposition to thinking before coding - but this does
> not show in the process of software development.
> On a regular basis teams have a very hard time to not talk in terms of
> implementation details about a solution.
> They mostly lack shared concepts, methodology, and terminology to do
> other than code (and maybe draw a class diagram).
This sounds really frustrating, Ralf, and I understand why it would
get you down. I recently spent time with a group of well-meaning
programmers whose code made it look like it was written in 2000, just
judging by their use of the language. In that situation, TDD seems
years away.
When I started practising test-first programming -- even before my
tests really drove my design -- I spent about 1 year before I felt
that I really began thinking of code in terms of input/output rather
than algorithms and implementation details. I spent that 1 year
practising an average of 4 hours/day. That means 1,000 hours of
practice. Do you think the teams with whom you consult consist of
people who have practised 1,000 hours yet?
It's not easy, it takes time, and we must have patience if we want to
see people improve.
> Yes, refactoring is an activity which does not add customer value by
> itself.
> It's definition says, you must not change functionality while
> refactoring.
>
> Thus refactoring should be avoided - unless it is necessary to make
> adding functionality aka customer value easier.
This sounds to me like saying "you should avoid exercise, unless it is
necessary for you to keep your metabolism efficient enough to burn
excess fat and avoid heart disease". While strictly true, the condition
is so close to universally true that stating it explicitly in most
contexts obscures the truth, rather than helps to reveal it.
I prefer to say "the point of exercise is to increase one's metabolic
efficiency in order to burn excess fat and avoid heart disease", which
is the same statement made in a way that doesn't risk misleading the reader.
Similarly, the point of refactoring is to simplify the design to reduce
the marginal cost of features and to stop the cost of delivering a given
feature from rising quickly over time.
Refactoring /done well and for a sensible purpose/ adds customer value
that most customers with whom I work have difficulty seeing and
therefore that I have learned to articulate clearly.
I don't know many things that, /not/ done well nor for a sensible
purpose, help a business realise more profit, so don't do any of those
things.
--
J. B. Rainsberger :: http://www.jbrains.ca ::
http://www.thecodewhisperer.com
>> Naturally the design we move to has to be in our head ... at least
>> somewhat. But for god's sake sit down with any good refactoring
>> person and watch what they do. They can evolve the design all over
>> without turning to some other form of "design".
>
> Sure, very good developers might be able to do that.
Yes. I would argue that the ability to do that is one of the marks
of a good developer. Why you are arguing against being a good
developer concerns me.
You seem to have conveniently forgotten that in my notes on this
thread I have said repeatedly that other ways of thinking about the
design are just fine, and that the ability to think about the design
while coding has particular value.
You have then argued that thinking about the design while coding is
impossible -- but it is not.
You have then argued that all multi-tasking is bad -- but it is not
-- and that thinking about design while coding is bad -- but it is
not.
You are now arguing that the ability to think about the design while
coding is something that only a genius or an autistic can do -- but
it is not.
The ability to think about design while coding is a valuable skill
which is readily improved by practice doing it. If one were to
trouble oneself to watch people doing it, one would see that they
interweave design considerations and code in quite a fine-grained
fashion, so that the result is that the design they have does not
deviate from one they want by very far at any time, while
maintaining the enviable situation that they always have a program
that actually, testably works.
Your ideal seems to be a good design which is "evolvable". I take
that to mean, a good design that supports the next things one plans
to do: a design which is always ready to receive the changes we want
to make to the program (and therefore the design).
History has placed before you a new way to do that, called TDD,
embodying producing tests that keep the program working and growing
in functionality, with refactoring, techniques that evolve the
program's design smoothly whenever we discover, by any means, that
it needs improving.
TDD plus refactoring exists. Normal people can do it. It requires
deep skill, as does good programming and design no matter how we do
it. Unlike up-front work, which is always insufficient, TDD plus
refactoring allows us to apply what we learn, when we learn it.
The alternatives to TDD are dark and dire. We can
- try to think of everything important up front.
this is doomed to failure, and even if it were not, it delays
the delivery of any value until we have thought of everything.
- discover design ideas later and put them in the next program.
this is not helpful. we need for our design ideas to go into the
current program, not some future one.
- discover design ideas later and put them in without tests and
without refactoring.
this is very difficult and risky. we tend to break programs that
are not well supported by tests, and we tend to break designs
when we do not use function-preserving transformations.
TDD/Refactoring just happens to be better than all the known
alternatives. It allows us to produce value quickly and safely, and
to make sure that all our best design ideas are in the code.
Now then. When we set out to learn to do this valuable thing,
//one// good approach is to work almost entirely with the code in
front of us. (And, surely, to work together with someone else to
talk with.) Doing this is difficult. We feel the need to take a
break, to draw a picture, to do some CRC cards, for god's sake just
to get a chance to THINK!
Of course we feel those things. We are pushing ourselves to learn,
just as we might push ourselves to lift that weight one more time,
to ride that bike just a bit further.
We do that "in the lab", in trying to learn this powerful new
technique. Our pair helps us do this, because we learn to speak
about our design (yet another way of representing the idea), and we
learn to let them drive while we pay attention but think as well.
Even in a TDD/Refactoring exercise, of course, we might well draw a
picture or do some other form of design, or we might just rest.
That's fine. If you ever work in a code-fest where you build the
same program, again and again, with different pairs, you'll find
that ideas from the last time around will pervade the next time.
That's fine, too. Part of the learning is that an idea we had an
hour ago can go into the code now. Part of the learning should be
that the code we get is not all that different from the code we had
at the moment we had the idea ... and therefore we could have moved
the code to support that idea then ... without the rewrite.
All this is learning. It strengthens our ability to support code
with tests, and to evolve it smoothly without disruptive rewrites.
These are valuable things to be able to do.
> But then, there exist people who can calculate the square root of
> numbers with 10 digits in their head.
Yes. And there are perfectly normal people who can do arithmetic
more rapidly and accurately than we can, using techniques that are
easy to learn and apply with practice.
> Or there are painters who can do whole wall paintings without
> sketching first.
Yes. And there are perfectly normal people who can visualize enough
of a wall painting in their head to get started, and who can work on
details in such a way as to produce such a thing without either
being a genius or otherwise mentally bizarre. They have one thing
that we do not have ... hours and hours of practice.
> You might attribute that to the culture and education of the .Net
> community; poor slobs having to work with Microsoft trash. Or you
> might say, that's the deplorable state of the are in far away Germany.
> But to be honest, on my trips to England or the US doing conference
> talks I haven't found that much of a difference between developer
> communities/cultures.
I attribute it, primarily, to people not doing the work, instead
denying the value of that skill, or its ability to be grasped by
ordinary people. No doubt at some past time, the ability to add a
few numbers together seemed like magic. It wasn't. It was a matter
of attaining some elementary skills, and practice.
The thing we call "model" is not a UML diagram (a standard which, by
your accounting, most people do not use, suggesting, perhaps that it
isn't all that good but I digress ...
A model is not a diagram in some particular form. A model is an idea
in our head, which we get there, and manipulate there, using some
physical means such as UML, hand-waving, CRC, or any other means.
One way -- just one way -- of modeling our ideas is in code. This
particular way has the property that it is the only model that we
/must/ produce if we are going to get a program working on the
computer.
The better able we are to produce that particular unique model
incrementally, the faster we can deliver value. But if we do produce
it incrementally, we will surely make design discoveries //no matter
how much up front work we do//. Therefore there is value to
producing that code so that it is strongly supported by tests, and
there is value to knowing how to smoothly improve the design of code
so that we can put in our new design ideas safely at any time.
We can still get design ideas any way we want to, with diagrams, arm
flapping or through the creative use of song. We have to be able to
put them into our programs or those ideas will not be of value.
The best known way to produce code that supports our new design
ideas is TDD/Refactoring, and the best way to get code like that is
to practice TDD/Refactoring until we are good at it.
> But I'm happy for you that you're working in environments where such
> capabilities are common.
I do not work in "environments" where such capabilities are
"common". I work with people who value the ability to program
better, more effectively, more smoothly, and with increasing skill.
Anyone can do those things ... unless they do not try.
If we dismiss the observations of people who have been down the
journey before us, we're going to get lost in all the same dead
ends. That can be a lot of fun ... but we might be able to do
better.
Ron Jeffries
www.XProgramming.com
No one expects the Spanish Inquisition ...
> I'm all in favor of focus. That's why I'm saying: focus on getting the
> code right while coding.
> Slinging code by itself is difficult enough. So you should start doing
> it with the addition burden of finding evolvable structures on your
> shoulders.
Should /not/ start, I imagine you meant to say.
Those of us who do TDD/Refactoring reasonably well, and we are many,
don't think of ourselves as "slinging" code. This may be important.
We write a test showing what we intend to accomplish, and a few
minutes later, we have accomplished it. If doing that has made the
design not quite as nice as it was before we started, we evolve the
design in place to keep it good.
The more we practice this, the easier it gets and the better we get
at it.
That said, there will always be times when our design ideas come too
late for immediate inclusion but they are still desirable to put
into the program. This is the original meaning of "technical debt",
the deviation of the code now from what we understand of the
desirable design now.
It turns out that our tests, and our refactoring skill, enable us to
bring the program and our understanding back in line more readily.
That's a good thing, and we get it by building our skill in TDD and
refactoring.
> Sure, whatever structures are below the radar of prio modelling need
> to be defined while coding.
> But since code is comparatively hard to change you want the explicit
> modelling radar to cover as much ground as possible.
That is one thing to want. Another thing to want is to write code
that is not so hard to change. Another thing to want is to build the
skill to notice design issues that are above the radar. And another
is to build the skill to do something about it in the moment.
> I some guru programmer can do without, that's fine. But as you cited
> even Kent Beck found it difficult to arrive at a solution just doing
> TDD.
Perhaps the most distressing thing about programming is that "guru
programmers" are not some kind of mutant that was born programming.
Guru programmers, as Joe pointed out earlier, have spend thousands
upon thousands of hours learning how to be good programmers.
Some of that learning was done with katas and other exercises. Much
of it was done "on the job", paying attention to what happens,
trying new things at small enough scale to learn without doing harm,
and so on.
There are thousands of programmers with good skill at evolving the
design. That facility is available to programmers who are beginning
at any level and the results seem primarily to be a function of how
much they practice.
> Structure code in an evolvable way as much as possible on a
> whiteboard. Postpone coding. Make coding a no brainer.
If coding were a no brainer, penguins could do it. It isn't a no
brainer and shouldn't be. Sure, draw on a whiteboard, it's fun and
useful. But bring your brain to the coding keyboard, please, and
learn all the good ways to use it.
Earlier you said that coding was hard enough. I think that's more
likely than that it will ever be a no brainer. So, again, learn to
apply your brain to more aspects of programming. It can only help.
> Design work done on a whiteboard is not only easier (bubbles dont
> crash, drawings are easier to change than code), it also allows more
> team members to participate. That increases the likelihood of good
> design. That increases the understanding of structures by more people.
Yes, it does all those things. And it is incomplete, imperfect, and
unlikely ever to be right "up front" ... because we never, ever,
know up front everything that is going to happen and to be asked
for.
We can //and should// turn back to the whiteboard when, later on, we
need to think about the design in ways we can't encompass in code.
We will always need to do that, I'd guess.
And having done so, we need to bring that new understanding back to
the code. We can't do that with no brain. We can't just "sling" code
to do that. We need discipline and skill to put our new
understanding into existing code.
The best known discipline and skill for doing that, so far, is
called TDD and refactoring. Programmers who know how to do that will
prosper.
> Again: coding is no virtue, but a necessary evil in order to satisfy
> customers with software. I guess we can agree on that, because we're
> constantly seeking relieve from coding thru compilers, DSL, or some
> other form of code generators.
> So why make coding more difficult by mixing it with "structure
> finding"?
> To me there are two reasons:
> 1. addiction to code: writing code is so satisfying personally, that
> developers seek out ways to keep doing it (on a level matching the
> current culture; today that's 3GL, but formerly they were happy with
> assembler and opposed higher level languages).
> 2. lack of an idea of how otherwise evolvable structures could be
> found before coding. Take a pol at your average developer conference:
> how many of the attendees are using UML? how many of those are using
> more than class diagrams and sequenz diagrams?
> I've done this poll several times in my talks at conferences in
> Europe. The results are roughly: 15% doing UML, 5% using more than
> those two diagrams.
> Naturally TDD, an all code approach, must appeal to the 85% of
> developers not using the #1 world wide standard for designing
> software.
So this #1 world wide standard that is used by only 15% of the
people who might use it. Is that telling us something about the
people, or about the standard? As you suggest below, it is likely
something about the standard.
The purpose of a model is to get ideas into our brain. It might just
barely be possible that there are ways other than UML to do that.
> But does that really show the validity of TDD as a successor to
> explicit design? Or is it a sign of failure of UML?
> I indulge in thinking the latter - an am on a quest for easier
> modelling tools, modelling tools for the masses, so to speak.
That's good. I'd suggest that you consider CRC as you think about
this, and, really, I think that a few non-formal UML-like diagrams
can be incredibly useful.
And don't forget human, shared, spoken language. Good naming
conventions and a few diagrams can go a long way toward getting
everyone an improved understanding of the design.
> Because,
> as said above, code is hard to change, and refactoring does not add
> value, and "structure finding" and "code instruction finding" are so
> different, they should not be mixed.
Some code is harder to change than other code. It seems prudent to
learn to write the easier to change kind. TDD and refactoring help
us to do that. (Refactoring does add value: I'll come back to that.)
It turns out that what makes code hard to change is that people do
not learn to do "structure finding" at the same time as typing in
the code (apparently even when it is a "no brainer"). This is not
some amazing new skill that only a genius can attain. It is a simple
matter of pausing every few minutes, looking at what we have built,
and taking a few minutes to improve it. It's not a big deal: it does
require practice.
Now then: As it happens, refactoring does add value. It does not add
features. It does, however, result in a program that is easier to
add features to.
Adding features without refactoring adds some number of features
over time. Adding features plus doing refactoring can add /more/
features over the same period of time. That is more value.
Now of course, we could refactor forever and never add another
feature. I've not seen many teams fall into that trap, because the
business people generally won't let them. Nonetheless it is
possible.
It seems to happen, most commonly, when the team's understanding of
a good design has gone forward and the real design has stayed bad,
so there is much work to do to get to the better place. Whether they
call it a rewrite or a 40day refactoring, the result is the same,
angry customers.
It's better to keep up on the design, and its better, when new ideas
and new needs come along, to know how to do refactoring. And when
refactoring, good tests are of immense value.
Hmm, tests and refactoring. Here we are again. These are skills that
normal people can build, and they are incredibly valuable.
You seem to care deeply about programming skill. I suggest that you
start thinking of TDD and refactoring in terms of a skill that is
potentially good to have, and that you learn them well enough to
understand even better than you do now, when to apply them and when
not.
Ron Jeffries
www.XProgramming.com
There is no award for "being XP". There is an award for doing the
right combination of practices: success.
> *** Two cases when you shouldn't try XP ***
> 1. The biggest barrier to the success of an XP project is business
> culture. Any business that runs projects by trying to point the car in
> the right direction is going to have a rough time with a team that
> insists on steering.
> 2. A variant of "pointing the car" is the big specification. If a
> customer or manager insists on a complete specification or analysis or
> design before they begin the small matter of programming, then there
> is bound to be friction between the team's culture and the customer or
> manager's culture.
> The project may still be able to be successful using XP, but it won't
> be easy.
I would still use TDD and refactoring in these cases, even if I
wouldn't use all of XP, which has much more to it than TD and
refactoring.
The reason I would use TDD and refactoring in these cases is that it
is the best way I have found in a half-century of doing this, to
create a program that I understand all the time, that does what I
intend it to all the time, and that responds to requirements changes
both from the business side and to design improvements from me and
the rest of the team.
I'm not equally good with TDD in all languages. In particular, I
find it very difficult to use in Linden Scripting Language for
Second Life, where so much of what one scripts is only measurable in
what one /sees/, that is, in what some object in the world does.
Even there I use TDD when I can and I use refactoring all the time.
Ron Jeffries
www.XProgramming.com
The lyf so short, the craft so long to lerne. -- Geoffrey Chaucer
> reading through the thread, it seems like for many people the process
> of uncovering different responsibilities is coupled with removing
> duplications, and I'm wondering why do we tend to focus on that
> aspect.
I think there are at least two reasons, relating to what you say
next:
> If we follow TDD so that we write first the code so that it works in
> the simplest way, we can continue then to read through the code and
> find blocks that play a certain role and extract them; these roles
> could end up defined as separate responsibilities, so that SRP is
> applied. Finding areas of different responsibility in our code doesn't
> have to be necessarily duplicate code, does it? Code that does
> something, decides something or maintains some information can be
> extracted into a different class to separate its responsibility from
> the current class.
What you describe is a good practice. It turns out that when we do
as you describe, we do often manage to extract bits of code that
have some meaning. This relates very closely to the notion of
"Programming by Intention", where when we set out to do something,
we first posit a method of function that does what we want, then
call it, then write it. This, too, results in areas of
responsibility factored out.
As you suggest, we can certainly look at a block of code we have
just written and realize it has some meaning or "intention", and
extract it. This is a good thing to do.
It's not enough for the at least two reasons I refer to above:
First, sometimes we miss doing this. Instead we write two similar
patches of code without realizing we're doing it, or perhaps two
team members or pairs write them. Usually we really are doing just
the same thing again, though perhaps in our mind we don't remember
that we have had that intention before, or perhaps we are thinking
of it a bit differently now.
The result is that we wind up with sections of code that look
similar: duplication. So duplication shows us where we haven't
gotten things quite right.
Second, quite often we find that when we are about to fit in some
new feature and find the design to be not quite right, our guesses
about what the design should be are weak. If we make the design
changes first--which seems quite logical-- we often find that we
don't get it just right. It is too much or too little. So, instead,
we push the new feature into the existing code, which often results
in some duplication, which we then remove.
> Does this make sense? Am I missing something here? Or am I not the
> only one to think so?
You're right: there is more to it than duplicate code, and there are
other valuable practices that help keep the code clean and the
design good. What is quite interesting, however, is that duplication
removal takes us so far along the road to good code, all by itself.
Ron Jeffries
www.XProgramming.com
Perhaps this Silver Bullet will tell you who I am ...
What is quite interesting, however, is that duplicationremoval takes us so far along the road to good code, all by itself.
There's more than one kind of duplication and more than one way to
remove it. If, for example, we need to do something similar in two or
more places then creating a smaller reusable component *is* removing
duplication. Pretty much all refactoring can be reduced to either
eliminating duplication or clarifying intent (or both.)
I do not say that TDD is always the best thing to do.
But TDD as I see it is more a way of thinking : at some point in time, I
have a design materialized by some code.
Then I just ask to myself : can I make it better? - whatever that means
: simpler, faster, more maintainable, etc.
Adding tests, one at a time, just help me to do this.
Regards,
Olivier
> What allows the flattening of CCC in XP is simple design, automated
> tests, and constant refactoring,
> TDD also involves simple design, automated tests, and constant
> refactoring.
> Is TDD necessary to flatten the CCC?
> Is TDD sufficient to flatten the CCC?
>
Interesting. How would you define TDD without simple design, automated
tests, or constant refactoring? If you had all three how would that be
distinct from TDD?
On 5/15/11 1:41 AM, Ralf Westphal wrote:
> Yes, refactoring is an activity which does not add customer value by
> itself. It's definition says, you must not change functionality
> while refactoring.
>
> Thus refactoring should be avoided - unless it is necessary to make
> adding functionality aka customer value easier.
Looking at it that way, then typing is an activity that does not add
customer value by itself. It's just a means to express the desired
functionality.
The funny thing is, I've found that I can create that functionality
faster by using refactoring, plus the tools of the IDE. Some years
back, I started wondering why I was coding faster using TDD than I had
been before. I realized that the use of refactoring let me think about
the code in higher level constructs than characters or lines of code.
Like using higher level languages let me express things more quickly
than assembly language did, using known refactorings let me express
things more quickly than the lines of the higher level language.
> If I can come up with evolvable code without refactoring I think
> that's better than being forced to go thru refactoring steps first. I
> hope we can agree on that.
I wouldn't agree on that. I know that it takes me longer to create code
that evolves in the right way without refactoring than it does for me to
create dirt simple code and then evolve it with refactoring that
supports that evolution. It takes me less time because I don't have to
think so many moves ahead. And, if I change my mind about the direction
it should evolve, then refactoring makes the code easy to push into the
new shape I want, like manipulating a hunk of well-worked clay.
> If so, then we can go on to discuss the more interesting question: how
> to arrive at clean code right away? (Sure I'm talking greenfield code
> here. But thats difficult enough for a start.)
>
> People advocating TDD seem to be saying: that's (almost) impossible;
> you can't sit down an write clean code just like that. For a given
> requirement you need to approach an evolvable structure thru
> refactoring. You cannot know it before you have started to code.
That's not what I'm saying. I'm just saying it's faster and easier to
do so with TDD and refactoring. And that some of what makes code clean
depends on what the code needs to do. Doing that in little steps is
faster than thinking it all out and then correcting the little places
where I lacked sufficient foresight.
> As much as I like writing code test first, I nevertheless contest the
> notion of "you need to go thru refactorings to find an appropriate
> structure."
Need is a strong word. It's just easier, I find. And, even when I
think I know the appropriate structure ahead of time, I'm sometimes
surprised that I find a better one along the way. I write tests that I
intend to push for the need of the structure I thought was appropriate,
and find that a simpler structure suffices.
I don't care if you use TDD and refactoring, or not. But I don't think
your rationalization holds water. Refactoring isn't waste if it gets me
to a good solution faster, even if it looks like it's adding a step. (I
suspect that there are other steps that get shorter.) It's done that
for me. I don't know why it wouldn't do that for you.
- George
--
----------------------------------------------------------------------
* George Dinwiddie * http://blog.gdinwiddie.com
Software Development http://www.idiacomputing.com
Consultant and Coach http://www.agilemaryland.org
----------------------------------------------------------------------
>> What is quite interesting, however, is that duplication
>> removal takes us so far along the road to good code, all by itself.
> Is it because upon actually removing duplication we see our code 'shrinking'
> and get to see an immediate improvement, whereas refactoring code into
> reusable smaller components of single responsibilities doesn't give us an
> immediate visible result in the sense that removing duplication does?
> I really hope I don't come across as nit-picking or tedious, this is far
> from what I'm aiming at here; I honestly try to understand what you/other
> people think of these two practices and their importances without trying to
> say which one's 'better'. Just being truly curious from my
> not-so-experienced stand...
I believe / theorize that there is a more concrete basis for
duplication removal being so powerful. I think it has something to
do with recognizing that two patches of code, while not identical,
share some commonality (duplication), which leads us to look at them
and build the abstraction that is trying to emerge in the code.
Turning this around, it suggests that missing abstractions may very
frequently show up in (partially) duplicated code, which means that
a sharp eye for duplication will identify ideas that have not yet
even become well formed in our mind.
Of course, it could just be that duplication is a very common
"mistake" and so removing it goes after a large percentage of
problems. I think it's more than that but I could be wrong.
Ron Jeffries
www.XProgramming.com
Accept your conditions, but not your fate. -- Rod Walsh & Dan Carrison
> I was wondering if any aspect of TDD changes depending on whether or
> not it is practised in the context of XP. Have you noticed any
> difference?
I'm not sure I can answer this in a useful way, because the XP
"style" is now pretty much embedded in my nervous system. Let's see
what I say ...
> In "TDD by Example", Kent Beck says:
> TDD isn't an absolute the way that XP is. XP says. "Here are the
> things you must be able to do to be prepared to evolve further".
I believe that I do not agree with the premise that XP is an
absolute (and I suspect that Kent would no longer say that it is
either.) XP is not the only way to do software well, any more than
TDD is not the only way to do software well (Ralf, please note that
I've been saying this right along.)
XP, like TDD, is /one/ good way (or actually a whole related family
of good ways) to do software well, and the better I can do XP (or
TDD) the more qualified I am to decide when to do them, and when
not.
Neither of these things is "binary" to me, and each pays off more
the more skill I have with them. In both cases, despite my long
education and background in computing, they pay off more the better
I get at them.
I have been, years ago, really good at UML and other forms of
non-code design. Investing more in the details of those did not pay
off for me. Investing more in the details of XP, of Agile in
general, and in TDD continues to pay off to this very day.
> TDD
> is a little fuzzier. TDD is an awareness of the gap between decision
> and feedback during programming, and techniques to control that gap.
> "What if I do paper design for a week, then test drive the code? Is
> that TDD?" Sure, it's TDD. You were aware of the gap between decision
> and feedback. and you controlled the gap deliberately.
> Assuming that doing paper design for a week is not contemplated in XP
> (is that right?), would this be an example of doing TDD differently
> when not in an XP context?
One needs to do as much paper design as needed, whenever it is
needed, be that up front or right in the middle of an afternoon's
coding.
Because paper design advances understanding but not "features", it
can quickly lose value when compared to a technique that advances
understanding plus feature progress. Some people can advance
understanding plus feature progress very well using techniques like
TDD. No people ... literally NO people, can advance understanding
plus feature progress at the same time using paper design.
However, a little paper design can then make feature progress go
faster because understanding is necessary to good programming. Can
it make us go faster than TDD? It depends on several things:
- How good are we at paper design? It happens that I'm very
good at it, and still I reach diminishing returns quite quickly.
People who are less good at it will reach diminishing returns
even more quickly.
- How good are we at simultaneous coding and design (via TDD or
some similar approach? The better we are at these, the sooner we
reach the transition point where a simultaneous approach is
better than a design then code approach ... at least for a
while.
- How good are we at recognizing that it is time to switch?
Sticking with either one too long is wasteful ... but it is
differently wasteful. A little too much paper design doesn't do
much harm. We might have a few too many ideas but ideas don't
usually hurt us. A little too much simultaneous coding and
design can result in a bad physical design which, depending how
good we are at refactoring, can take a long time to get out of.
All these play together. If our TDD/Refactoring skill is very high,
we need not spend much time in the solo paper mode, which does not
advance features, yet we do not lose velocity by going off on random
"code slinging" adventures that mess up the design, because we have
the skill to observe the design and the code at the same time.
It is quite possible that some people really cannot simultaneously
do code and design. These people are doomed to do paper design and
then try to work toward it. I believe they are also doomed not to be
very good.
(I will address this last paragraph a bit further in my reply to
one of Ralf's comments to me because I think he has caught himself
in a contradiction.)
For me, I don't do TDD differently when I'm doing XP than when I'm
not, because I am always trying to work more like XP and to work
more like TDD and to do a better job of paper design when I do it,
and so on. To me these things are not exclusive and conditioned by
each other: they are all things to continue to learn to do well.
Hmm, I was right: I couldn't directly answer your question. I hope
this explanation of why is somehow enlightening anyway. :)
Ron Jeffries
www.XProgramming.com
Testing quality into a program is like spinning straw into gold.
-- George Cameron.
>> Again the alpha-end kata is an example for that. Look at my code
>> (http://codepaste.net/h5pfe6) and tell me, if this looks much
>> different from what you would have come up doing TDD? My guess, the
>> difference is small and maybe just a matter of style/taste.
>>
>> So the structure of my solution is the same as with TDD - but without
>> a single refactoring.
>> Only one refactoring was necessary to accomodate the "new requirement"
>> of providing converters for different number systems.
>> That clearly was a refactoring of type B and thus unavoidable.
> You pointed out this "Alpha End converter" exercise. I just tried to do
> it with TDD. Well, TDD as I do it - not sure it is a standard and
> "certified" way of doing TDD.
> My result, including refactoring history, is here :
> https://github.com/Oaz/AlphaEndConverter
> Basically, my tests and refactorings led me to a much shorter piece of
> code with no need to introduce concepts such as 'base power' in the design.
> And the whole thing took me no longer than a quarter of hour.
Very interesting, Olivier, thanks for doing this.
And let me congratulate both of you for putting your code out to be
looked at. That takes great nerve to do. Well done both.
Olivier, your solution exemplifies a thing that happens often ... in
these smaller problems if not in larger.
Namely, the design we create on paper turns out to be more design
than we need to solve the problem in hand. TDD has a strength in not
causing us to do more design than we need, which is good because it
can lead to solutions that are no more complex than they need be.
This is also a weakness, of course, since sometimes not enough
design is done //anywhere// (whether on paper or in code) and the
design is not robust enough. I'd far rather do too much than too
little design.
To me, Olivier's design is arguably "more procedural", but it is
shorter and simpler and therefore has a lot going for it. This is a
common outcome with TDD ... we do not over-design ... by which I
mean that at any moment, we have no more design than we need.
It can be argued that Ralf's design will be easier to extend. This
may even be true. Nonetheless, that extra design is not carrying its
weight now, even if at some future time it may.
One last point, which I'll put here rather than in yet another reply
to Ralf.
This careful paper design may have some good properties. I do not
think "didn't need to refactor" is automatically a good property,
however, because it depends on how long it took to get there and how
long it takes to refactor.
But more interesting to me are these facts:
1. We don't know if Ralf's solution works or not: we do not see the
tests. He says there are tests but we didn't get to see them. It
sounds like the tests are created after the fact. There may be blind
spots when we do that. In any case, we just can't tell. Easily
enough corrected, and of course in a real situation we could browse
them if we wanted to. Nonetheless, right here right now, we can't
tell.
2. We don't know why the solution is what it is: we do not see the
progression (which we can see in a series of tests). This is
uncorrectable. We can show evolution with TDD much more readily than
we can see how the paper design evolved and why.
3. The design as it stands has some rather visible flaws. Perhaps
the most important of these is the preponderance of "internal"
methods, which often signal an abstraction trying to appear. The
most telling of these is the returning of a tuple, which is always a
red flag in the code saying "I have an object in mind here but I
didn't bother to make it an object". In this case, if I understand
the code correctly, we have a mantissa (cleverly named Item1) and an
exponent (named Item2). These names do not communicate our design
intention nor our implementation intention.
We then have a separate method that slams values into that tuple.
This is a violation of the Law of Demeter. A better design might
encapsulate all of that behind some new class with mantissa and
exponent, and methods internal to that class that set the
components.
The result of all this this is that while perhaps the paper design
saved a refactoring, it remains further than one might like to see
from a design that correctly presents the abstractions that it
contains.
Now back to Olivier's program. It is in fact simpler ... but is it
as easy to understand? I'm not sure. On the one hand it has
everything embedded in that single convert method but even for a
lover of the ternary operator, that method is tricky to understand.
It is recursive (very cool) and therefore harder to understand than
Ralf's loop.
We see here, I hope, that the question of "which is better" is quite
complex. We get to see a bit more of Olivier's thought process and
really none of Ralf's because he did it on paper that we cannot see.
Olivier's solution covers exactly what it needs to and no more:
there seems to be no excess in it.
Ralf's solution seems to me to have more design than was needed to
do what it does, but I can't be sure because I can't see the tests.
Furthermore, it contains more abstractions, which is not necessarily
good, and it contains some very incomplete abstractions around the
tuple and the demeter-violating method that hammers on it.
I'd have to say that I prefer Olivier's for being simpler, and agree
with Ralf that another reader might prefer the other. More of
Olivier's design process is visible and Ralf's design process is
hidden.
It's a hard question. Partly it is a matter of taste, but I think
the partially-implemented abstractions, the violation of Law of
Demeter, and the absence of the tests gives Olivier the win in this
case, by a small margin, in this referee's opinion.
Your mileage may vary.
Ron Jeffries
www.XProgramming.com
War remains the decisive human failure.
-- John Kenneth Galbraith
> But does your solution prove me completely wrong?
> No, I don't think so.
You're right, it doesn't. And I actually believe that the recursive
solution, while it has a certain elegance, is less robust when we
realize that we'll have to face it in the future and figure out out
again. The looping solution is simpler in some important ways.
> Implementing algorithms is not really what baffles most developers.
> Rather it's working on larger problems, how to structure software
> systems.
Very true ...
> And it's how to put several developers to work on the same feature at
> the same time to jointly produce it as fast as possible (think WIP=1).
Yes ... which requires a common understanding among those
developers, as you rightly point out ...
> For that some kind of "design of coarse grained functional units" is
> necessary, eg classes, components, packets, services.
Yes. Readers of Extreme Programming Installed (Jeffries,
Hendrickson, Anderson) will recall the "Quick Design Session", which
is a short period of time when the people working on some feature or
features work through a design sketch prior to moving into code ...
or in the middle of coding when they feel the need to bring their
head up out of the muck.
This is very important ... which is why right along I've been saying
that drawing pictures or tossing CRC cards or other means are
important. However ...
> TDD is hardly of
> help then.
This may be true for you, but it is emphatically not true world
wide. Many individuals and teams understand how to show and
manipulate "course-grained functional units" in code. It's not
always the best way but sometimes it is.
You, Ralf, assert on the one hand that you are very skilled in
TDD, and yet on the other hand you state that you cannot work on
both design and code at the same time, and that you cannot see the
coarse grained design (very well) in the code.
I can think of a few explanations for this. One would be that your
brain is simply not capable of doing what many other brains can
do. I think this unlikely. My own model of what would cause you to
say those things is that your skill at TDD is not as high as that
of people who CAN work on design and code at the same time, and
who CAN envision coarse-grained design while working in code.
You seem to take that as a bit insulting. Hardly. I'm assuming
that you could learn how to do this as well as anyone ... and that
as yet, you have not.
> Maybe we should continue to compare different approaches on a larger
> scale.
> How about a small but whole application? Like maybe a Tic Tac Toe
> game?
There can be value to this, though it may be quite difficult to
bring it out in a written forum like this one. To compare the two
approaches, we need to control for quality at least in the sense of
running all the tests, and quite possibly for other matters of
quality such as having all the abstractions needed and no more, and
so on.
I'd like to see it done and I am not sure whether we have the time,
the will, or the medium to bring it off.
You guys have already gone further than most in displaying your
code, and again, I congratulate you. If you can do more that will be
great.
Ron Jeffries
www.XProgramming.com
Here is Edward Bear, coming downstairs now, bump, bump, bump, on the back
of his head. It is, as far as he knows, the only way of coming downstairs,
but sometimes he feels that there really is another way, if only he could
stop bumping for a moment and think of it. And then he feels that perhaps
there isn't. -- A. A. Milne
> ####################################
> With these elements in place�simple design, tests, and an attitude of
> constant refinement of the design�we experienced the flattened curve.
> A change that would have taken a few minutes before much coding had
> occurred took 30 minutes after we had been in production for two
> years. And I see projects spending days or weeks making the same sort
> of decision, rather than doing what they need to do today and trusting
> themselves to fix it tomorrow if they need to.
> With this shift in assumption about the cost of change comes the
> opportunity to take an entirely different approach to software
> development. It is every bit as disciplined as other approaches, but
> it is disciplined along other dimensions. Instead of being careful to
> make big decisions early and little decisions later, we can create an
> approach to software development that makes each decision quickly, but
> backs each decision with automated tests, and that prepares you to
> improve the design of the software when you learn a better way to
> design it.
> ####################################
I'm really not here to explain or justify Kent's words from over a
decade ago. I'm not even always able to justify mine from the same
era.
What he says here is mostly true, I think, depending on what we mean
by flattening the curve.
> How does TDD relate to the cost of change curve (CCC)?
TDD provides a suite of very tight tests as a /side effect/ of a
very useful design technique. (Not the /sole/ design technique but a
very useful one.
The tests provide for increased certainty that changes have not
broken things and increase speed of detection when they do. This
reduces the cost of change and the cost of error, flattening the
curve.
> What allows the flattening of CCC in XP is simple design, automated
> tests, and constant refactoring,
> TDD also involves simple design, automated tests, and constant
> refactoring.
TDD is how an XP programmer DOES simple design, automated tests, and
refactoring. It is the name for the suite of behaviors that cover
those topics in XP.
> Is TDD necessary to flatten the CCC?
Depends how much you want to flatten it. Comprehensive unit tests
might do the same level of testing at higher cost. Changing the code
without understanding behavior-preserving transformations might
still allow us to make changes, but more slowly thus not flattening
the curve as much.
TDD / refactoring are the best way we know // at the coding level //
to keep the curve flat. Perfect up front design will not do this,
from the viewpoint of XP and Agile, because (a) perfect up front
design is not possible, (b) implementing a perfect design perfectly
is not possible, (c) up front design is done at the moment we know
less than we ever will again, and (d) things change anyway.
That doesn't mean we don't do paper design: we do and it is good. It
just doesn't give us the things that TDD does and we need those
things.
> Is TDD sufficient to flatten the CCC?
Nothing is sufficient in software and never will anything be
sufficient. We are always challenged by harder and harder problems
and we must always be advancing our skill. We need to know how to do
paper design early, and how to do it late. We need to know how to
test early and how to test late. We need to know how to write code
to a design and how to evolve the code to include our new learnings.
And a thousand other things.
Ron Jeffries
www.XProgramming.com
Curiosity is more powerful than skepticism.
> But I find it very limiting to stop at that.
> So I have ventured into "unknown territories" beyond TDD... (which in
> the end where not uninhabitated; I met other travellers back from
> another century over there).
> And what I�ve found is much more efficient than TDD - but maybe
> require a departure from some dearly held believes.
I do not prefer TDD because of faith, nor because of a lack of
understanding how to do non-code design. On the contrary, over the
half century I've been developing software, I have studied and
developed as much skill as I could in every design technique that
has attained prominence over that period ... including TDD plus
refactoring.
I have found value in all of those. I prefer TDD because, like Adan,
having developed rather high skill in all the techniques out there,
I find that a very liberal dose of TDD lets me go faster, with less
stress and fewer mistakes than any approach heretofore.
I have every reason to believe that the equation would hold true for
anyone of (roughly) equal skill in all the various forms of design
and coding under consideration. (I could imagine that some special
mental or physical characteristics might play in there. For example,
I would expect that a blind person would find UML design to be much
less productive than a sighted person might. And I can at least
imagine that someone's unique mental characteristics might leave one
ill equipped to do TDD (which to be done well MUST include focus on
both detailed coding and what you have elsewhere called "coarse"
design).
My experience in teaching TDD to many many individuals and teams has
led me to believe that most anyone can learn it. Since many people
can do in TDD what you say you cannot -- namely deal with detailed
and coarse design issues in that "medium" -- my confident
expectation is that these people learned something, attained some
perhaps small skill, that you, as yet, have not.
If there was just one guy standing here saying TDD can do what you
say it cannot, it might be a tossup. However, I can do it, Adam can
do it, George can do it and the many people we have helped learn it
can also do it.
That makes me quite confident that you could do it too. I'm not sure
why that seems insulting to you, but it seems that it does, and I
regret that. I still think you could also do it if you cared to, and
that it would change your preferences between paper and in-code
design if you did.
I'm perfectly happy if you choose not to. What I'm less happy about
is that your description of the universe as being one in which no
one can do coarse and fine design in the code is not the universe
I'm living in.
Ron Jeffries
www.XProgramming.com
I must create a system, or be enslaved by another man's;
I will not reason and compare; my business is to create. --William Blake
> Sorry to say, but this seems to imply you know about my TDD skills
> well enough to recommend I improve on them.
> How come?
> Do you think just because I�m not all for TDD I�m not good at doing
> TDD?
No ... but since you say things about TDD that in my experience, and
that of many others, are simply not true, namely that it is
"multitasking" and therefore bad, and that it is not possible to
deal with fine and coarse design in code, I have to conclude that
some of us must know something about it that, as yet, you may not.
> I�d say there are other possible explanations:
> a. I�m good at TDD and still am dissatisfied with it�s results.
If that were the case you'd be saying different things. You'd not be
talking about how impossible it is to design this way but how
inefficient it is. Yet your argument leans much more on possibility
than it does on efficiency.
Even then, it would be necessary to explain why, despite reports of
TDD experts all around you, saying that it's the best way they know
so far, they are all wrong.
> b. I�m not good at TDD, but TDD nevertheless is not the pinnacle of
> software design, and even someone outside the "TDD believe system" can
> sense that.
The assumption that people who like TDD do so because they are
"believers" is ... a bit off. I like it because I've been doing it
assiduously for 15 years. Prior to those 15 years I have worked just
as hard learning every design technique as it came along. The sum
total of my current half-century of software development leads me to
the //discovery// -- not the mere belief -- that it is a very
powerful addition to my way of working and that, in particular, it
allows me to think about design in the presence of code, which
provides faster feedback than thinking about it separately.
But no one is saying TDD is "the pinnacle of design". We are saying
that day in and day out, we find that doing TDD, full-on, with
refactoring, gets our software done faster and better than when we
do it with up front paper design and then "code slinging", and
better than interleaving "code slinging" and paper design.
It turns out, we don't do "code slinging" at all: we use a very
disciplined way of producing code that keeps it clean, keeps it very
well supported by tests, and builds great skill in evolving the
design on the off chance (only 100%) that it will need it.
We still draw pictures of the code, or go chat about it, or get a
really good design idea in the shower or on the drive home. And we
build that design in a disciplined way that lets us incorporate not
just those insights, but the ones that come to us as we produce the
code in our incremental disciplined fashion.
> Also your statement implies: Ron is good at TDD (because TDD is the
> best way to design software); thus whoever is not doing TDD is wrong
> (or at least in danger of malpractice because he�s not applying what�s
> broadly recognized as the pinnacle of design).
Doesn't imply that at all. It happens that I've tried lots of ways
and find this one to be substantially more valuable than you do ...
and it happens (but does not imply) that I've helped many others get
this same discovery.
I didn't come to TDD directly from gross hackery. I came to it with
35 years of experience doing all the disciplined design approaches
that came along, and now I come to you with a full half century of
that plus 15 years of developing my TDD skills in the same way.
> Hm... this does not feel good, Ron.
Yes, I see that, and it really confuses me. I'm assuming something
good about you, namely assuming that you have the innate ability to
do what I can do, what Adam can do, what George can do, what many
others can do, and use TDD more effectively than you've reported
here.
And yet ... you seem to feel badly because I'm supposing you can
readily learn to do these things that you're reporting trouble on
just now.
I have confidence in you ... and somehow that hurts your feelings.
I'm sorry about your feelings, but not willing to withdraw my
confidence in you.
> This does not sound like the scientific method.
It isn't the scientific method. It is more like a report from your
future from someone who has been there. I've done waterfall method,
ad-doc development, Spiral Method, Structured Design, the Booch
Method, UML, and many more, in real world software development. And
I've done TDD for a long time as well.
I'm reporting that I find that TDD gives me certain advantages. I
observe that you are not getting those same advantages in the same
proportion. My best guess is that I have stumbled on a way of doing
it which you have not. Another possibility, of course, is that you
know a way of doing paper design that I don't know, that somehow
multiplies its power. Since you have repeatedly said things about
TDD that are not true for me and lots of people I know, I lean
toward the theory that there's at least one more TDD learning
waiting for you.
I could be wrong, and even if there is, you can decide not to seek
it. That's OK. I don't get royalties from people using TDD. I do
care that whenever you, or anyone, says "TDD can't do this", and
there are people out there like me who know that it can, one or more
of us says so.
TDD can be used to evolve a design faster than at least some people,
highly skilled in paper design, can do paper design and then code to
it. TDD can be used in situations where the requirements change, and
therefore the paper design initially created needs changing, and it
can let us do those changes without a rewrite.
It doesn't make much sense to me for you, or anyone, to suggest that
it cannot do that, because people are in fact doing it. The fact
trumps the theory. So whenever such a suggestion is made you can be
sure that I, and the others who know how to do those things, are
going to say "Hey, wait, we can do those things. It's just not true
that the only way to go is that other way."
Ron Jeffries
www.XProgramming.com
In times of stress, I like to turn to the wisdom of my Portuguese waitress,
who said: "Ol�, meu nome � Marisol e eu serei sua gar�onete."
-- after Mark Vaughn, Autoweek.
I'm not sure what red green { refactor red green } looks like. Is the
first red on paper? Hmm, I hope not.
I am very interested in hearing more about your technique, Ralf. Like
Ron, I do TDD because it's given me the best rate of return so far. It
seems that every time I've tried to model and write clean code up
front, I've stalled, stumbled, made mistakes, and bumbled around
getting little done. Almost every time I've done a short design
session, then TDD, I've finished my task, and been proud enough to
share. Like George said, I "feel" much more efficient and effective
when I build features TDD.
If you have a new way to model that I can practice and learn that will
make me more efficient at building maintainable code than TDD, I'm all
ears. Just saying "do more modeling up front" isn't it, though,
because I've done that and then went to TDD.
I always try to remember the goal when I write code. The goal is
maintainability. No other measure of good design matters, because the
goal is keeping it easy to add new features. TDD clicked right away
with me in terms of the goal because TDD makes you maintain the code.
Again, it's viscosity working in your favor. And I'll say it again: If
you have a new way to build maintainable code better then TDD, please
share.
This has been a fascinating conversion, a little tense at times
perhaps, but very interesting. Thanks all!
--
Curtis Cooley
curtis...@gmail.com
blog:http://ponderingobjectorienteddesign.blogspot.com
===============
Leadership is a potent combination of strategy and character. But if
you must be without one, be without the strategy.
-- H. Norman Schwarzkopf
On Mon, May 16, 2011 at 11:31 AM, Curtis Cooley <curtis...@gmail.com> wrote:
...
> I always try to remember the goal when I write code. The goal is
> maintainability. No other measure of good design matters, because the
> goal is keeping it easy to add new features. TDD clicked right away
> with me in terms of the goal because TDD makes you maintain the code.
> Again, it's viscosity working in your favor. And I'll say it again: If
> you have a new way to build maintainable code better then TDD, please
> share.
I suppose this is automatically implied for many people, but when
reading Bob and Micah Martin's /Agile Principles, Patterns, and
Practices in C#/ (APPP) it really struck me that it's not nearly as
simple as focusing on maintainability. Indeed, I had often focused too
much on maintainability; I had used too many design patterns for the
sake of maintainability when a few functions would have been simpler
and sufficient. It probably didn't take me much longer to use those
design patterns, but I wasted time. I was attempting to anticipate
changes that would never come.
APPP puts it this way, '"Fool me once, shame on you. Fool me twice,
shame on me." This is a powerful attitude in software design. To
keep from loading our software with needless complexity, we may permit
ourselves to be fooled once. This means that we initially write our
code expecting it not to change. When a change occurs, we implement
the abstractions that protect us from future changes of that kind. In
short, we take the first bullet and then make sure that we are
protected from any more bullets coming from that particular gun" (pg
129).
I think there are many barriers to doing TDD well, like the above.
Despite following the three laws of TDD, I wasn't doing TDD
particularly well. How much is too much refactoring, for example? I
overdid it, and I recognize that now. I had to learn to trust myself
enough to leave the code as-is until a sufficiently strong stimulus
came along at which point I could refactor it to a more reasonable
design against real "changes of that kind."
--
Kaleb Pederson
Blog - http://kalebpederson.com
Twitter - http://twitter.com/kalebpederson
>
> This has been a fascinating conversion, a little tense at times
> perhaps, but very interesting. Thanks all!
> --
> Curtis Cooley
> curtis...@gmail.com
> blog:http://ponderingobjectorienteddesign.blogspot.com
> ===============
> Leadership is a potent combination of strategy and character. But if
> you must be without one, be without the strategy.
> -- H. Norman Schwarzkopf
>
> --
> You received this message because you are subscribed to the Google Groups "software_craftsmanship" group.
> To post to this group, send email to software_cr...@googlegroups.com.
> To unsubscribe from this group, send email to software_craftsma...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/software_craftsmanship?hl=en.
>
>
Ron,
Thanks for your comment.
I agree with your statement: the code is not easy to understand and
needs further refactoring to make it understandable.
In my opinion, this is the kind of refactoring the most difficult to
deal with: transform the code so that someone else will find it easy to
read.
One thing that usually helps me is to think about the model behind the
code that comes out of the TDD and check if this model is visble just by
reading the code.
I should have done that with this program but I did not and the model
("the digits in base N for a given number is the concatenation of the
digits representing the part of the number grouped in packs of N units
and the single digit for the remaining units") remained hidden.
Olivier
Does it make sense to you?
Olivier
Hi Kaleb!
I couldn't agree more with what you just wrote, and it means I
misrepresented what I meant. TDD helps me write maintainable code
because it helps me keep the design simple. I can see how designing
for maintainability could lead to over-designed code, but I would
argue that over-designed code is less maintainable than simple TDD
code.
You've made me start to contemplate the meaning of maintainable, and
that's a good thing! Off the top of my head, I meant maintainable in
the sense that it's easy to understand, fix, and extend. I guess that
it conforms to the XP rules of simplicity?
The reason we care about design is so we don't make a mess. We don't
want to make a mess because someday we'll have to go back and fix or
extend the code. I guess that's the goal? Don't make a mess?
> Is TDD a part of XP that is NECESSARY to flatten the curve
I believe it is. The reason I believe that is that I believe that
comprehensive automated testing is necessary to get the level of
feedback needed to flatten the curve, and these tests must be run very
often (At least on the order of a few times per hour. Much faster
should be possible.) I have heard many claims that such aggressive
testing is possible without TDD, but every time I have had an
opportunity to investigate such a claim I have found that the team
wasn't doing nearly enough "test-after" to get the benefits.
> Is TDD a part of XP that is SUFFICIENT to flatten the curve
I believe it is *not*. At the very least some sort of aggressive
automation is needed so that changes can be tested and shared
immediately with the whole team. Whether this looks like traditional
continuous integration, continuous delivery, or something else I am
not sure.
I also think that having a collaborative work environment where I can
simply shout when I need to have a discussion about something I am
about to do is a huge benefit to flattening the curve. I have heard of
teams managing this adequately when they were distributed, but, again,
I have never witnessed it (I have witnessed distributed environments
where design was shared within hours or days, but not immediately.)
Pair programming is also useful. In my experience good pair
programming is at least as important to simple design in XP as
test-first programming is. Having my pair chime in with a good idea at
just the right moment or catch a mistake right when I make it is a
huge factor in flattening the curve.
>
> Asking both questions was just my roundabout way of asking whether TDD
> is PRECISELY the part of XP that flattens the curve.
>
What would you do with that answer if you got it?
Part of the challenge of adopting XP piecemeal is that the practices
support each other very nicely. There is a synergistic effect that
happens when you are doing just enough of each of them that doesn't
seem to happen when you do one or two in isolation. That's not to say
that everyone does (or should do) XP the same way, but when it works
it works like a stew -- the right stuff in the right proportions, no
single ingredient dominates.
> Although your answer does tell me an important part of what I wanted
> to find out with my questions, it also makes me realise that to find
> out the other parts I should have phrased the questions thus:
> Is TDD a part of XP that is NECESSARY to flatten the curve
No ... there are many practices to keep the code alive and they all
flatten the curve somewhat. TDD is one of the more powerful
flatteners.
> Is TDD a part of XP that is SUFFICIENT to flatten the curve
It flattens it somewhat. There are ways to flatten it more.
> Asking both questions was just my roundabout way of asking whether TDD
> is PRECISELY the part of XP that flattens the curve.
No. ATDD does. Continuous integration does, pair programming does,
refactoring sans TDD does ...
> The reason I wanted to know this is because I remember that when I
> first read about how XP worked, I found the idea of flattening the
> curve remarkable. One minute, XP was putting me at ease by mentioning
> the familiar universal software engineering assumption that the cost
> of change curve is exponential, and the next minute it was turning
> everything upside down by saying that it was able to flatten the
> curve, which allowed it to justify contrarian strategies like TSSTCPW,
> YAGNI, and Last Responsible Moment, which in turn reduced project risk
> and thus increased project value.
The cost of change topic is, in my opinion, mostly a red herring. A
sufficient reason for doing the simplest thing is that doing a more
complex thing (obviously) takes longer. A sufficient reason behind
YAGNI is that investing prior to the moment one needs the investment
is wasteful. Similarly for LRM.
> Also, you know how sometimes you
> hear developers asking: "How can I sell the idea of refactoring the
> codebase to my manager". And the answer is some compelling analogy,
> e.g "Think of code that needs refactoring as a "growth". Removing it
> requires invasive surgery. You can go in now, and take it out while it
> is still small. Or you could wait while it grows and spreads - but
> removing it then will be both more expensive and more dangerous. Wait
> even longer, and you may loose the patient entirely". Well I thought
> that the key technical premise of XP, a flat curve, was also
> remarkable in that although at first it clearly seemed impossible, it
> was so simple to grasp (due in part to people's familiarity with the
> exponential curve) that once I suspended my disbelief, it was
> compelling how the seemingly contrarian strategies were justified by
> the premise. I thought that XP's premise would be very important and
> effective in selling XP to managers.
When a team tries to sell "refactoring the code base" they are in
very dangerous territory. First of all the request tells me that the
team have crapped up their code base, which is prima facie evidence
that the team isn't really very good.
Second, the only argument for refactoring after the code is crapped
up is that we will go "faster, later". So this team that wasn't
smart enough not to crap up their code base is now asking me to
throw good money after bad so that they -- demonstrably incompetent
though they are -- can work in some invisible way in hopes of doing
a good enough job to speed up later.
I don't think so. A manager would, nine times out of ten, be a fool
to agree to this.
The right strategy when the code is bad is to make it better
incrementally, by refactoring areas where new features are wanted,
making them a bit cleaner (and therefore making the next feature to
pass through a bit easier to do), while continuing to deliver
features, which is, after all, what the team is paid to do.
So if a team said to me "we need to refactor our code base", I would
be pretty sure that they don't know what a good code base looks
like, that they don't really know what they are changing or why, and
that they don't understand the business's problem, which is to get
new features soon.
In short, that's a beginner's request and I would not be inclined to
support it.
> Assuming the answer to my questions is that TDD is THE part of XP that
> flattens the curve (or at least a significant part of it), then TDD
> has the same key technical premise as XP. But unless I am mistaken,
> when TDD is discussed/advocated as a stand-alone technique, outside
> XP, I don't think I hear people mentioning the technical premise. I
> don't think I hear people using the idea of the flat curve to justify/
> explain TDD.
The curve never goes flat. The XP practices, done well, permit a
team to produce features at roughly a constant rate over a long
period of time. TDD is a big part of this. It isn't all of it, and
you have to do it bloody well to get the benefits.
> When Ralf said that doing design through refactoring is wasteful/
> painful, that it is faster/cheaper to refactor the design model
> instead, I decided to suggest to him that he should view the cost of
> slowly refactoring from passing test to clean design as the reasonable
> price to pay for the benefits that result from TDD's flattening of the
> cost of change curve: much reduced risks and the attending increase in
> project value.
I don't follow that but I think it's because I don't buy the cost of
change curve argument as a strong motivator in any case.
> Here is a passage from Extreme Programming Explained that acknowledges
> the cost, but balances it with the payoff:
> "When implementing a program feature, the programmers always ask if
> there is a way of changing the existing program to make adding the
> feature simple. After they have added a feature, the programmers ask
> if they now can see how to make the program simpler, while still
> running all of the tests. This is called refactoring. Note that this
> means that sometimes you do more work than absolutely necessary to get
> a feature running. But in working this way, you ensure that you can
> add the next feature with a reasonable amount of effort, and the next,
> and the next. You don't refactor on speculation, though; you refactor
> when the system asks you to. When the system requires that you
> duplicate code, it is asking for refactoring. If a programmer sees a
> one-minute ugly way to get a test working and a ten-minute way to get
> it working with a simpler design, the correct choice is to spend the
> ten minutes. Fortunately, you can make even radical changes to the
> design of a system in small, low-risk steps"
OK ...
> I have not replied to Ralf yet because I am still digesting his claims
> that by doing the design before the coding, he also experiences a flat
> cost of change curve, and that rather than designing through
> refactoring, he says he can pay less by refactoring the design model,
> and then modifying the code to reflect the new design. I find your
> answers at the top of this post, and in other posts useful in
> addressing these claims.
Ralf isn't wrong that it is easier to refactor a paper design than a
code design. It /is/ easier. It's not wrong to think on paper. It
is, however, less than ideal to think very long without testing
one's ideas in code. Speculative design is easy to get a bit wrong
in many different ways. In the example Ralf bravely did, we saw
partial abstractions plus over-design. When we combine that kind of
thinking with the concrete code, we get faster feedback on the
design. When we know how to TDD and refactor well, we don't need to
stop thinking and we can make better decisions as to how quickly to
move to code.
Ron Jeffries
www.XProgramming.com
When you blame others, you give up your power to change.
-- Robert Anthony
> One thing that usually helps me is to think about the model behind the
> code that comes out of the TDD and check if this model is visble just by
> reading the code.
> I should have done that with this program but I did not and the model
> ("the digits in base N for a given number is the concatenation of the
> digits representing the part of the number grouped in packs of N units
> and the single digit for the remaining units") remained hidden.
Exactly ... and this is where Ralf is on a good track. It is often
useful to use a "paper" model to help us see what we have done, or
what we should do.
My only concern with Ralf's position is not the use of external
models. It is that I believe he under-values the power of TDD /
Refactoring. Since TDD / Refactoring pays off for me and many
others, that leads me to think he's doing something else.
Ralf: Note that I'm answering email randomly. I know you have a
reply to me that I've not responded to and I'll do that shortly.
This one precedes that one and therefore does not include whatever
you most recently said.
Ron Jeffries
www.XProgramming.com
Make it real or else forget about it -- Carlos Santana
On 5/16/11 5:54 PM, Olivier Azeau wrote:
> Putting several developers to work on the same feature is, to me, an
> issue with TDD because design emerges from coding and, at some point,
> the multiple designs have to fit with each other.
> I did not figure out any other way than starting with a bit of design
> before coding ;-)
I've found it works quite well with the developers communicating during
coding. Have you tried that?
> On 16 Mai, 23:52, Curtis Cooley <curtis.coo...@gmail.com> wrote:
>> I couldn't agree more with what you just wrote, and it means I
>> misrepresented what I meant. TDD helps me write maintainable code
>> because it helps me keep the design simple. I can see how designing
>> for maintainability could lead to over-designed code, but I would
>> argue that over-designed code is less maintainable than simple TDD
>> code.
> Could you (or anybody) please give a definition of "overdesigned
> code"?
Code is over-designed to the extent that it contains more classes,
methods, or other entities than are required by its present level of
function. A simple example is the existence of unused code, put in
because "we're gonna need it".
Kent Beck's definition of "Simple Code" is
1. Runs all the tests;
2. Contains no duplication;
3. Expresses all our design ideas;
4. Minimizes the number of classes, methods and other entities.
Over-designed code violates #4 by having more entities than are
required to express the design, or #3 by having more design
expression than required to support the current tests.
An example in the number conversion example would be the insertion
of an abstract superclass and a single concrete subclass, before the
code actually needs to support multiple numeric bases.
Someone designing on paper might will discover the perfectly good
design of using a simple concrete subclass for each numeric base,
perhaps just containing the list of digits for that base, or any
such implementation, inheriting from an abstract superclass. It
would be almost impossible for most of us to build a single-base
version without putting in the abstract superclass and the single
subclass we really need. We just couldn't resist.
Until the day (or hour or minute) that that code gets the second
concrete class, it is over-designed for its capability.
A good TDDer, Kate Oneal, thinking about this problem as she went
along, might see the same design. Kate would not put in the abstract
superclass / concrete subclass in response to her single base tests:
she //might// build a single concrete class.
Kate might even build the list of digits in line in the conversion
class: I believe Olivier's program may have done that. At some point
during the implementation of the original numeric base, she would
allow herself to "notice" that inside the conversion class there was
a numeric base idea trying to get out. She might, at that point,
extract a concrete "Base13" class.
Then she would stop. Kate knows as well as we do that for the next
numeric base she is going to need the abstract / two concrete
structure. But she doesn't need it now. So she doesn't put it in.
Then she writes the first base-seven test. Quite likely, it runs.
Soon she expresses a number bigger than seven in her base seven
tests ... and surprise! (not really) she "discovers" the need for
the abstract / concrete structure.
Then Kate probably does one of two things:
1. She comments out the red > 7 test, gets back to green, then puts
in the abstract class and the concrete base 13 class. She then
uncomments the > 7 test, goes red, and uses the red bar to implement
the other concrete class.
Or, 2, she implements the > 7 test by putting in some little bit of
hackery, such as an if statement in the base 13 class that checks
the desired base somehow, and returns the base 7 list:
if ( base == 13 ) return digitsOneThruD;
else return digitsOneThru7;
This makes the test go green. Kate then goes Oh wow look, this
design is starting to suck (but she's not really surprised), and she
refactors the if statement into the two classes.
(Replace Conditional with Polymorphism,
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html)
The up-front design person, Gil, says
"How wasteful! We knew all along that we needed the abstract /
concrete structure. We should have put it in at the beginning."
Kate responds
"Yes, we knew that. The TDD ritual is to wait for the code to tell
us, whether we know or not. This gives us some things that we
like:
"First, we never have more code than we need to get to the current
green. This minimizes the code ... and minimizes the time to get
to green. We can ship on any green, as soon as we have enough
function.
"Second, by practicing recognizing this need -- pretending that we
don't see this obvious design -- we become skilled at getting
design ideas from the code. Since much of the code we will
encounter in our lives will have design flaws, this skill at
recognizing the need, and responding to it with the right
refactoring, serves us well."
The design person, Gil in this case, always replies that it seems
wasteful to him, since obviously and admittedly the TDD way went
through one or more unnecessary steps:
"With your silly TDD way, you had to type in code and then change
it. That's just plain waste! With my way, I just type in the
correct code to begin with. That's far more efficient."
Kate smiles, having been here a million times before. She says:
"Perhaps not. If I can spend less time in up front design than I
used to, I get my first tests running sooner. Think of it this
way: there is simply less code to type in to get base 13 running.
With that, plus the shorter time I spend in up front design, I can
get base 13 running faster than I could if I first spent more time
up front, and then spent more time typing it in because it's a
longer solution.
"Second, any up front design is speculative. I expect that it will
work, and because I'm a good designer, I am usually right. But I
won't know how well that design really fits until I begin to build
it, and to use it.
"Gil, probably in your programming life, you've been handed a
bunch of classes to use to build something. If your life has been
like mine, you found that those classes were useful, but hard to
use and didn't really quite fit your problem. Early on, if your
experience is like mine, your use of those classes was a pain.
They were too complex to use readily."
Gil has had that experience too, and nods. Kate goes on:
"I have learned that even when I do my own design, if I create the
abstractions first, they often do not quite fit my desired usage.
They get in the way a bit, slowing down my learning and my
implementation. So I've found that starting with the simple design
and evolving it as I need it works better for me."
Gil replies, as they always do:
"OK but if you were better at up front design, I still think you
would go faster by the end, because you wouldn't have to do all
that extra editing."
Kate replies cheerfully:
"Better at up front design, like you, I suppose you mean? Maybe
so. But its not clear, is it? Another perfectly possible outcome
is that a programmer using TDD would go faster to any given green
bar, because they never write a line of code prior to that moment
that they do not need.
"If each refactoring happens at the earliest necessary moment, it
will be quite small, and generally the IDE's refactoring tools
will do it in a few keystrokes. The TDD pracitioner starts out
ahead, by working simply. They might stay ahead forever, by
refactoring promptly, always ensuring that they have the minimal
code for the moment."
Now Gil is pissed off.
"You're saying that you can design better than I can using this
stupid TDD way and that you're smarter than I am because you can
design and code at the same time. I can design your pretty ass
right into the ground."
Kate says,
"I'm not saying that. I'm saying that I'm pretty good at design
and pretty good at TDD, and that for me, I go faster with a bit
less up front design than I used to use. That lets me hit the
ground running -- if I were into running which I am not -- and
then my simultaneous use of the design and building of the design
gives me better results. I get faster feedback about how good my
design is because I use it sooner.
"I'm totally sure that if you had exactly the same design skill
that I have, and exactly the same TDD skill, you'd get the same
results. But of course our skills aren't quite the same.
"If your design skills really are substantially better than mine
-- and thank you for the complement about my ass but I'll stay
above ground for a while longer if you don't mind -- then even
with my head start getting to the first green bar, you might catch
up by the final green, through saving all the typing I do to
refactor. Depending how quickly you could get the first up front
design right, or how quickly you could go back and forth between
paper and code, you might come out ahead.
"But I do start sooner, and I switch to paper less frequently.
This gives me time to do a little more typing. And, just possibly,
because I always build the least code I can to do my current job,
I might inject fewer stupid coding mistakes, or discover them
sooner because my TDD tests tell me. So I have less switching
overhead, and maybe less debugging overhead. I might stay ahead
forever because of lower overhead.
"Maybe not in this case, since you are a god among designers, but
in many cases it's also possible that I do design just as well as
the next person, but that I'm somehow more facile with TDD than
they are. If that's the case, again I'd stay ahead forever, simply
because I start sooner and don't slow down due to under design.
"But honestly the comparison of your design powers with my silly
way of doing things doesn't matter to me. I've studied design more
than most people and done TDD thoughtfully for a long time. For
me, it works best. I've seen it work best for a lot of other
people, and my intuitive feeling is that unless someone designs at
some incredibly high level, and is perhaps also really slow at
editing ... well anyway, my feeling is that for people about like
me, TDD will work better when they've come to use it as I do.
"But that's just my feeling. I'm just saying that it works for me,
and I have a pretty good sense of why. I wanted to share that with
you. Do with it as you will. It makes me better to work the way I
do, and that's why I do it."
Gil says, "Kate, you argue like a demon from hell. But I'm still not
convinced TDD is better for me."
Kate replies,
"Thank you. I am the descendent of a long line of litigators, but
I decided to get honest work instead. And I don't know whether TDD
is better for you. Everyone is different. All I know is that I
have worked long and hard learning design and becoming facile with
TDD, and it's better for me. I know a lot of people who have done
the same and had the same discovery.
"Maybe you're past that point and you are different. Or maybe that
discovery still lies before you. It may not matter at all. Your
work is good, and fast. Learning to shift more of your time from
paper design to TDD design might make you better still, or it
might not. Maybe you'll find my experience tantalizing enough to
push further to find out more about what's true for you, or maybe
you won't. Either way, you get to decide."
Ron Jeffries
www.XProgramming.com
To be on the wire is life. The rest is waiting. --Karl Wallenda
> It shouldn't be that difficult to work out the intent of the code because
> the unit test should describe this.
The tests describe /what/. Intention is about /how/.
> Customer value is always a difficult subject with refactoring. It would be
> nigh on impossible to express the customer value of an individual
> refactoring effort no matter how soon it is performed.
Yes. The business value of a post-hoc refactoring effort is very
difficult to identify, primarily because it delivers no feature
value now and only promises (or hints at) faster feature development
later.
The best option is to have the code's design always just as good as
the present level of function calls for, neither too much, which
reflects wasted (not yet paid-off) effort, or too little, which
slows us down. Of these two, a little over-design is far less
harmful than insufficient design, because usually (for some
definition of usually) it will pay off pretty soon. It's possible to
create amazing structures that are unnecessary forever, but a
customer's impatience for stories can probably hold that off.
If, however, we give our code to the customer with an inadequate
design, we are developing the habit of handing it over too soon, and
we are slowing ourselves down, step by step, for the future.
Cleaning dirty code up is something that we need only do once.
Working with dirty code slows down every feature that passes through
that area, forever. So keeping the code clean is cheaper overall
than letting it slide.
> The challenge is
> harness a relationship with the customer that means that they appreciate the
> overall value of attempting to create an environment where each developer
> will comfortably and confidently improve any piece of code that they come
> across without feeling as though they could cause damage to the overall
> system. I see TDD as a means to improve the synergy of the team in creating
> the simplest, most maintainable system.
Yes, TDD does that. And the best way that I know to harness the
ability to change the code as needed is to clean up the code we're
working on. If the design has slipped a bit in some area, then the
next time we do a feature in that area, we clean things up a bit.
It's like the boy scout rule of leaving the campground cleaner than
you found it. Over time, the areas we work in will become quite
clean, and if other areas that we never visit never get cleaned up,
no harm is done because we're not slowed down by them.
Ron Jeffries
www.XProgramming.com
For best results, avoid doing stupid things. -- Clifford Stoll (Acme Klein Bottle)
--
I don't think you misrepresented it at all, I just didn't think it was
sufficiently defined to help some of the less-experienced TDDers, like
myself perhaps.
> TDD helps me write maintainable code
> because it helps me keep the design simple. I can see how designing
> for maintainability could lead to over-designed code, but I would
> argue that over-designed code is less maintainable than simple TDD
> code.
Aside from the Martin's APPP I haven't seen much that details how to
balance simple design and adequate refactoring... but perhaps I don't
know exactly what I'm looking for either.
> You've made me start to contemplate the meaning of maintainable, and
> that's a good thing! Off the top of my head, I meant maintainable in
> the sense that it's easy to understand, fix, and extend. I guess that
> it conforms to the XP rules of simplicity?
The c2 wiki Minimum Number of Classes
(http://c2.com/cgi/wiki?MinimumNumberOfClassesAndMethods) quotes Ward
Cunningham:
"Better to create the one class needed now and let the future
programmer refactor it into the classes needed in the future. I am
suspicious of the one responsibility rule because I've seen beautiful
code that violates it."
> The reason we care about design is so we don't make a mess. We don't
> want to make a mess because someday we'll have to go back and fix or
> extend the code. I guess that's the goal? Don't make a mess?
Perhaps. Maybe the balance is being able to say, "this is fine for now
but we can easily refactor to XX if we ever need to do YY or ZZ." Of
course, fine means no/minimal duplication and all the normal things.
Thanks for your thoughts Curtis.
If someone came up to me, at a users group meeting for example, and
said something like, "I've heard about TDD and I'd like to practice,
but I don't know where to start."
I could say something like, "TDD is basically a three step process,
red green refactor, that is repeated until your problem is solved.
Red means don't write a line of production code until you have a
failing test. Starting out I would suggest writing small simple tests.
Green means write the simplest production code you can think of to get
the test to pass. For example, if your test is 'assert 4, foo.bar()'
then just have bar() return 4.
Refactor means improve the code you have so far, making it reveal its
intent, while keeping the tests passing. Just starting out I would
suggest focusing on duplication. Remove any and all duplication you
can find. For example, the 4 is probably in the code and in the test
and is duplication of information. It probably means more than 4. It
could be an id, or a ranking, but it probably means more than 4.
Refactor it out and make the intent clear.
Repeat until you've solved the problem you sat down to solve."
Is there a simple, for dummies, way I can practice what you are
proposing? You suggested red green {refactor red green} but I don't
see the paper modeling step in there. That's why I asked if the first
red green was on paper, since you said we should always model first.
Gleaning what I can from the mass of information in this thread, my
take so far on your technique is:
1. Solve the whole problem on paper/whiteboard using models
2. Refactor the models until they reveal intent
3. Code the model in the desired language.
Is that close?
Ralf,
I just want to say that I admire what you are doing here. It takes courage to stand up for a controvercial position especially when you are faced with so many experienced practitioners.
Most of the arguments I have heard against TDD are either of the "it's too slow" or "it will never reveal an optimal solution" variety. These are easily defused by anyone with the experience to do it well.
The argument you are presenting seems different though. I am not sure I have really grokked your position yet, but I am glad you are offering it. We need these kinds of debates so that we can continue to move the state of the art forward and not rest on our laurels (*not* to suggest that anyone here is doing that.)
Just to be clear, is the essence of your argument here that TDD refactoring does not necessarily lead to a more intent revealing design, and that we can waste a lot of effort refactoring in the wrong direction?
This is true, of course. When I learned TDD I was told to always refactor with the intent of conforming to Kent Beck's four rules of simple design. Thus, if I wasn't refactoring to express intent I was doing it to eliminate duplication. And if I wasn't doing that, or adding new behavior test first, or deleting code I didn't need, then I shouldn't be doing it.
Is that different from what you are saying? If so, how is it different?
i think part of this is what programming language you use -- the
faster it lets you get code done, the easier it is to use code to test
things and yet not be rat-hole-ing and wasting effort. if you are in
some bad morass tar pit of a programming language (and you probably
don't even realize it, until you've tried other setups and given them
quality learning time) then that's a drag, and might push people away
from using code as a tool to face reality.
sincerely.
A man-week of work is not a refactoring. It might be a restructuring,
but in my experience it's more likely bug-fixing. People call almost
any change to the code "refactoring" these days. That doesn't make it true.
> I've always seen intention as being the /what/ myself so this might be the
> source of my confusion. As in "What is the intended purpose of the code?" As
> I see it if this cannot be determined from the tests then something is wrong
> with the tests. This is what I meant from my previous post. I'd rather not
> shift this thread to a discussion on the English language as the thread
> seems to have covered a lot already!
I'm kind of regretting rising to the what/how debate. We can
certainly think of it, then, as a different level of what:
Acceptance test level what: Given the user's name, return the
corresponding account.
Now we decide to implement this with a binary search. Somewhere in
the code we have a method
findAccountGivenName(string name) // this, too, is a what
SearchPackage.binsearch(accountTable, nameOffset, name);
// this is a "how" with respect to findAccount, at least as
I'd say it, not a "what". Of course it says what we are doing,
binary searching, and / but it answers the question "how do we
find the account?"
Strictly speaking, I would agree that when I write a line by
intention, I am saying /what/ I, the programmer, want to do next. The
lines of intention, in a row, explain /how/ the overall method
works.
> Otherwise I think that everything you have replied with is a better version
> of what I was trying to say!
Still not the best, obviously. :)
Ron Jeffries
www.XProgramming.com
Fear is the mindkiller. --Bene Gesserit Litany Against Fear
Very nice description, Esko. You should publish this somewhere more
enduring than a mailing list.