Subtitle: and talk about it UNprofessionally
I’ve been writing code for about the last decade and a half. It really wasn’t till I started working on a team of developers that I was pushed to write better, more readable code — a very important lesson. Then, it wasn’t till I started breaking old habits and started writing in a test driven development (or TDD) fashion that I started to write professional code.
Some of you might be thinking “ugh. another tdd fanboy.” — maybe. To each their own and I’ve found a lot of success using this method. So, I’d like to try and enunciate some of the reasons why I choose TDD these days. Feels like the perfect first post for my “Code” topic.
For all those moments when you approach that place in your code where you need a thing to do stuff (as a beloved colleague of mine might say), here are my reasons why you want to start with your tests first!
Reason #1: Code Flow for Contracts
In order to write testable code, it helps to get into the habit of writing functions or methods that take input, and give output. Simply put: IO. When your code starts to do more of this, you’ll notice that your functions become smaller bites, instead of large, procedural-like code that is conveniently encapsulated for no-reusable-reason.
Think of it like this: I give you this, you give me that. We now have a “contract” as to what we can expect from each other. Once we have that down, I can start throwing everything (even the book!) at the function or method to test how it handles any given situation I intend to put it through.
Now is where we both ask ourselves: “but what about the times where it’s getting something we don’t intend it to ever handle?” Great question! I’m glad we asked it. Well, to be honest, that happens. Most people call that a bug. When you come across those pesky bugs and you’ve found a scenario you didn’t anticipate, figure out the cause, write a test for how it should have worked, THEN fix your code.
Now you’ll know if all of your scenarios are still passing =]
Reason #2: Testing for a Known State
Knowing if all of your scenarios are still passing is a great dovetail into what I like to call the “known state”. Having a known state is like pouring some of that sweet sweet “left field prevention sauce” all over your code. Now watch how often changes to your code cause fires in other parts of it. Write tests to acquire a known state, then continue building, modifying, and expanding your code base from that foundation.
The known state has another healthy benefit as well! Not only does it help you recognize impacts of your changes that you didn’t expect (ie: bug prevention!), it helps you understand the changes that you are making. In other words: when your results are different and your tests flag them as failures/breakage, now you can ask yourself if the changes are what you want to happen.
This is great because now your breakage can be an intended artifact of progress and you can verify that those pieces are happening correctly. Once you’ve broken things to your desired effect and the new tests are passing, get rid of those no-longer-relevant tests and move on!
Of course, we wrote tests for the new expectations before we started modifying code and breaking old tests, right? 😉
Reason #3: Minimum Feedback Time Reduced
This is one of my favorite reasons for doing TDD. What do I mean by Minimum Feedback Time Reduced? I simply mean: “How long does it take before I know that my change worked?”
Let’s take for example a scenario where we’re going back to old habits. We’re writing a script that is largely procedural code. You just give it some arguments and run it from the command line or hit refresh in your browser and it starts from the beginning, chugs through all the things, and once it completes its task, gives you the most amazing output ever. Blah!
What if this script takes 30 seconds or more to run? What if it’s making API calls and you need to see if you’re correctly handling the payload now? Do you really want to start from scratch just to see if the newly formatted white space in your output melts face?
Here’s a hint: no. No you shouldn’t. Not now, not ever. There’s a better way, I tell you! TESTS!
One of the greatest things about tests is that they allow you to get pinpoint accuracy on the code you’re working on. I switch to my command line (or some IDE’s have it built into a widget in the same window), run my tests, and I know in a fraction of a second if what’s happening is what I intend. No more doing everything from the start, just so I can see what one piece at the end looks like. Hence, I tackle more because I tackle faster.
I realize I’m about to break the rule of threes here, but I just can’t help myself! So here’s bonus reason #4… it’s pretty epic though.
Reason #4: Don’t Climb, Break the Mountain Apart and Walk Over
This is about to get weird, but I like this analogy, so I’m sticking with it. Let’s say writing software is kinda like boxing; each new problem you have to solve would be like the next opponent in your upcoming fight. Most problems we face as software developers are about as intimidating as the Olsen Twins back when they were in the original Full House. Suffice to say, the vast majority of hurdles we face in software engineering are relatively trivial.
Yes. I basically just said I could beat the s!@# out of the Olsen Twins when they were 6 yrs old. Yeah baby, check me out! Ryan points to the meek and relatively unimpressive nature of what he’s (ironically?) calling “the gun show”. Eh, let him have it. He’s all smiles and impressed with himself right now.
NOW. Let’s say we’re about to embark on a new challenge filled with unknowns, difficult concepts, architectural challenges… the kind of fight that’s going to be on pay-per-view. I would liken this to strapping some gloves on and stepping in the ring with Mike Tyson. I don’t care how hard I try, Mike would knock the living snot out of me… I’d say within the first 3 seconds. Unless I ran… another story for another day though.
Here’s where TDD helps me out.
Unlike boxing in real life, tests allow me to break Mike Tyson down into smaller pieces. In other words, if Mike Tyson could only fight with a finger at a time, or just his nose maybe — and I didn’t have to think about any of the other challenges he poses as an opponent, I begin to swing way above my weight class.
No longer am I worried about the innumerable unique ways this application could start to spiral out of control. That kind of distraction is asking for a knuckle sandwich! Nope – instead, I write an easy test for a small, sectioned off, minute piece of the problem to demonstrate my happy path. THEN I write my code to satisfy JUST THAT — nothing more. Eventually, one finger, nose, eyeball, ear at a time, I beat the s!@# out of Mike.
Feels pretty good knowing that in some ways, Mike Tyson never stood a chance facing me.
Take it from me kids, get swole on TDD.
Now if the Olsen twins were using TDD—–heh ok ok, I’m done! =P
And That’s Why
If I’ve left you with nothing more than “Ryan likes Turtl–uhhh TDD”, ok. But when I start to think of the benefits of TDD — the contracts, the known state of an application, the speed increase, and the ease of development, there’s no way I’m going back. As I look back to some of my previous works, I’m kinda surprised I ever got anything working. It’s kind of amazing, really.
I hope this gives someone out there a little perspective or motivation to give it a try if they’re not already sold!