Back at NDC in June, one of the talks I enjoyed most was Fred George’s “Microservice Architecture”. It focused something I already believed in (deploying software in small, chunks that are as isolated from each other as possible).
But Mr. George goes way further than that. He believes in disposable software. Pieces of functionality that are small enough that they can be thrown away and rewritten. He talks of software having a lifespan of weeks, and not months or years.
He believes in well-defined interfaces between these small “services”, but there are few rules for what goes on inside. If a developer wants to fix a bug in an existing service he can. If he wants to throw it away and re-write it, he can. If he wants to use a completely different programming language, he can.
In the course of explaining the architecture, Mr. George said something that knocked me back a bit. He said that his architecture reduces the need for unit tests. TDD didn’t seem to be part of his approach.
This came hot on the heels of Dan North’s “Decisions Decisions” talk, where he pointed out that there are no sacred cows, everything is a trade-off, even TDD. Two talks into NDC 2012, and I’m being talked out of using Automated Tests. My kind of conference.
So, this week I tried it. I tried writing code, with no automated tests. That part isn’t new, I do that all the time. What was new was I wrote the code with the intention to throw it away and start again if the need arose.
If I felt the design had gotten away from me, I’d throw it away. If I tried to make a change and the code was working against me, I’d throw it away.
It’s Groundhog Dev. Sonny and Cher sing “I got you babe”, the Visual Studio editor is blank and you start again, with a chance to do things a little differently, armed with the knowledge from the groundhog Dev sessions that have gone before.
So what happened?
I just build the feature. No TDD, no BDD, no automated tests of any kind. A little up front design, and then on with the show.
The End Result?
Not so good actually. At a certain point I discovered that I needed to get the Absolute value of a number, and there was no clean place to get it. If I got it too soon, I lose the sign, which I need later. If I get it too late an earlier calculation is wrong.
When this happens it’s the code speaking to you. Your design sucks. Start over.
Armed with what I’d learned from Iteration 1, I scrapped it all and started again. Still no automated tests, a bit more design up front to take account of some things I hadn’t realised first time through.
The End Result?
Better. The problem with the sign vanished. I’ve messed a little with F# recently, and there was a definite “functional” feel to what I ended up with. But, it felt a little over-engineered in places.
It worked though, which is kinda the point.
OK, there didn’t need to be an iteration 3. But something about iteration 2 bugged me. Those over-engineered bits. Surely the last thing that disposable software should be is over-engineered.
I have also been working with FitNesse more and more, and had tried and failed in the past to find a place for it. But this little project seemed tailor-made for it. It looked like it could be a perfect example of how FitNesse could work for us.
I started again. But this time there would be tests. Only FitNesse end-to-end tests, no Unit Tests.
The End Result?
To be blunt, astonishing. Now, there is a warning that this was my third time in a week rewriting the same piece of functionality, so any improvements I report in iteration 3 will be quickly dismissed as just knowing the problem better, but I don’t think that’s it.
Once I had figured out the way I could test the feature (which is by far the hardest part of using FitNesse, or any testing Framework) the process of writing the code flowed beautifully.
Even though I wasn’t TDD’ing at the Unit Test level, I still found I was working in small increments, I had less long hacking sessions. Less compile errors to work through.
The resulting design was much cleaner than either of my first two attempts. It was also more decoupled from surrounding code, which is one of the goals I had been shooting for.
The amount of code dropped substantially, all the over-engineered bits simply vanished. Five classes were effectively folded into one, with no loss of cohesion.
I suspect that Fred George’s approach to developing software is not something you can just ‘Do’, in the way I tried to. He talks about the well-defined interface between services. It’s clear there are techniques, standards and patterns that he follows that would need to be learned to be successful.
For me, the revelation was the value of tests. Even, or perhaps especially end-to-end tests. Developing a feature guided by end-to-end tests, I found myself energised in a way that I wasn’t when I was just coding to a design.
This may go against what Mr. George was trying to get across. I’m not for a second saying that what he proposes is wrong, or wouldn’t work. I’m sure it would and I’d love to experience it, someday.
For today though, for me, for the skill set I have, and for my temperament, tests are what work.
If you can find the time, and the right small discrete piece of functionality, I would urge you to try your own experiment. You don’t have to adopt the same approaches I did, but try something different.
Having developed the third iteration of this little project I tried a parallel run of iterations 2 and 3, just to make sure all was working as expected. They weren’t. I discovered one or two bugs in iteration two. For what it’s worth, it wasn’t the tests that I wrote for iteration 3 that eliminated those bugs directly. It was the significantly simpler code.