DaedTech

Stories about Software

By

Meetings and Introverts: Strangers in Strange Lands

I’ll admit as I type the first sentence of this post that I don’t know whether this will conclude a two-part “mini-series” or whether I’ll feel compelled to write further posts. But I wanted to write the follow up that I hinted at to the post I wrote about introversion for programmers (well, specifically me). Tl;dr refresher of that post is that social situations are exhausting for me because of their inherent unpredictability as compared to something like the feedback loop of a program that I’m writing (or even the easily curated, asynchronous interaction of a social media vehicle like Twitter). The subjects I left for this post were “Erik as a problem solver and pattern matcher,” consensus meetings, and two exceptional social situations that I don’t find tiring. The challenge will be spinning these things into a coherent narrative, but I’ll take a crack at it.

Whenever I look in my Outlook calendar and see something like “Meeting to Discuss Issue Escalation Strategy,” I am struck with a surprisingly profound feeling that life is filled with senseless waste, the way one might look in dismay at his sunglasses floating down a river he accidentally dropped them into. I see an hour or two of my life drifting away with no immediately obvious reclamation strategy. My hypothesis is that this is the sort of standard introvert take on what I’ll call “consensus meetings” rather than what many programmers seem to think of as a programmer take on them. As Paul Graham points out in one of my all time favorite posts, “when you’re operating on [a programmer’s] schedule, meetings are a disaster.” But I’m not really a maker these days anymore; for the time being, I’m a manager. And I still find these meetings to be a disaster.

Extroverts draw energy from social situations and become invigorated, while introverts spend energy and become exhausted. And, when I’m talking about social situations, I mean drinks and bowling with a group of friends. Introverts like me enjoy these nights but find them tiring. Now, apply this same sort of thinking to adversarial situations that are veritable clinics in bike-shedding. A bunch of introverts and extroverts gather together and set about deciding what the organizational flow chart of issue escalation should look like. Should it start at Tier 1 support and be escalated to the manager of that group, then over to internal operations and on up to Bill in DevOps? Or should it go through Susan in Accounting because she used to work in DevOps and and really has her finger on the pulse? Maybe we do that for 2 months and then go to Bill because that’s not really sustainable in the long term, but it’s good for now. And, we should probably have everyone send out a quick email any time it affects the Initrode account. And… ugh, I can’t type anymore.

So here sit a bunch of extroverts and me. The extroverts love this. People in general love having opinions and telling people their opinions. (I’m not above this — you’ve been reading my rants here for years, so there’s clearly no high ground for me to claim.) But it’s the extroverts that draw energy from this exchange and work themselves into a lather to leave their marks on the eventual end-product via this back and forth. The more this conversation draws on, the more they want to interject with their opinions, wisdom and knowledge. The more trivial and detailed the discussion becomes, the more they get their adrenaline up and enjoy the thrill of the hunt.

I on the other hand, check out almost immediately. From an organizational realpolitik perspective, these meetings are typically full of sound and fury, signifying nothing. The initial meeting organizer turns on the firehose and then quickly loses control of it as the entire affair devolves into a cartoonish torrent of ideas being sprayed around the room as the hose snakes, thrashes, and contorts with no guiding hand. Nobody is really capturing all of this, so the extroverts leave the meeting flush with the satisfaction of shouting their opinions at each other, but most likely having neglected to agree on anything. But, my inclination to check out goes deeper than the fact that nothing is particularly likely to be accomplished; it’s that neither the forum, nor the ideas and the opinions are interesting or important to me.

scan0003

I earnestly apologize if this sounds arrogant or stand-offish, but it’s the honest truth. And this is where the part about me being an introverted problem solver and pattern-matcher comes in. The meeting I want to have is one where I come prepared with statistical analysis and data about the most efficient flows of information in triage scenarios. I want performance records and response times for Bill and Susan, including in her former role in the case of the latter. I want to have synthesized that data looking for patterns that coincide with issue types and resolution rates, and I want to make a recommendation on the basis of all of that. To me, those are table stakes to the meeting. Whoever has the best such analysis should clearly have his or her plan implemented.

But that’s not what happens in extrovert meetings. As soon as the meeting organizer loses control of the firehose, we’ve entered the realm of utter unpredictability. I start to present my case study and the patterns I’ve noticed, and then someone interrupts to ask if I captured that data using the new or old ticketing system. And, by the way, what power point template am I using because it’s really snazzy. And, anyway, the thing about Susan is that she’s really not as much of a people person, but Doug kind of is. Now the extroverts are firmly in command. All prior analysis goes out the window, and, as people start jabbering over one another reasoned analysis and facts are quite irrevocably replaced with opinions, speculation, gossip, and non sequitur in general. The conversation floats gently down stream and washes up on a distant shore when everyone decides that it’s time for lunch. All of the analysis… unconsidered and largely ignored.

And that, the extroverts taking over and leaving me to space out, is the best case scenario. In the worst case scenario, they start peppering me with a series of off-topic, gotcha questions to which I have to reply, “I don’t know off the top of my head, but I can look into it later.” This puts me at a huge disadvantage because extroverts, buoyed by the rush of the occasion, have no qualms about guessing, fudging, hand-waving, or otherwise manufacturing ‘analysis’ out of thin air. When things take this kind of turn, and someone else “wins the meeting,” it’s even more exhausting.

Regardless of which kind of meeting it is though, the result is usually the same. After lunch and everyone has a chance to forget the particulars of the discussion, it becomes time to email the real decision maker or chat one on one with that person, and re-present the analysis for consideration. Usually at that time, the analysis wins the day or at least heavily informs the decision. The meeting robbed me of an hour of my life to accomplish nothing, as I knew it would, when I looked sadly at my Outlook calendar that morning.

There are two kinds of meeting that have no chance to fit this pattern, however (I’m omitting from consideration meetings that are actually policed reasonably by a moderator to keep things on-agenda, since these are far more rare than they should be). These are meetings where I’m passively listening to a single presenter, or actively presenting to the group. It’s not especially interesting that I’d find the former kind of meeting not to be exhausting since it’s somewhat akin to watching a movie, but the latter is, apparently, somewhat interesting. Presenting is not exhausting to me the way that a night out at a party is exhausting. There are sometimes pre-speech/talk jitters depending on the venue, but the talk is entirely predictable to me. I control exactly what’s going to be said and shown, and the speed at which I’ll progress. There is a mild element of unpredictability during the Q&A, but as the MC for the talk, you’re usually pretty well in control of that, too. So, that is the reason I find typical corporate meetings more exhausting than presenting in front of groups.

A strange thing, that. But I think in this light it’s somewhat understandable. Having reasoned analysis, cogent arguments, and a plan is the way to bring as much predictability (and, in my opinion, potential for being productive) to the table as possible. For me, it’s also the way most likely to keep the day’s meetings from sucking the life and productivity right out of you.

[thrive_leads id=’7177′]

By

TDD and Modeling a Chess Game

For the first post in this series, I’m responding to an email I received asking for help writing an application that allows its users to play a game of chess, with specific emphasis on a feature in which players could see which moves were available for a given piece. The emailer cited a couple of old posts of mine in which I touched on abstractions and the use of TDD. He is at the point of having earned a degree in CS and looking for his first pure programming job and it warms my heart to hear that he’s interested in “doing it right” in the sense of taking an craftsmanship-style approach (teaching himself TDD, reading about design patterns, etc).

Modeling a game of chess is actually an awesome request to demonstrate some subtle but important points, so I’m thrilled about this question. I’m going to take the opportunity presented by this question to cover these points first, so please bear with me.

TDD does not mean no up-front planning!

I’ve heard this canard with some frequency, and it’s really not true at all (at least when done “right” as I envision it). A client or boss doesn’t approach you and say, “hey can you build us a website that manages our 12 different kinds of inventory,” and you then respond by firing up your IDE, writing a unit test and seeing where the chips fall from there. What you do instead is that you start to plan — white boards, diagrams, conversations, requirements definition, etc.

The most important outcome of this sort of planning to me is that you’ll start to decompose the system into logical boundaries, which also means decomposing it into smaller, more manageable problems. This might include defining various bounded contexts, application layers, plugins or modules, etc. If you iterate enough this way (e.g. modules to namespaces, namespaces to classes), you’ll eventually find yourself with a broad design in place and with enough specificity that you can start writing tests that describe meaningful behavior. It’s hard to be too specific here, but suffice it to say that using TDD or any other approach to coding only makes sense when you’ve done enough planning to have a good, sane starting point for a system.

