Some things I think I like about ‘Specification-style’ development

I specifically say ‘Specification-style’ to make it clear that I don’t pretend to be doing “full-blown” Context/Specification BDD as described by Scott Bellware here, or even that I can use MSpec as developed by Aaron Jensen (typical ‘non-approved software’ issue…whatever).  Instead, what I am doing is taking what I take to be an important aspect of it and running with it.  Of course, just because I take it to be an important aspect doesn’t mean it actually is, blah blah blah.

I’m working on a system that does stuff in a very ‘legacy’ way.  Keeping in mind that ‘legacy’ often means simply “code that I myself didn’t originally create or code that I myself did create but a while ago before I knew what the hell I was doing or code that I myself did create but long enough ago that I don’t remember what the hell I was doing”, this code is very ‘legacy’ in that it has those wonderful methods that do 17 things, like pull a file from some file system somewhere, import it somewhere, work on the data somehow, write out the now modified data somewhere, ftp it somewhere, then if necessary send an email to someone somewhere telling them that the data was modified and somehow incorrect.

Great stuff.  Easy to deal with.

Anyhoo, I had to modify some of the code in one (or more) of these wonderful methods and needless to say, the code isn’t easily modifiable, and obviously had all these dependencies (one or more files, ftp, email, etc.).

But I had a requirements document.  And some access to the business users to ask questions.  Not perfect, but okay.

The actual requirement was basically along the lines of “Given a collection of data A, if any item B within A failed validation based on condition C, send an email to D.”


Now, BDD has at times been described as “TDD done right.”  I’ve always thought this was flawed because I think TDD is flawed.  But, since I think that TDD is flawed in some sense like how Churchill viewed Democracy as flawed, e.g. ‘Democracy sucks, it just happens to suck less than any other system of government,”  I could work with it.

TDD is flawed in many ways, but one way in which it is flawed is that it is focused at the class level.  If I’ve got a DoStuffClass, then I need to have a DoStuffTestClass that has 100% code coverage of DoStuffClass.  I’ve never liked this because I don’t like the concept of 100% code coverage (sort of, read on) and because I don’t know why you should test at the class level.  Classes exist to allow you to do stuff.  If the ‘stuff’ that you are doing doesn’t require you to test every possible permutation of what one might possibly do with the class, then why do it?

Should you test the constructor of your class?  Should you test that each getter/setter on the class works?  I think that it depends on what the consumers of your classes are doing.  This is actually one of the reasons why I now see the reason why you might want to get rid of setters (and maybe getters) on your classes.  If you allow random consumer code to change an instance of your class because of its setters, then you have a risk that the consumer might do something that invalidates it.  If, instead, you only allow your class to be modified through commands in a way that guarantees your class can’t be invalid, then you only need to test those paths. 

The tie-in to ‘specification-style’ development is this:  your code should only do what it needs to do to fulfill the specifications that you know *right now* it needs to fulfill.   To go back to the (pseudo)-requirement, I need to validate item B based on condition C.  I can imagine conditions E, F, Q, etc. and may be tempted to code that in now based on the imagined conditions.  But no one has asked me to do that validation, and when they do, they will probably come up with requirements that don’t actually match the E, F, Q, etc. that I imagined.  So, be lazy.  Don’t do it.  It can be comforting to have more test cases that end up green (or grey, if using MSpec), but if you haven’t actually been asked to do what those test cases are testing, you aren’t really accomplishing much.

Back to the main point

When working with legacy code, you obviously want to abstract away from particular implementations.  There are many ways of doing this, but in almost every instance that I’ve come up with, it is pretty easy.

If your data is coming to you from a flat-file, it is almost always easy to come up with some interface or entity that represents the data that exists in the flat-file.  Write that code, and test against it.  If you have to test that an email is sent when some validation fails, create a wrapper class or an interface and test against that.  It isn’t necessary to create services and repositories and whatnot, but that works for me most of the time. 

Most importantly, code to the specifications.  There is nothing in “Given a collection of data A, if any item B within A failed validation based on condition C, send an email to D” that specifies any particular implementation, other than the email part.  You could probably pretty easily abstract that part to some INotification interface anyway. 

To lay it out:

Collection of Data A: though I know it comes from a flat file in production, the data has a structure I can define, and I can stub this out.

Item B failing Condition C: I can write code that does this pretty easily.

Send email to D: I can write a wrapper class or interface that has a ‘send’ method and verify in tests that this was called.

Most importantly, I can create a service call that passes in the data and then either calls or doesn’t call the send method.  This is easy to mock/stub out.  I don’t need to know about files or emails or whatnot.  I just need to know, in the heart of my code, does it fulfill the specification(s) or not?

None of this, obviously, means your code will work in production.  Getting code to work in production means dealing with real emails, real ftp sites, etc. etc. etc.  This is why you should expand the notion of ‘specifications’ to include production specs.  But that’s another topic for another time.

posted on Wednesday, September 09, 2009 8:21 PM Print
No comments posted yet.

Post Comment

Title *
Name *
Comment *  
Please add 1 and 5 and type the answer here: