Here's an example**:
if (order.OrderTypeId == Order.OType.NORMAL && processingStack.CanBeProcessed(order.Id, maxProcessingAttempts, minutesDelayBeforeProcessing))
{
da = null;
if (order.Lock(moduleUserId, ref rl, -1, ref da, ref pe))
{
CancelAuthorizedOrders(order, moduleUserId, countries, providerConfig, ref processingStack, maxProcessingAttempts);
da = null;
order.Unlock(moduleUserId, ref da);
LoggingModule.Increment(LoggingModule.LogCategory.Orders, LoggingModule.LogCounter.Cancel);
}
}
Aside from the fact that this snippet sits within a public method that has no tests (immediately then, according to Feathers, legacy code), what stands stands out about this code? How easy is it going to be for future developers to read, understand and maintain?
I'm going to break down why I think this is so bad, starting with the first line:
if (order.OrderTypeId == Order.OType.NORMAL && processingStack.CanBeProcessed(order.Id, maxProcessingAttempts, minutesDelayBeforeProcessing))
Why not have a boolean variable with a meaningful name for your second condition here, which would make the 'if' statement much smaller and more readable?
Something like:
bool canBeProcessed = processingStack.CanBeProcessed(order.Id, maxProcessingAttempts, minutesDelayBeforeProcessing);
if (order.OrderTypeId == Order.OType.NORMAL && canBeProcessed)
{
...
}
Since we're doing that, why not do the same for the first condition as well e.g.
bool canBeProcessed = processingStack.CanBeProcessed(order.Id, maxProcessingAttempts, minutesDelayBeforeProcessing);
bool orderIsNormalType = order.OrderTypeId == Order.OType.NORMAL;
if (orderIsNormalType && canBeProcessed)
{
...
}
I think that is much more readable and easier to debug should you need to. Of course you can debate whether you need to put in a variable or not depending on the circumstance. Had the 'if' conditional been "if (order.OrderTypeId == Order.OType.NORMAL)" only, I would almost certainly have left it alone, since that is simple to read and understand.
Next, variable names like 'da' and 'pe' and most heinously 'r1'. Is that 'rOne' or 'rEl'? What does it reprasent? It's declared 45 lines away. Give your variables meaningful names so the developers who inherit your legacy have less to do.
Next:
if (order.Lock(moduleUserId, ref rl, -1, ref da, ref pe))What is -1 here? Why do I have to look at the signature of the 'Lock' method to figure out what you're doing here? Magic numbers, meh!
None of the above suggestions change anything about how the code works, they are just about making it more readable. But code that is more readable is eminently easier to maintain. Of course if you have vast methods, within vast classes and no unit tests then maintaining and refactoring them is going to be difficult, but starting with good, clear names and readable code is a great first step towards helping those that follow after you.
* 'Legacy' being a euphemism for s***.
** Some method and variable names have been changed to protect the innocent.