Slap your hand if you’re thinking about 2-D arrays right now.

Okay, so let’s get started with this actual planning. We’ll probably need some kind of concept of chess pieces, but early on we can represent these with something simple, like a character or a chess notation string. So, perhaps we can represent the board by having an 8×8 grid and pieces on it with names like “B_qb” for “black queen’s bishop.” So, we can declare a string[8][8], initialize these strings as appropriate and start writing our tests, right?

Well, wrong I’d say. You don’t want to spend your project thinking about array indices and string parsing — you want to think about chess, chess boards, and playing chess. Strings and arrays are unimportant implementation details that should be mercifully hidden from your view, encapsulated snugly in some kind of class. The unit tests you’re gearing up to write are a good litmus test for this. Done right, at the end of the project, you should be able to switch from an array to a list to a vector to some kind of insane tree structure, and as long as the implementation is still viable all of your unit tests should be green the entire time. If changing from an array to a list renders some of your tests red, then there’s either something wrong with your production code or with your unit tests, but, in either case, there’s definitely something wrong with your design.

Make your dependencies flow one way

So, forget about strings and arrays and let’s define some concepts, like “chess board” and “chess piece.” Now these are going to start having behaviors we can sink our TDD teeth into. For instance, we can probably imagine that “chess board” will be instantiated with an integer that defaults to 8 and corresponds to the number of spaces in any given direction. It will also probably have methods that keep track of pieces, such as place(piece, location) or move(piece, location).

How about “chess piece?” Seems like a good case for inheritance since all of them need to understand how to move in two dimensional spaces, but they all move differently. Of course, that’s probably a little premature. But, what we can say is that the piece should probably have some kind of methods like isLegalMove(board, location). Of course, now board knows about piece and vice-versa, which is kind of icky. When two classes know about one another, you’ve got such a high degree of coupling that you might as well smash them into one class, and suddenly testing and modeling gets hard.

One way around this is to decide on a direction for dependencies to flow and let that guide your design. If class A knows about B, then you seek out a design where B knows nothing about A. So, in our case, should piece know about board or vice-versa? Well, I’d submit that board is essentially a generic collection of piece, so that’s a reasonable dependency direction. Of course, if we’re doing that, it means that the piece can’t know about a board (in the same way that you’d have a list as a means of housing integers without the integer class knowing about the list class).

Model the real world (but not necessarily for the reasons you’d think)

This puts us in a bit of an awkward position. If the piece doesn’t know about the board, how can we figure out whether it’s moving off the edge of the board or whether other pieces are in the way? There’s no way around that, right? And wasn’t the whole point of OOP to model the real world? And in the real world the pieces touch the board and vice versa, so doesn’t that mean they should know about each other?

Well, maybe we’re getting a little ahead of ourselves here. Let’s think about an actual game of chess between a couple of moving humans. Sure, there’s the board and the pieces, but it only ends there if you’re picturing a game of chess on your smart phone. In real life, you’re picking pieces up and moving them. If you move a piece forward three spaces and there’s a piece right in front of it, you’ll knock that piece over. Similarly, if you execute “move ahead 25 spaces,” you’ll drop the piece in your opponent’s lap. So really, it isn’t the board preventing you from making illegal moves, per se. You have some kind of move legality calculator in your head that takes into account what kind of piece you’re talking about and where it’s located on the board, and based on those inputs, you know if a move is legal.

So, my advice here is a little more tempered than a lot of grizzled OOP veterans might offer. Model the real world not because it’s the OOP thing to do or any dogmatic consideration like that. Model the real world because it often jolts you out of a code-centric/procedural way of thinking and because it can make your conceptual model of the problem easier to reason about. An example of the latter is the idea that we’re talking about pieces and boards instead of the 2-day arrays and strings I mentioned a few sections back.

Down to Business

I’m doing this as an exercise where I’m just freewheeling and designing as if I were implementing this problem right now (and just taking a little extra time to note my reasoning). So you’ll have to trust that I’m not sneaking in hours of whiteboarding. I mention this only because the above thinking (deciding on chess piece, board, and some concept of legality evaulator class) is going to be the sum total of my up-front thinking before starting to let the actual process of TDD guide me a bit. It’s critical to recognize that this isn’t zero planning and that I actually might be doing a lot less up front reasoning time than an OOP/TDD novice simply because I’m pretty well experienced knowing how to chart out workable designs from the get-go keeping them flexible and clean as I go. So, without further ado, let’s start coding. (In the future, I might start a youtube channel and use my Pluralsight setup to record such coding sessions if any of you are interested).

