Tuesday, 4 September 2012

SOLID principles and TDD

This post has moved to http://coding-is-like-cooking.info/2012/09/solid-principles-and-tdd/

21 comments:

J. B. Rainsberger said...

"It's less help though if you don't know what a good design looks like in the first place." We can't agree on what good design looks like -- several discussion threads on various mailing lists have shown that. I think it's impossible for someone to refactor code with absolutely no idea whether the result is good or not, because in order to want to refactor code, the programmer would generally need some concept of good and poor design. I have both witnessed and experienced incidents of refactoring, having a certain unexpected structure emerge, then realising what's good about it without having seen that pattern before. In particular, this led me to question my previous ideas about data hiding/encapsulation.

I'm not sure which claims I go too far in making, but I try to teach that TDD can help the programmer deepen understanding of the principles you've cited, which assumes some prior knowledge of them. I believe that TDD can help the programmer uncover these principles, which assumes access to someone who can say, "Why yes, that's DIP you've discovered. Let me tell you about it." I've seen both numerous times.

Emily Bache said...

J.B. - In what was, admittedly a rather short session at XP2012, I felt you were making some rather bold claims. Your comment here is substantially more nuanced :-)

I think we both agree that doing TDD can give you design insights, and that learning design principles will help you to do TDD, particularly the refactoring part. It certainly helps to get feedback on your code (teaching!) from someone more experienced.

I'm skeptical that there is some kind of shortcut where you can produce a good design by only knowing and applying a couple of simple rules. Good design is hard.

Wojtek said...
This comment has been removed by the author.
Wojtek said...

Great post.

I had similar experiences.

When I learned about SOLID I wanted to do it. It was very hard in the beginning. I ended up following simple TDD rules to come up with code that was more likely to be compliant with SOLID rules. Then I saw some patterns emerging. Now I am more often sure in cretin situations how to write SOLID code, TDD taught me that.

http://test-driven-development.com/2012/04/09/tdd-is-like-cooking-hehe/

Luca said...

I take this opportunity to ask you a pair of questions here and see what's your opinion based on your experience.



Suppose that we all 3 agree that SOLID are good design principles.
And then we talk with real teams in organizations, we look at the team members that better know about SOLID, we talk with them and we discover that many of them have a good abstract understanding of the principles. When we show them real lines of code and we ask them to spot principle's violation (and so possible improvements) we discover that many of them spot very few violations of the principles and don't see even many of basic/major violations.

Question 1) In a context like this don't you think that even few of the basic practices required to make the code GOOS testable like
- passing collaborators and dependencies via the constructor instead of internal instantiation
- extracting and reference interfaces of dependencies & collaborators instead of concrete types
- breaking down big classes with too many responsibility;
can produce a visible and tangible improvement of the design?



Question 2) given that writing unit tests is another way to understand and reuse in isolation small parts of the code (that is the same goal of good OO design) don't you think that the effort of writing good test & testable code itself points to the direction of finding/discovering what good design is for the code one is testing ?

Wojtek said...

Luca: I think you answered your questions already.

Ad. 1: There is that. There is also a lot more that can be done. Following SOLID, etc. will pay in future. Just remember about making informed decisions.

Ad. 2: Yes. You can reach a point where you don't need to drive your design with unit tests. But it takes time :) And if you work in the team you are not on your own, there is always somebody that is less competent :)

Emily Bache said...

Wojtek - thanks for the link to your post. I'm glad I'm not the only one who's noticed coding is like cooking :-)

Emily Bache said...

Luca - about your questions

1) That practicing GOOS TDD leads to a SOLID design seems like a reasonable working hypothesis.

2) testability and good design are closely related, so if the code has unit tests it will naturally have some desirable design characteristics. I don't think TDD is the only way to reach a good design though.

tab said...

Quite valuable thouhts outlined in depth (as we know it from you ;) Thank you Emily!

When you write

He protested that he shouldn't have to change the production code in order to get it under test, then left. That was the last time he came to me for advice.

I was reminded of Jurgen Appelo's post "The Trojan Form of Change" this week:
http://www.noop.nl/2012/09/the-trojan-form-of-change.html
Unfortunately too often we don't have the time or capacity to help sb. going into deep. But the message is clear and this should be our everytime goal:

inject an idea virus in a traditional organization

Although this advice is valuable it is only half of the story: it talks about the WHAT not about the HOW.
Yet, may we all keep going!

J. B. Rainsberger said...

Emily: Odd, since I've been practising and delivering those claims for years, that I wouldn't have delivered them the same way at XP2012. I don't remember enough to be able to say one way or another. I believe that one can reduce the SOLID principles (among others) to some linear combination of removing duplication and improving names, which explains why I focus on those two techniques.

Your skepticism, I conjecture wildly, assumes that it's possible to remove duplication and improve names diligently and not notice what's happening. I think you'd have to work hard to avoid doing that, and much like the kid who decides to cheat by writing down all the answers to the test on her shoe, some of the right answers sneak into her memory.

It usually goes like this: someone tries to explain SRP to you, and you just don't get it, or you get a very superficial understanding of it. You focus on removing duplication and improving names. Without actually /trying/ to understand SRP better, you will. It's the "Karate Kid" effect: when you practise the moves, even though you don't understand their larger purpose, you build the ability to see the larger purpose. Assuming you try to notice what's happening, the connection to SRP will happen.

