This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and many iterations to get reader feedback, pivot until you have the right book and build traction once you do.
Foreword
Justin Searls is what I like to call a testing brother from another mother.He works with the awesome folks at Test Double providingconsulting services for people doing Ruby and Javascript, with a focus ontesting. He and I have had some interesting discussions about testing in generaland I asked him to provide his perspective on the benefits of testing.
I first used PHP to build a web application in 2004. By that time, Id already been taught and had failed to learn the benefits of unit testing and even test-driven development. In this case, I just wanted to get my application written, with as few secondary concerns to worry about as possible.
Developing in PHP made me happy. Both the language and its community seemed focused squarely on helping me get things done, and they didnt impose an expectation that I adopt any highfalutin practices or conventions. As a result, I was iterating on a working application from day one.
And day one was hugely productive! The same went for week one. As it happened, month one was such a success that I had completed the minimum requirements of an application for which Id been given over 20 weeks worth of budget.
I soon discovered, however, that there was trouble in paradise. Slowly, adding small features had begun to take me significantly more time than Id been accustomed to large features taking. While Id previously been a beacon of workplace optimism and excitement, I noticed that I was spending a larger proportion of my day feeling confused and frustrated. I wasnt only dreading showing up for work, Id even begun to resent my editors application icon, because clicking it meant that I was in for a bad time.
The root cause, and its plainly obvious to anyone who might read my code, is that the source had grown into a tangled jungle of rogue mega-functions, replete with trees of towering switch statements and swamps of nested if-else constructs. The code lacked any rhyme or reason. It contained no helpful signposts to my future self. Concerning myself with the design of the code hadnt seemed necessary, because my intimate, almost instinctual knowledge of the code had made me so productive at the projects outset.
But at some point, the complexity of my applications code had increased beyond what I could hold in my brain at a single time. Prior to that point, I had been unfettered; liberated from formalities like up-front design or maintaining automated tests. Beyond that point, though, I could sense that my haste had cornered me into a dead end. The only question was: could I re-bottle whatever genie Id released to help me become so productive in the first place?
In the case of that application, the answer was no; I couldnt make things better. The drastic internal changes necessary to make the application easier to maintain would have required confidence that my changes didnt break anything. At the time, I lacked the expertise to write the automated tests I would have needed to gain said confidence to make any major changes. Ultimately, I relented and stayed on the path of most resistance, muscling my way through to the end of the project. Needless to say, no one was as impressed with my productivity during the final leg of the project as they had been after its glorious month one.
At this point, it might seem like I told this cautionary tale for the purpose of exhorting to readers, see?! Thats why you start writing tests for everything on day one! Because even if you feel great today, someday your application will explode with complexity and youll be miserable. I wont say that, however, because that would be a specious, dogmatic argument and it would oversimplify the world of rich and subtle challenges facing software developers. There are, after all, many ways to ship working software with acceptable maintainability; excellent software can absolutely be written without any automated tests at all.
So if testing isnt absolutely necessary for success, why is it valuable? The answer is that automated tests are an excellent tool for establishing tight, rapid feedback loops when creating and changing code. To explain, let me rewind to the beginning of my story.
I said that working with PHP made me happy, because I felt the extraordinary positive feedback of seeing something, albeit small, start working in the first couple of hours. Not only that, but I could get ongoing feedback that my changes worked as quickly as I could save a file and refresh a browser window. It was only once my application had grown significantly that the feedback loop had started to slow down a five second pause here, a few clicks to set up the app there, perhaps a few minutes to verify the change didnt introduce any bugs. What I eventually realized that I wanted was for every day to feel like day one of a fresh project; I wanted a rapid feedback loop to be sustainable for the life of the application.
In the past, I had tried building applications of a similar scope with other tech stacks, like Java, known for their long-term hardiness. But my projects never seemed to get off the ground. I failed in part because Id sink the first, crucial hours of my motivation into troubleshooting while setting up the recommended build tools and supporting libraries. And even when I managed to clear that hurdle, any sense of progress was stymied by the nagging doubt that my architecture would elicit the judgment of my contemporaries. Perhaps a more durable technology stack or application design would have made my rapid feedback loop more sustainable in the long run, but the initial short run was so painful that Id never find out what the long run felt like.
It turns out that a sense of progress is crucial to productive software development. Feedback, both positive (that worked!) and negative (that doesnt!) has a huge impact on the psyche of the developer. When people endeavor to build tangible things, they receive concrete feedback about progress constantly (e.g. the table has two legs, so Im about halfway done; this book introduction has 283 words, so Ive got a ways to go). But when building something as ephemeral as software, progress comes in fits and starts, sometimes to the point of feeling illusory.
The magic of unit testing, particularly in the context of test-driven development, is that it gives the developer the ability to control his or her own sense of progress. Traditional feedback requires our fully integrated application and our eyeballs to assert whether were on the right track or wrong track. Unit testing allows us to establish a feedback loop at whatever level-of-integration we wish (e.g. perhaps a bunch of objects in coordination, perhaps one function in pure isolation), so long as we can imagine and implement a way to assert working behavior that doesnt require our eyeballs manual inspection. In this way, even if were faced with implementing a solution of daunting complexity, unit tests can usually help us break the problem down in such a way that we can make (and importantly, feel!) incremental progress on our path to overall completion.