First of all, I’m going to create a board class and a piece class. The piece class is going to be a placeholder and the board is just going to hold pieces, for the most part, and toss exceptions for invalid accesses. I think the first thing that I need to be able to do is place a piece on the board. So, I’m going to write this test:

[TestClass]
public class BoardTest
{
    [TestClass]
    public class AddPiece
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Does_Not_Throw_Exception_When_Adding_A_Piece_To_An_Unoccupied_Square()
        {
            var board = new Board();

            board.AddPiece(new Pawn(), 2, 1);
        }
    }
}

First of all, I’ll mention that I’m following the naming/structuring convention that I picked up from Phil Haack some time back, and that’s why I have the nested classes. Besides that, this is pretty vanilla fare. The pawn class is literally a do-nothing placeholder at this point, and now I just have to make this test not throw an exception, which is pretty easy. I just define the method:

public class Board
{
    public void AddPiece(Pawn piece, int xCoordinate, int yCoordinate)
    {
                
    }
}

Woohoo! Passing test. But that’s a pretty lame test, so let’s do something else. What good is testing a state mutating method if you don’t have a state querying method (meaning, if I’m going to write to something, it’s pointless unless someone or something can also read from it)? Let’s define another test method:

[TestClass]
public class BoardTest
{

    [TestClass]
    public class GetPiece
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Retrieves_Piece_Added_To_Location()
        {
            var piece = new Pawn();
            var board = new Board();
            board.AddPiece(piece, 1, 1);

            Assert.AreEqual(piece, board.GetPiece(1, 1));
        }
    }

    [TestClass]
    public class AddPiece
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Does_Not_Throw_Exception_When_Adding_A_Piece_To_An_Unoccupied_Square()
        {
            var board = new Board();

            board.AddPiece(new Pawn(), 2, 1);
        }
    }       
}

Alright, now we probably have to make a non (less) stupid implementation. Let’s make this new test pass.

public class Board
{
    private readonly Pawn[,] _pieces = new Pawn[8,8];

    public void AddPiece(Pawn piece, int xCoordinate, int yCoordinate)
    {
        _pieces[xCoordinate, yCoordinate] = piece;
    }

    public Pawn GetPiece(int xCoordinate, int yCoordinate)
    {
        return _pieces[xCoordinate, yCoordinate];
    }
}

Well, there we go. Now that everything is green, let’s refactor the test class a bit:

[TestClass]
public class BoardTest
{
    private Pawn Piece { get; set; }

    private Board Target { get; set; }

    [TestInitialize]
    public void BeforeEachTest()
    {
        Piece = new Pawn();
        Target = new Board();
    }

    [TestClass]
    public class GetPiece : BoardTest
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Retrieves_Piece_Added_To_Location()
        {
            Target.AddPiece(Piece, 1, 1);

            Assert.AreEqual(Piece, Target.GetPiece(1, 1));
        }
    }

    [TestClass]
    public class AddPiece : BoardTest
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Does_Not_Throw_Exception_When_Adding_A_Piece_To_An_Unoccupied_Square()
        {
            Target.AddPiece(new Pawn(), 2, 1);
        }
    }
}

This may seem a little over the top, but I believe in ruthlessly eliminating duplication wherever it occurs, and we were duplicating that instantiation logic in the test class. Eliminating noise in the test methods also makes your intentions clearer in your test methods, which communicates more effectively to people what it is you want your code to do.

With that out of the way, let’s define something useful on Pawn as a starting point. Currently, it’s simply a class declaration followed by “{}” Here’s the test that I’m going to write:

[TestClass]
public class PawnTest
{
    [TestClass]
    public class GetMovesFrom
    {
        [TestMethod, Owner("ebd"), TestCategory("Proven"), TestCategory("Unit")]
        public void Returns_2_3_When_Passed_2_2()
        {
            var pawn = new Pawn();

            Tuple possibleMoves = pawn.GetMovesFrom(2, 2);

            Assert.AreEqual(2, possibleMoves.Item1);
            Assert.AreEqual(3, possibleMoves.Item2);
        }
    }
}

