One of my new year resolutions is finishing notes on two foundation programming books: Clean Code and The Pragmatic Programmer. Both are dense but worthwhile reads. They explain concepts you'll likely carry throughout your career. I recently finished Clean Code's informative yet painful chapter on Unit Testing. As in, I started having anxiety-inducing flashbacks to a previous job. As I write this, I'm running on three hours of sleep, have bags under my eyes, and have consumed nothing but ginger ale and bread scraps to keep the shadows at bay.
Why, you ask? That chapter laid out how tests keep a codebase maintainable and flexible. I realized the horrors of my past had taught me the same lesson, and this chapter was confirming it all over again. Had I read this book before, I could have spared myself much pain and carpal tunnel.
As part of both my recommended recovery, and to help you avoid a similar path, I'm sharing that story here. I don't expect most people to run out and read Clean Code (though I recommend it), but my story has the same message. At its heart is a great irony in why I decided not to write tests, and how it came back to bite me in the brain stem.
So relax, pray, and enjoy!
At First, Saying No to Tests
A couple of years ago at one of my first jobs, my duties included building a new pattern library. It was for a third party to make small web pages for our main client. It would be a mix of old and new components, and my job was updating and managing the styles.
I was the only full-time programmer there most of the time I worked on it. I was balancing other responsibilities so my time was often limited. I needed the pattern library work to be fast, flexible, and maintainable. Adding an entire suite of tests seemed like a bad move. It'd be a new section of code to write, deal with, and worry about as I try to make changes. They seemed counter-intuitive and wasteful.
That was not the case at all, as I would soon learn.
The Growing "No Test" Rot
The first sign of my mistake came in about a week. We'd made some changes and sent the pattern library over. The third party found a couple of unexpected bugs and sent a report over so we could fix them. We shrugged and did so, thinking that was that. The next day we got another email about a few more bugs we missed popping up. This cycle repeated one or two more times before it finally stopped.
Then it happened again for the next release, but worse. People started to get pissed off. I started getting anxious.
Turns out there were some style leaks in our components. These were due to poor style architecture and how we nested components. In our pattern library, this meant every release guaranteed a few unexpected bugs. Often in ways that we could never predict.
The solution was terrifying: any kind of change meant we had to manually check each component. We'd need to check them on different browsers and old devices too for our client. There was lots of internal feedback and iterations as the deadline approached. I'd often need to check several times in one day.
This added up to wasting literal hours every week or day going through dozens of component pages. It was mind-numbing and tedious to the point where I was prone to silly mistakes or skipping items. Someone would see this and make me repeat it all, wasting even more time. Even after I'd packed up and was about to go home, sometimes I'd get called back. I'd have to do several more quick checks before I could drive away.
The depth of my mistake hit me when I realized there were days when I did nothing but monotonous testing. I'd set up several computers to mirror the library across different systems. I would run through everything, make a small change or two, check it again, and repeat all day. I was a programmer, yet was doing the repetitive, simple work I should be making a computer do. Something had gone horribly, horribly wrong.
The Ultimate Irony
After that, I saw the price I kept paying without tests.
I dreaded making any changes, no matter how small. But my job was changing the codebase by adding to it or fixing it, so my job itself filled me with dread. I associated all changes like this with tedious labor, not meaningful work. It was the same with bug fixes, so I had less incentive to fix them and more to let the code rot.
Having read Clean Code's testing chapter, the ultimate irony became clear to me. I skipped testing thinking it'd make the code maintainable and flexible. But it had the opposite effect - tests are what make code maintainable and flexible in the first place.
I could have made updates without fear it would break something else. The tests would catch it for me so I'd fix it, saving time and energy. They'd ensure everything operated at a foundational level. Even when things went wrong they would never threaten the app's core functionality. I could add new components safe in the knowledge everything else worked as expected.
These maintainability and functionality benefits outweigh the costs of writing and improving on the tests themselves.
How I See Tests Today
It's not like I don't still struggle with tests today. I work with both Ruby on Rails and Ember, both with different test frameworks. I'm competent with Ember, but there's still many concepts and tricks with Rails testing I'm learning as I go.
But more importantly, my challenges today are about the how of tests, not the why. No one needs to argue with me about why I should write tests, or remind me to include some (most times, at least). I learned that lesson before, and it stuck pretty hard because it hurt so much.
If you haven't started learning how to test your programs yet, I encourage you to do so. You will accept how important they are one way or another. If you can choose between learning this via a blog post or a painful career lesson, I recommend the first one.
Unless you want to be like me, waking up in the middle of the night, in a cold sweat screaming about form integration tests. Then who am I to stop you?
Top comments (8)
The irony, writing test woul make the development slow... What most fail to notice is that not doing so would make the process even slower in the near future.
Hey Max! Great article! Honestly, I'm going through the same feeling as you did. Can you recommend to me how I should get started with writing tests? If it helps, I am a Nodejs and Typescript developer. Thanks!
Well, it's tough to recommend any specific libraries since they can vary a lot and depend on personal preference. I do have some notes from the chapter in question that goes into more details about writing good tests, I think they'd be a good read for you - max-antonucci.gitbook.io/study-not...
The most helpful tip I found to be is getting set up so you can write tests before the code itself. Knowing the important elements to test and how encourages writing more testable code, since you'll adjust the new code as needed to fit the tests. Code tends to follow better practices this way, since clean tests encourage clean code!
If you want some frameworks that have great testing built in, Ruby on Rails and Ember.JS are my personal favorites. Most if not all of the setup is already there, so it's easy to quickly write your tests as you build out applications.
Cool! Thanks again! ๐
The only reason to not spend a lot of time writing tests is that you are an amazing coder, in that case you'll write all the tests much faster. Code without testing is incomplete code. Code without testing is like an UFO sighting swear to me that works, I still need proof. If the test is written after, during or before the code is, in my opinion, debatable and a matter of taste. But if is not properly tested is not finished, is not working code.
Very well said. Tests as evidence the code works is another great way to view them, since it gives an understandable measure of "ready or not ready" for everyone in the company. Leaving it to the engineer's word only risks damaging the relationship between departments when bugs inevitably slip through.
And I also like them to be a sort of documentation, I usually end up commenting more about the intent in the test than in the code; almos like a tour of the code in the tests.
Why just say what something do when you can show it and prove it in the tests; I'm starting with Rust and like Python you can add automagically some comments to the docs. Documenting and testing, two birds.
And you make sure is not stale, because those are tests that have to work.
I've never found not writing tests to be a problem. I've been a professional developer for 25 years and I've never used automated tests on any project