Three Months Later, How’s it Going with TDD?
TL;DR: Slowly. It's going slowly. But it's awesome.
If you're actually interested in the details behind the above statement, read on. I have precious little time to devote to tinkering on my side-project blog, and I want to share what I've learned with you.
Learning from the Masters
First, I've been guided in my efforts to apply TDD to my Javascript coding by James Shore's excellent webcast. I have tried, in my own coding, to weave a path in between slavish devotion to his every choice, and complete disregard for one of the best JS tutorials out there. I have created a small NodeJS server application (thoroughly tested, naturally) that can serve a web page. I have set up a small client-side application that uses Karma to automate browser testing. I have avoided using frameworks as much as possible — though I make an exception for Express, as I don't have anywhere near the level of technical chops to build out an HTTP server from scratch. For the most part, in what I describe above, I'm not really innovating over the path Shore sets out for me. I'm simply following the footsteps on the floor.
Here's where I have decided to part ways from Shore's technical choices:
- Typescript. I spent a whole year of my professional life suffering through Javascript's everything-tastes-like-chicken approach to type safety, and I have no problem chewing my arm off to get out of that madness. I understand Shore's hesitation about using transpilers, but pure JS is a train wreck of a language. Don't get me started on function-based scoping or "truthiness." If something like Typescript makes my life easier, I'll take it.
- ESLint and Prettier. I've used them, love them, and trust them. Sometimes the choices are just that easy.
- Using Promises. Shore created this series many years ago. He expresses distrust of JS Promises, but then he falls into callback hell quite a bit more than he should. Promises are one of my favorite new parts of JS. And they have the not-to-be-underestimated side benefit of turning what could be a non-deterministic, race-condition-prone test into a controlled, predictable flow of logic. When I see something that works better than promises, I'll use them. Until that bright, shining day, you can pry them from my cold, dead fingers.
- JQuery. Again, Shore made these videos many years ago. People still use JQuery for DOM manipulation, but I don't intend to. It's slow, outdated, and as far as I'm concerned, obsolete.
Here's where I was surprised I agreed with Shore:
- Jake. This is probably the biggest surprise, one that bears some explanation.
A few years ago, Grunt was the obvious build tool for JS. Then people got disgruntled (as it were) with it. I can see why this happened. Grunt is kind of opinionated in how it works. It is highly configurable, but if you can't find the exact plugin you want for it, you had better be prepared to simply roll your own. Not a difficult task, but it feels like it should be easier.
So the bulk of the JS world, it seems, moved on and adopted NPM as the build tool of choice. I love NPM for its dependency management, but depending on it for builds seems to me to be a very, very bad choice. All you can do with NPM is define a script for your build. I'm used to tools like Make or Ant, where you define a small-focused task, and then build up a set of dependent tasks for what you want to accomplish. For instance, you need your compilation step only once, and you can re-use it in both your dev and deploy scripts. NPM, on the other hand, requires you to import one script and has no build-level dependency management at all. This means that tasks that ought to be flexible, modular, and well-defined, are anything but.
I balked at this decision as long as I could, but I've been pretty happy with Jake so far. There is very little configuration needed. Tasks are written in pure Javascript. Dependencies are transitive, and easy to define. I may still go back and re-attempt Gulp, but at the moment I don't see a need to.
One big caveat: Jake does yet not support JS promises. This is a huge miss, though not one that has bitten me yet. If this becomes an issue, I may revisit my decision and re-implement my build in Grunt.
How TDD has Changed My Life
I can't stress enough how much easier TDD makes things. As I mentioned above, my coaching duties leave me with precious little time to devote to side projects like this one. When I have to spend a week or more away from the coding keyboard, the TDD style allows me to get back on track immediately.
I deliberately took on some technical challenges on this project simply so I could learn parts of the JS ecosystem that I have not yet had time to wrap my mind around. In particular, Typescript is a new horizon for me. TDD allowed me to specify one and only one problem to solve, and left behind both a test to ensure that particular problem remained solved forever and documentation about the solution I used. Things are going slower than normal, but I have also learned more than I normally would, because I am no longer trusting someone else to understand the ecosystem for me. Rushing a solution out the door often results in tech debt accumulation, which slows you down later.
I'm looking forward to extending my knowledge to the sorts of DOM manipulations and animation that my current Gatsby site is managing without much of a hassle.
Stay tuned for more!