|
A few years ago this comment would have made me facepalm, bang my head on the corner of my bulky CRT monitor and look for the nearest ice pick to stab through my eardrums. Kind of like I did when I read the transcript of Joel and Jeff's reaction to Uncle Bob and SOLID. These days it just makes me sad, for two reasons and not (probably) the ones you think. It makes me sad mainly because, well, in a LOT of ways you're not wrong. You can live (and live, and live) in a comfortable bubble between the time a new codebase is started and the time technical debt settles in and makes new features of nearly any kind far more expensive than they're worth, for an entire career. You can deliver a lot of software that way. You can also develop some pretty good habits (as they did in the days before TDD frameworks) around manual testing and general experience, that mitigates the onset of technical debt and bad architectures. The further down that path you go, the better you get and the more successful projects you complete, the less TDD will seem like a Good Idea, and more like, as you put it, someone trying to "sell a religion." I've been in the industry 15 years, and I've worked with a lot of guys like that think like this and gone a long way down that path. Probably like you. Some really, really smart guys that I've learned from and admired. Some of the smartest coders I've worked with. They had unreal IQs and analytical strengths that always knocked me out. They could crank out features very, very fast. And they wrote some of the worst code I've ever seen. Not dumb code. Bad code. Awful. A trail of debt to make the heart sink and the eyes roll back: long, 300-600 line methods, inlined sorting algorithms because maybe they thought built-in Hash's search was slower than they could write themselves. Early denormalization. Unclear intentions, poorly named methods, and (here's the overall characteristic): non-idiomatic code. Code that doesn't follow conventions, code that is hard to read, impossible to reuse, obviously unclear in its original intent, and therefore brittle, brittle, brittle. The business consequences in specific instances were more than tangible. Six weeks for a refactoring here. Months added to timelines for new features there. Features actually abandoned. Millions in revenue lost. There has been an uncanny relationship between these kinds of coders and a common theme of resistance to the following ideas: TDD, SOLID, Design Patterns. The same guys who leave a trail of maddening debt (all the faster because they also tended to be some of the smartest coders on the team), also tended to have a deep suspicion of "high level" thinking, frameworks, big-picture conventions. And always, always, the same old red herring, false dichotomy you present here: "Do you want to ship code or write elegant, well-tested code?" As if the two were opposed. As if the two weren't actually directly related. Here's the thing: they had a point. The inverse errors -- over-architected, over-tested, over-thought code can be just as crazy-making. Some junior dev who just read Design Patterns in 2005 and thought they'd Seen the Light and starts spraying Bridge Patterns all over the codebase was just as bad as the aforementioned analytical, structured-code debt machines. I can only go on what I know, and here's what I know: without exception every guy I've worked with that thought this way -- suspicious of TDD, plays "shipping" against "tested/craftsmanship" -- also created code that in the mid-to-long term cost the company far more money than they apparently saved by "shipping fast" and writing untested, unclear code. The most frustrating thing to me about this is that, once you integrate these practices -- TDD, patterns, SOLID -- into your workflow, once you master them, they cost nothing in terms of time spent. You get practical about it, use it judiciously where it makes sense, don't use it where it's unnecessary and (like a lot of the Design Pattern stuffs) ignore it altogether. But you can only do this if you can see both sides, and you can only see both sides if you've mastered the practices. I spend much less time writing tested code than I would without it. I spend much less time managing well-designed codebases than I would thrashing around in a tangled mess. It's like learning Vim enough to get proficient. You have to commit to it. Then you get pragmatic about it, learn its strengths and weaknesses and over time get to know the real benefits. But you have to have that willingness to commit to it up front and anticipate the benefits down the line. And as an aside, in my 15 years, the great coders I've worked with (a few name-drop-worthy dudes in the mix) have, without exception, been committed to a pragmatic, healthy approach to TDD that was neither religious nor slow, and in fact made the entire team faster, the codebase more robust and the software far more shippable. And while I have run into a lot of guys who have never written a single test - much less mastered TDD to the point of being able to speak intelligently about the pros and cons from a standpoint of actual experience - declare loudly and confidently that TDD is "religious" ("snake oil" is another favorite) and "feel free to ignore", I have never run into an experienced coder who has integrated TDD into their workflow in a credible way, who actually can speak to both sides of the argument say the same thing. |