Alright, now let’s make this thing pass:

public class Pawn
{
    public Tuple GetMovesFrom(int xCoordinate, int yCoordinate)
    {
        return new Tuple(xCoordinate, yCoordinate + 1);
    }
}

You might be objecting slightly to what I’ve done here in jumping the gun from the simplest thing that could work. Well, I’ll remind you that simplest does not mean stupidest. There’s no need to return a constant here, since the movement of a pawn (move ahead one square) isn’t exactly rocket science. Do the simplest thing to make the tests pass is the discipline, and in either case, we’re adding a quick one liner.

Now, there are all kinds of problems with what we’ve got so far. We’re going to need to differentiate white pieces from black pieces later. GetMoves (plural) returns a single tuple, and, while Tuple is a cool language construct, that clearly needs to go in favor of some kind of value object, and quickly. The board takes an array of pawns instead of a more general piece concept. The most recent unit test has two asserts in it, which isn’t very good. But in spite of all of those shortcomings (which, are going to be my to do list for next post), we now have a board to which you can add pieces and a piece that understands its most basic (and common) movement.

The take-away that I want you to have from this post is two-fold:

  1. Do some up-front planning, particularly as it relates to real-world conceptualizing and dependency flow.
  2. It’s a journey, and you’re not going to get there all at once. Your first few red-green-refactors will leave you with something incomplete to the point of embarrassment. But that’s okay, because TDD is about incremental progress and the simultaneous impossibility of regressions. We can only improve.

By

Get Fed Up Every Now and Then

In SQL Server management studio, all of objects in the database are listed in the object explorer in the format [schema].[object]. In large databases of the legacy variety, it’s not uncommon to find that the only schema for application tables/views/sprocs is dbo. In this case, navigating to the object you want requires the infuriating step of typing “dbo.” before the table name. This may not seem like a big deal, but you have to type fast for that style of navigation, and typing that period, far from home row, creates a problem. This has annoyed me for years, but today, I’d had enough.

With a few minutes of googling, I found this Q&A exchange. Clearly others feel the same way and while I don’t consider that to be ideal, it’s certainly an improvement over what I was doing. I can hit “F7” on a folder and bring up an “object explorer” window that lets me search by object name alone because it separates schema as a different column. Win. Sort of.

I have been using SQL Server Management Studio for nearly a decade and I never knew this. But, after a few minutes with google, now I know. Sometimes it’s google, sometimes it’s a tweet, sometimes it’s asking a coworker. But, the common thread is that one day I just say “enough” and decide to do something about it. It might not be a total solution, but I decide right then and there that the situation and my life need to be improved somehow, immediately.

I think this is a pretty valuable activity. Obviously, you pick up new tips and tricks this way, but more subtly, you’re embracing a philosophy that your routines and habits can always be improved and you’re mentally setting an expiration date on mindless, sub-optimal, or obtuse activities. In a way, this is fairly agile (using lower case a in a nod to the recent blogosphere brouhaha over “taking back agile” — I just mean it’s predicated upon the idea of iterative, steady progress). You’re not paralyzing yourself by analysis to figure out the best way to do things up front all of the time, but rather leaving yourself open to the possibility (certainty) that you can improve the way you do things.

ThisEndsNow

So pick something. Today. Pick something that’s a minor irritant that you’ve simply put up with for a long time and say to yourself, “This. Ends. Now.” in a dramatic, action hero voice. Spend a few minutes doing research and I’d imagine you’re going to be happy. I’ve personally never regretted anything about this time investment except for the regret that I didn’t allow myself to get fed up sooner. So, go ahead. You don’t have to take it anymore.

By

The Least Pleasant List

Negative feedback is something with which we all have to contend, and probably on a fairly regular basis. It comes in a whole lot of forms with varying degrees of merit or importance: official performance reviews, talks with coworkers, miscellaneous gossip, heated discussion with loved ones, and even things like comments on a blog post or publication (I love The Oatmeal’s treatment of this last one — see the section about comments). You may or may not be expecting it, but nevertheless, it tends to hit you like a slap in the face.

I usually respond to negative feedback by trying to understand what flaw of the feedback provider is causing them to be wrong about me. Perhaps they’re simply some kind of crank or idiot, or maybe it’s more elaborate than that. They might have serious psychological problems or else a diabolical motivation for hatching a conspiracy against me. Maybe they’re jealous. Yeah, totally.

But after a while, I stop thinking completely like a child and allow just the teeniest, tiniest bit of introspection. I mean, obviously, the person is still a jealous moron, but it is possible that maybe showing up late to work and snapping at everyone I talked to all morning could have been just the slightest bit off-putting to someone. I’ll generously allow for that possibility.

I am, of course, exaggerating for effect, but the point remains — I immediately respond to negative feedback by feeling defensive or even hostile. It’s easy to do and it’s easy to take feedback badly. And to make matters worse, there is definitely feedback that deserves to be taken badly such as someone simply being rude for no reason. Not all negative feedback is even reasonable. The end result is that it becomes very hard to make feedback lemonade out of the negative lemons your critics are lobbing at you.

But I urge you to try. Here’s an exercise I’m contemplating. Whenever I get negative feedback, legitimate or spurious, I’ll make a note about it. I’ll jot it down in some kind of notebook or perhaps make a spreadsheet or Trello board out of it, and I’ll let it digest for a while. Once somewhat removed from the initial feedback, I can probably filter more objectively for validity. If I can make some actionable improvement, I’ll do so. Every now and then I’ll check back to see if the things I used to get the feedback about have changed and if I seem to be making strides. Maybe such a scheme will be worthwhile for me and perhaps it might be for you too, if you’re so inclined.

It’s not easy to take a frank look at yourself and admit that you have shortcomings. But doing so is the best way to improve on and eliminate those shortcomings. It’s hard to recognize that negative feedback may have a grain of truth or even be dead on. I know because it’s hard for me. But it’s important to do so if you’re serious about achieving your goals in life.

By

What TDD Is and Is Not

In my travels, I’ve heard a lot of people embrace test driven development (TDD) and I’ve heard a lot of people say that it isn’t for them. For the most part, it’s been my experience that people who say it isn’t for them have either never really, actually done it or haven’t done it enough to be good at it.

Lest you think I’m just being judgmental, here’s a post of mine from about 3 years ago, before I had truly taken the humbling plunge of forcing myself to follow the red-green-refactor discipline to the letter. My language here is typical of someone who buys into unit testing and even to TDD, but who hasn’t become facile enough with the process to avoid feeling the need to tinker with it and qualify the work. If you cut to the heart of what’s going on in my post there (and I say this hat in hand) it’s less “here’s my own special way of doing TDD that I think is actually superior” and more “I haven’t yet gotten good enough at TDD to use it everywhere and that’s rather frustrating.”

Stoplight

Not long after I wrote that post, I forced myself, on a project, to follow the discipline, to the letter, and I’ve never looked back after the initial pain of floundering during what I used to describe as the “prototyping stage” of my coding. I solved the ‘problem’ of TDD not being compatible with that prototyping by thinking my designs through more carefully up-front, solving only problems that actually exist, and essentially not needing it anymore. TDD wasn’t the problem for me when I wrote that post; the problem was that I wasn’t being efficient in my approach.

It’s been my experience that most of the criticism of TDD comes from people like me, years ago. These are people who either have never actually tried TDD or who have tried it for an amount of time too short to become proficient with it. I can rarely recall someone who became proficient with the practice one day saying, “you know what, enough of this — I don’t see the benefit.” For me this statement is hard to imagine because TDD shortens the feedback loop between implementing something and know if it works, and developers innately crave tight feedback loops.

And since the majority of criticism seems to come from those least familiar with the discipline, there are a lot of misconceptions and straw man arguments, as one might expect. So today, I’d like to offer some clarity by way of defining what TDD is and what it it isn’t.

What TDD Isn’t

