I’ve been watching the TDD debates with Martin Fowler, Kent Beck and David Hansson. There is some good material in there. It’s pretty cool watching these three. All three are developer heroes of mine. Unfortunately, there isn’t much of a debate.
Martin Fowler plays time cop and mediator but doesn’t speak that much which is a shame because I know he is a bad ass - and he’s got that cool accent. Kent seems to go with David’s flow. Kent even has an article on Facebook called Rest In Peace TDD.
There is no debate here. Where is the tension? The conflict? Where is the name calling?
I’m not looking for a death match but the real power players in this conflict are Robert Martin and David Hansson.
Let’s look at a brief history: the order of events occur in real time (not really, just been watching too much Jack Bauer on 24).
It all started with an article/talk that David came out called TDD is dead, long live testing. David states that he doesn’t test first and he is pretty content with his own style. Of course, David knows how to sell his ideas so he starts his article with a semi-offensive tag line.
Test-first fundamentalism is like abstinence-only sex ed: An unrealistic, ineffective morality campaign for self-loathing and shaming.
At the same time David also gave a great speech at RailsConf 2014 about TDD. David’s beef with TDD is that we loose architecture clarity when we start to break apart and encapsulate pieces of rails controllers.
In waltzes Uncle Bob whose reciprocated response attacks David’s claim as nonsensical and lacking credibility.
This forms into a sort of Pokemon battle where @dhh and @ub are hiding behind their blogs without any direct interaction between the two.
@dhh attacks | @ub defends | @ub counterattacks
This spawned a “debate” on Google Hangouts. But again, not really much debating going on here. Watch them yourself, gather your own opinions.
|Part 1||Part 2|
|Part 3||Part 4|
I don’t imagine many people care what I think. But what the hell, you’re reading this right? I agree with David on many things: especially on the points like code clarity & knowing the trade offs involved with TDD.
I work a lot with Laravel and I see the community diving into things like the hexagonal pattern and DDD. I’m not personally working on a large application like Facebook. I write at best, medium sized applications. My experience with DDD and hexagonal is that when done on smaller projects, it turns seemingly simple and harmless code into complex and over-engineered crap. I’ll say it again, code to crap.
I’ve done hexagonal layering and repositories. The real problem I was trying to tackle was code maintainability, project risk and developer sanity.
I’ve been writing code for over a decade. I get paid to do it too. I consider myself a professional and I don’t always test first. I also still haven’t found a silver bullet when developing though. Sometimes I prototype first. Other times I’ll write a test spec and then write code to satisfy that spec. It just depends on the situation really.
However, writing a test first ensures that I am writing an end-result I can feel confident about. But I don’t want tests to drive my design. Changes dictate my design. It is one of the most important factors of my design. That is not to say that domain is any less important but I like to follow this age-old principle:
Encapsulate the things that change. Let sleeping dragons lie.
Okay, that last part about dragons I just made up. If some code (the dragon) never changes, don’t write a bunch of indirection or abstractions around that code. Also a manual test would work just fine here. Anything else and it’s just making it harder man.
Here is an example of doing it in style. In Taylor’s laravel/cashier project he has a class called Invoice that can print off a pdf receipt of an invoice. However, one of the methods of an invoice is called getPhantomProcess which uses the external phantomjs binary file in order to create a pdf document. Is this bad design? It seems to violate open/closed and single responsibility principles. This code is less flexible because the only way to create pdf documents is through phantomjs. But notice Taylor made his code simple to follow, it’s elegant. He didn’t let tests dictate his design either.
Taylor could have injected in a dependency for Invoice that allows for a pdf to be created some other way. It certainly would have been easier unit tested this way. However, this part of the code is not going to change. Taylor is always going to use Phantomjs, so he is content. And thus, my point:
Everyone knows Taylor is an awesome developer. One thing that makes him great is that he is okay with breaking rules if it makes life easier. David, Kent and Martin all seem to be the same way too. I can agree with that. Make life easier.
I don’t agree that TDD is dead though. I think it has evolved. To know if TDD is dead, we should define it. Uncle Bob states three principles of TDD are:
- Write no production code, except to pass a failing test.
- Write only enough of a test to demonstrate a failure.
- Write only enough production code to pass a test.
The TDD principles are not the problem here. Notice these principles say nothing specific about your tests. It doesn’t say that you have to write tons of mocks and unit tests. TDD doesn’t say anything about your architecture or design. I like to interrupt these 3 principles slightly different, much like writing an essay article:
- Create an outline that satisfies my objective (perhaps my objective is to inform you about cows)
- Using the basic outline, fill in details with some paragraphs. (here is were I write the story)
- Reflect. Does the objective of the story and outline still match? (did I actually inform you about cows? Are there any tangents about unicorns?)
I think the way we test is evolving. For example, (imo) testing behavior in web apps is more far important than unit testing. Sure tests might run a bit slower but unit tests produce a far less valuable feedback loop than say an integration test. In fact, the most valuable feedback loop is a customer which assuredly won’t provide feedback in mere milliseconds.
Regression testing a web app seems to work well too, as I think David & Kent both touched on this point when they talked about. This is a grey area where TDD is evolving. If you are writing all your tests up front, then you shouldn’t have to write regression tests right? That’s not how it works in the real world. What is wrong with writing tests after the fact? Nothing. That one user, QA person or that jerk down the road is going to break something and then you’ll have to go fix it. If you have time you’ll write a test to ensure it doesn’t happen again. All this to say though, just because you do regression testing, doesn’t mean you should just throw TDD out the window. It’s not all or nothing here.
For modules (or that one hefty algorithm) that have simple objectives and interfaces (unlike the UX and presentation layer stuff then TDD makes a lot more sense. You start digging in to the units and approaching this with white box testing. That doesn’t mean you have to bust out Mockery and start mocking all over the place though. I’ve found that phpspec for creating doubles works well. However, if you start trying to mock large things like Eloquent, well phpspec fubars. This is just my own style though; I should get back on point.
I’ve found that using TDD used in the proper cases is bad ass and produces great quality code. Some proper cases
being re-usable modules, components, packages, frameworks, etc. An terrible time to use TDD is a throw-away prototype or when the problem domain’s architecture is very small or unclear.
So when it makes sense, write some tests, write some code, make sure tests pass. Rinse, wash, repeat daily. In the right context, there is nothing dead about the process of TDD. Don’t get me wrong, I enjoy watching Martin, Kent and David express themselves. Ever notice that Kent squints really hard when he is thinking? ++Geek awesomeness!
All this said, adding Uncle Bob to the conversation will make the debate better.