The Beginning & the End

August 15, 2009 § 1 Comment

How does a developer follow your specification?

We’ve all have been there.  Endless chains of code.  We have no idea of where it started and where it will stop.  You have that sense of being lost.  The problem is not necessarily with the size of the implementation, but with understanding the high-level steps through the code.  If you ever have found yourself in the debugger for hours try to locate that elusive defect within a massive code base, you know what I mean.

Often the approach to finding the code of interest is a process of elimination.  You ask yourself “Is this what I am looking for?”  You review it, and if it does not meet your criteria you move on to the next likely candidate.  If it met your criteria, you then drill down in to the code asking the same question over and over again until you discover what you are looking for.

This blind search eats up your time, it costs businesses money, and it delays efforts on other work products.

There are several techniques you can use to mitigate this problem:  refactoring, behavior-driven-development, etc.  I want to outline a technique when you are designing code for your system.  It involves informing the developer about the shortcuts s/he can take.

When you are presented the option to create software using a user stories or requirements, you often break down the problem in to its high level entities and their interactions.  For example, if you are building a payroll system and you need to write code to e-file paycheques you have several possible entities:  Employee, PayCheque, Aggregators for paycheques & employees, etc.  Once you identify them, you would relate each entity in the system to each other.  This effectively produces a logical representation of the problem domain you are trying to solve.

What it does not do is specify the flow of the user story or requirement through the system.  You could represent this in UML, but like documents, over time UML diagrams can get out of step with the code.  The problem of understanding flow may not seem relevant when you are the author and implicitly understand the intricacies of what you designed.  However when you move on to another project and someone needs to fix a defect in the code you wrote, the problem becomes apparent.

The next opportunity you have to design the representation of a user story or requirement, I would suggest that you design a class or method, that only delegates, where the entrance and exit of your specification is clearly outlined.

Imagine the following system:

class foo
{
void Process()
{
// start the work
DoMoreStuff();
}

void DoMoreStuff()
{
DoYetMoreStuff();
}

void DoYetMoreStuff()
{
FinishUpTheWork();
}

void FinishUpTheWork()
{
// finish up the work.
}
}

Now imagine that you have a defect you need to find.  Where do you begin?  If you know the system, you may remember that Process() is where you need to start.  For someone who does not understand the system, they may start at DoYetMoreStuff() because that is where the defect happens to manifest.  If that person does not find the error there, s/he needs to follow the call stack to find the source.  In the above code, this is a very simple activity.  Now imagine a specification in your product that involves tens of classes and ten times more interactions between them.  The complexity of the problem increases greatly.  How fast do you think you would find the defect then if you had no current knowledge of the code?

Unit tests may help find the source of a defect, but not all changes are defects.  They may be enhancements to existing systems, or perhaps the developer needs to review some code in order to understand it.  Without a clear entrance or exit for the specification in your code, the developer is left to his own devices to find what s/he is looking for.

Compare the same code using a method that encapsulates the specification: 

class foo
{
void Process()
{
// start the work
DoMoreStuff();
DoYetMoreStuff();
DoYetMoreStuff();
FinishUpTheWork();
}

void DoMoreStuff()
{
}

void DoYetMoreStuff()
{
}

void FinishUpTheWork()
{
// finish up the work.
}
}

It is far easier for someone to start at Process() and eliminate components that do not need to be observed.  The benefits using this technique are:

  1. A single entity is responsible for fulfilling the specification
  2. It is easier to run automated tests to validate the specification
  3. New developers can be directed towards a single point of entry and systematically eliminate entire sections of the process when locating functionality

I will be honest.  I do not think that this can be done in all cases.  Like many things in software development your coding standards, the condition of the existing code, your company’s best practices, and your experience all influence your designs.  I will state that this is an idea worth your time exploring the next time you create or edit code.

———-8<—————————————————–

How would you make a specification clear in your code?  Where does your specification begin and end?  Would this technique work for you?  an you leverage unit tests?  What about a combination of unit tests and this technique?

§ One Response to The Beginning & the End

Leave a comment

What’s this?

You are currently reading The Beginning & the End at Journeyman.

meta