I think it’s important that I first discuss what TDD is not. There are a lot of misconceptions that surround the test driven development approach to writing software, and there’s a pretty good chance that you’ve at least been exposed to them, if not slightly misled by them. Not only does this make it hard to understand how the practice works, but it may even lead you or your team to reject it for reasons that aren’t actually valid.

  1. First, TDD is not a replacement for user acceptance testing. Someone who practices it does not believe that it’s a valid substitute for running the application and making sure that it does what the requirements state that it needs to do.
  2. Classic TDD is also not comprehensive automated testing. The by-product of it is mainly unit tests, which are tests for finely grained pieces of code, such as classes and methods. Other kinds of automated tests, such as integration tests and end to end system tests involving databases or other external constructs will be a separate prong of your overall testing strategy.
  3. One thing that I frequently hear as an indictment of TDD is that it doesn’t encourage you to think through your design because you initially do the simplest thing to make tests pass. This is not accurate at all. It simply distributes the planning over the course of your development as you go rather than forcing it all to be done up front.
  4. Test driven development is not and does not claim to be any sort of load testing, concurrency testing, or anything else that you might put under the category of “smoke” or “stress” testing. The tests generated by TDD are not meant to test the behavior of your system under adverse conditions.
  5. Another common misconception is that TDD means that you write all tests for the system before you start writing your code. Frankly, doing so would be wildly impractical, and detractors who cite this impracticality as a reason not to do TDD are arguing against a straw man.
  6. TDD is not a quality assurance strategy. When your software department is contemplating new initiatives and dividing them up according to who will own then, TDD does not belong to the testing group.
  7. Detractors of TDD often point out that it doesn’t address corner cases in application or even class and method logic. That is true, but TDD doesn’t aim to address these. They belong with the other automated tests that will be written later.
  8. And, believe it or not, TDD is not primarily a testing activity. This is probably the hardest for people to wrap their heads around when learning the practice. But if you think about the acronym – test driven development, it is primarily development. The tests driving it are simply a characteristic of the development.

What TDD Is

Having gone over a series of things that TDD is not, hopefully I’ve cleared up some misconceptions and narrowed the field a bit. So let’s take a look at what TDD actually is. I should note here that the flavor of TDD that I’m addressing is “Classic,” triangulation-oriented TDD rather than the behavior driven “London” school of TDD.

  1. As mentioned in the last section, TDD is a development approach. It’s a development approach that happens to produce unit tests as you go, which you can then save for later. I suppose you could discard them and retain some of the benefit of the approach, but that would certainly be a waste since you’re going to want automated tests for your system anyway.
  2. Another facet of the test driven development approach is that you avoid “paralysis by analysis,” a situation in which you are so overwhelmed by the complexity of the problem that you simply stare at the screen or otherwise procrastinate, unsure how to proceed. TDD ensures that you’re constantly solving manageable problems.
  3. TDD produces test cases that cover and address every line of code that you write. With this 100% test coverage, you can change your code fearlessly in response to altered requirements, architectural needs, or other unforeseen circumstances. You’ll never have to look at the system nervously, wondering if you’re breaking things. Your tests will tell you.
  4. Besides allowing you to change code easily, test driven development also guides you toward a flexible design. The reason for this is that TDD forces you to assemble your code with testing seams in it, which are entry points, such as constructor and method arguments, that allow you to take advantage of polymorphism for easier testing. A side effect of this is that your code that’s testable is also easier to configure and mix and match in production.
  5. TDD is also a way to ensure that you’re writing as little code as necessary. Since every change to production code requires a failing test, you have to think through exactly what the system needs before you ever touch it. This prevents speculative coding and throwing in things that you assume you’ll need later, such as property setters you never actually use.

So how is all of this accomplished? Well, TDD is a discipline that follows a specific process and relatively simple process.

  1. As I mentioned briefly, the first step is that you expose some kind of deficiency in the system that you’d like to address. This could be a bug, a missing feature, a new requirement – anything that your codebase does not currently do that you want it to. With the deficiency picked out, you write a test that fails because of the deficiency.
  2. With the failing test in place, you then implement the simplest possible solution in your code to get the failing test to pass, ensuring that all of your other tests also still pass. This makes the system completely functional.
  3. Once the system is completely functional, you look at your quick and dirty fix and see if it needs to be refactored toward better design. If so, you perform the refactoring, ensuring that the tests still pass.

So there you have it: a brief overview of what TDD really is. If you’re interested in more on this subject and you have a Pluralsight subscription, check out my course on continuous testing TDD using a tool called NCrunch, which is all about speeding up your feedback loop during development. Most of this post is from the transcript of that course. If you don’t have a Pluralsight subscription and are interested in a trial, drop me a line and I’ll give you a free week of Pluralsight subscription.

By the way, if you liked this post and you're new here, check out this page as a good place to start for more content that you might enjoy.