I just don't think the amount of up-front explanation of SOLID is needed. Being extreme, I think we can get away with none. :)

J. B. Rainsberger said...

Luca: To (1) I answer "yes" and I usually teach removing duplication first, because it encourages people to break down large structures. To (2) I answer "absolutely yes" and I'm surprised I haven't emphasised that point in my workshops before. Thank you for that.

Roberto Guerra said...

I agree with Emily that some claims are a little exaggerated. I started doing 'TDD' before I knew or understood SOLID. When I look back at those tests, they were just plain wrong. I would try to use some testing tools that would do crazy stuff just so I could test my code. I wasn't thinking about design nor SOLID. After I read about SOLID(especially after watching Uncle Bob's screencasts) things snapped in my brain. I started applying those principles in my tests and my code started looking better. It wasn't the other way around: testing and then SOLID appeared.

But that is just my personal experience.

Alcides Flores said...
This comment has been removed by the author.
Alcides Flores said...

Hello everybody.

In this topic about TDD & SOLID design principles, I found the arguments of Mark Seemann here to be very interesting/illustrative as he compares TDD against each one of the SOLID principles to see how much TDD really enforces them.

Also I think the results of the mentioned experiments/incidents are not definitive and far more detail and research in the field is needed.

Greetings.

Emily Bache said...

J.B. You're saying basically that you when you teach TDD you concentrate on these two simple ideas, and that people pick up SOLID along the way. That sounds reasonable. I'm sure you teach TDD very well and naturally teach design at the same time.

What I react to is the claim that you can get "architecture without trying". Good design is hard.

I count myself as reasonably proficient at both removing duplication and naming, and yet I didn't do all that well on a couple of Luca's exercises. I don't think concentrating on only those two practices is sufficient for someone to learn good design.

Emily Bache said...

Alcides - Thanks for the link to the article. Mark Seemann concludes that TDD doesn't encourage a SOLID design very much. I think what Luca is arguing is that if you use the London School of TDD it encourages SOLID a lot more than with the standard kind of TDD that Mark is referring to. I think Luca's arguments have merit.

Luca said...

@Alcides


I exchanged some mail with Mark Seemann about the post you linked and the experiences documented by me (at the link to github posted by Emily).

Here two excerpts from Mark's mail :
1)"I’ve seen many times that teams generally just falter and fail with TDD. Their test code base becomes harder and harder to maintain as time goes by, until a crisis emerges that causes the team to finally ditch the tests."


2) "To succeed with TDD you need a critical mass of skilled programmers. That could actually form an interesting hypothesis in itself: Given a team of inadequately skilled programmers, TDD will not yield any improvements, but given a critical mass of adequately skilled programmers, TDD will yield the effects described in your paper."


This is congruent with the quote from Steve Freeman in Emily post:
"No tool nor technique can survive inadequately trained developers"

Indeed no one is claiming that the practice of TDD can force an inadeguately trained and skilled dev team into producing better design. Nor training on design theory could either.

Instead the original claim that is congruent also with Mark Seemann point of view is that skilled programmers can improve the design of the code they produce more with adeguate training and practice of TDD then they can just with studing and training of design theory and principles.

So to be precise, if we rate design quality from 1 to 10, looking among many real dev teams in real organizatons that are better at design the majority of them are at level 3,in this case proper training and practice of TDD can take them from 3 to level 6.

Now if we name good design as between level 8-10, extra theory and then extra practice is needed to achieve good design. TDD practice is more effective as initial improvement (3->6), while the theory helps better after the practice provided with TDD.


What is surprising here if you want, is that the dev team that are better in design on average are at level 3. Their ability to discuss and argue about theory of design is only partially reflected in the code they produce.

Dieter Cailliau said...

Static helper methods hard to test? IMO static methods are the most decoupled code blocks possible. No dependencies at all. It's static state that should be avoided at all times.

Emily Bache said...

Dieter - it's not that static methods are hard to test by themselves. What is difficult is if you have a normal method that you want to test, that uses the static one. It's hard to change the behaviour of the static method for the test. Take a look at the TurnTicketDispenser problem for example.

Roberto Guerra said...

I probably did something wrong, but in the TurnTicketDispenser example, I didn't have to change the static method. In java there would be more restrictions, but I didn't find that to be the case in Python. I can still initialize the sequence generator and that instance was able to call the static method. Something that you can't do in Java. That is why I didn't change its behaviour in the python exercise. Or maybe, as usual, I did something awfully wrong.

Fredrik Wendt said...

I'm a little late to the game here and I really only have one side note: Yes, dear Emily, having done a couple of sessions of programming with you over the years, I absolutely feel that you're coding style is heavily influenced from your background of coding Python. :-)
I've yet to dig into C# code world, but comparing what I've seen in Java to that of Python and Javascript, the way you're forced to expose what you want to access in Java makes you look away from exposing "state" just to make it testable. This is also one of the reasons GOOS or outside-in (I've referred to it as mockist vs stubborn) makes much more sense to a programmer used to a statically typed language with "real" hidden member fields, I think.

I always stress that the last D in TDD is Development (as in implementing a system), not Design. TDD is not a silver bullet, it's not some magic you apply to a junior developer and out comes flawless design and error free code.