DaedTech

Stories about Software

By

Chess TDD 41: Finishing up White Pawn Movement

This post was the first of another three that I recorded together.  I wanted to tie up some loose ends and move on to implementing pawn movement for black, but I just wound up tying up loose ends.

On a meta note, I’m going on vacation out of the country for a week, so I may or may not be in a position to log in and push posts live this week. It may be a light week for DaedTech posts.

What I accomplish in this clip:

  • Got IsNonCaptureAllowed working properly

Here are some lessons to take away:

  • There’s nuance to when to drive your design/development with acceptance tests versus unit tests.  TDD is generally good when you want to iterate quickly to get a narrow implementation right.
  • There’s no harm in extracting out a method from a test so that you can save yourself typing in the next test you’re writing.  (As long as you’re green while you do it)
  • It’s not relevant to my series, per se, but it’s worth knowing what characterization tests are.
  • Look out for the sunk cost fallacy with your own code.  The fact that you’ve pulled out and spent time creating a method doesn’t mean that the method is actually beneficial or valuable.
  • Beware of iceberg classes.

By

Chess TDD 40: Diagonal Capture for Pawns

This post was the last of a series of 3 I recorded together, so it picks up right on the heels of episode 39 from a flow perspective.  I initially tied up loose ends from the previous episode and then got the pawn’s diagonal capture in place, at least for the white pawns (acceptance tests for black pawns won’t go well right now).  Fits and starts, but real progress nonetheless.

What I accomplish in this clip:

  • Finished the TDD implementation for IsCaptureAllowed
  • Implemented IsNonCaptureAllowed
  • Finally gave pawn diagonal capture ability

Here are some lessons to take away:

  • Always look to refactor test code when it comes to readability and eliminating duplication.  These refactorings are not just for production code!
  • There’s a sizable contingent of people that don’t like seeing the Assert call factored out of unit tests.  Clearly a matter of personal preference, but you may get pushback in a shared code base.
  • In methods, preserve a consistent level of abstraction.  If you have complex two part conditional, don’t factor out one part and leave the other part — make them both conceptually similar.
  • Clean, readable code will look at least vaguely familiar to domain experts, even if they aren’t programmers.  They should see/read concepts that are familiar.
  • Keeping the lines of code low in a class isn’t a first class or all-encompassing goal.  A class can get ugly by having too many methods, even if all of those methods are small.

By

Chess TDD 39: Dipping a Toe into Complex Piece Movement

What I accomplish in this clip:

  • Finished implementing the bishop acceptance tests.
  • Introduced the concept of “capture move” and “non-capture move” to piece movement.
  • Made some good headway on the pawn’s diagonal capture capability.

Here are some lessons to take away:

  • I’ve mentioned this before in the negative a few times, but I’ll phrase it in the positive.  If you’re going to do refactorings, even trivial ones like deleting a dead method, do it with a green test suite.
  • Writing acceptance or integration tests, whether using them to drive design or not, tends to give you a good lesson in patterns of things that can go wrong.  The specific example I’m referring to here is how I noticed that movement for the black pieces from higher numbered rows to lowered number ones tended to be a problem across pieces.
  • Even if you’re not thrilled with a design, you can be productive and get things done with it when your test coverage is good since you won’t be afraid to change it later.  This helps prevent “coders’ block” or “paralysis by analysis.”
  • When you’re reasoning about implementation, allow yourself to stub out helper methods or methods on other objects that will ‘magically’ do what you want.  E.g. if I were writing a “CookDinner” method, I might just declare “TurnOnStove()” and stub it out, to be implemented later.  This practice of defining what before how can help you get toward the single responsibility principle.
  • I won’t belabor the metaphor of tech debt, but having a really high degree of test coverage and a lot of small, modular methods in the code base lets you live with tech debt a little more easily in the short term.  With a lot of crufty code around the problematic code, you’ll never want to touch anything when it comes time to pay it off.  With lots of coverage and good code elsewhere, getting rid of the tech debt at a time that suits you is easier.

By

ChessTDD 38: Bishop Tests and Being Wrong about Being Wrong

For this episode, I did something a bit different.  I actually recorded the coding for this and then two more episodes on its heels.  If you’re following along on github, you’d notice that I’ve checked in code for episode 40, though I still need to do the audio for those next two episodes.  In this particular episode, I get off to a nice start with testing for bishop and then get a bit derailed.  Luckily (spoiler alert), I get it straightened out in next episode.  And, for all 3 of these, I successfully kept to right around 20 minutes.

What I accomplish in these clips:

  • Implement part of the acceptance tests for bishop.

Here are some lessons to take away:

  • I find a lot of benefit in stripping everything but what I need in default templates I’m provided (e.g. the spec flow feature file, an web.config file, etc).  I prefer to be as deliberate as possible about everything that goes into the software work product.
  • No matter what you’re doing, if you do it brainlessly, it won’t go well.  I avoided copy and paste to avoid brainless mistakes, and just wound up brainlessly typing in the wrong thing by hand.
  • TDD lets you poke at and test the code to see if you can get to green (or red) without fully understanding what you’re doing, but if you do this, make absolutely sure you spend time immediately afterward writing tests or reasoning about the code so that you do understand it.
  • You almost certainly won’t stop yourself from making stupid mistakes, but you can develop techniques for discovering them more quickly and reducing their impact scope.

By

ChessTDD 37: Cleaning Up and Implementing Rook

Got back in the saddle with this episode, and had a shorter recording that basically went well.  Someone pointed out in the comments that I had reversed king and queen position for the black pieces, so I started off by fixing that, and then moved on to fixing a bug and finally implementing the Rook acceptance tests.  Wrapped it up in relatively short order, too.

What I accomplish in these clips:

  • Fixed my template for the full chess board.
  • Fixed the vertical version of that reverse bug from last time.
  • Implemented rook acceptance tests.

Here are some lessons to take away:

  • It’s important to understand past shortcomings of your code so that you can form good hypotheses about where you might have other potential weak spots.
  • If you’re going to run an experiment on your code, do it from a test that you’re writing instead of through the GUI or the debugger or something.  You’re running the experiment anyway, so you may as well capture the results in the form of an automated regression test.
  • A lack of test coverage for a line of code isn’t a problem in and of itself.  Coverage is a metric and not a goal.  The problem with having code not covered by tests is that there’s nothing to prove that the code was implemented in a thoughtful, deliberate way.  If you don’t have a test expressing what a path through the code is supposed to do, there’s no way to know if you’ve reasoned about that code.
  • Copying, pasting, and adjusting often winds up taking longer than typing by hand.  You spend more time staring dumbly at the screen trying to figure out what’s wrong than it would have taken you to hand type the code you need.