Thursday, July 26, 2012

Ceci n'est pas une interface


Likewise, extracting all the public functions from a class and surrounding them with

public interface ISomeClassName { 
 
}

Does not make an interface.

One of the terrible failings of Resharper is the Extract Interface dialog has a button for All Public functions.
This encourages this frankly misguided behaviour.


IEnumerable is an interface. It defines one thing and it defines it will. Wander into any code base and if you have a decent environment, you should be able to do some form of "Find Usages" which will show classes that implement IEnumerable. There will be quite a few. That's because it's REALLY useful.

If I take a Binary Tree, there may be many different functions, allowing me to add Ranges, delete Ranges, create Views, Find Items, Get Counts, etc etc. How do I benefit from extracting the entire public interface to create an IBinaryTree. It would likely turn out that the interface has so much in it that the only thing which can implement it is another Binary Tree.

Maybe I have a lump of code which wants a Binary Tree, but in fact it really only cares about a collection which has been sorted into a given order. SortedCollection (or if you must ISortedCollection) might be a perfectly reasonable interface.


When we look at the members that my code uses, it comes down to iterating through the list...


public interface ISortedCollection
   IEnumerator GetEnumerator();
}


So in fact my code just needs an IEnumerable. It does not need an IBinaryTree at all.*


If I pass in an IEumerable then I can actually use a List for my code after calling List.Sort().


Now we are down to what an interface is. An interface specifies some functionality that I want to use. Ideally the minimal functionality. It is so much less than a list of public functions in an existing class, and in being less, it is more.


Now my Unit Tests can be far simpler, I no longer need to have test which depend on the binary tree. I can use a simple Array which I define in sorted order. 


Small interfaces reduce unnecessary binding. 


* You could argue that a specific interface for Sorted data would allow me to catch, at compile time, the problem of passing unsorted data down here. This is true, but that's a whole other post.

Friday, July 6, 2012

Open/Closed Principle

With so many things interrelating, it's hard to explain the importance of one thing without relying on many other bits of knowledge.

Open/Closed is on of those things.

How can software be Open for Addition and Closed for Modification.

Surely I need to change code? And change it all the time.

Well, maybe.

If I write smaller functions, and smaller classes. If they do one thing, and do it correctly, then they probably don't need to change. Ever.

My larger blocks of functionality, which are made of the smaller blocks, will change more often, but if, for example I create a class or function to do a bilinear interpolation of a surface, then as long as it does a bilinear interpolation, I may need to optimize it, but it will only ever do a bilinear interpolation. If I want to do some other interpolation, I create a new class.

My higher level class may change to use the new interpolation, but I have not CHANGED the bilinear one.

I may wrap the General Bilinear interpolation in a class which is better named from the point of view of the problem domain, and takes domain named parameters. Rather than have an Bilinear.Interpolate(double x, double y)->double  my wrapper may be myChemicalMix.ApproximateReactionRate(double tempInCelcius, Acidity ph) -> ReactionRate.

Now my domain class does nothing but map my domain problem onto an interpolation. I no longer have to remember is x the Acidity or the Temperature.

So now when I change from a Bilinear Interpolation to a Triangular Interpolation, my Bilinear Class remains untouched, I write my Triangular Interpolation and Unit Tests, and if it turns out that they can each implement a Surface Interpolation interface, happy days.

Nothing in designing software stands on it's own. That's why it's easy to write a program, but it's hard to engineer one.

Without Single Responsibility and Dependency Inversion, Unit testing is staggeringly difficult.
Without The Open/Closed Principle, you will spend all your time changing tests.
Without Interface segregation, you won't understand what you need to mock out for a given test, and the test will be overly complex.

Good design can only come from understanding why.



* yes, I did make those up from thin air

Tuesday, June 5, 2012

What's a tractor for

Imagine that you teach someone how to drive a tractor, how to hook up a plough, and you send them out into the field.

At no point do you mention crops, seeds, harvesting, food, or seasons. You don't speak of fertiliser nor crop rotation.

When you come back the field is full of staggering, incredibly complex and mostly useless furrows.

In the same way we seem to teach people how to use polymorphism, lambda functions, inheritance, iterators, and other tools.

But somewhere along the way no one has pointed out the fact that what we are doing is trying to make it easier to write and maintain code. In addition, it would be nice if we could also make it easier to be sure the code is correct. 

It seems that we have taught the how, not the why.

In the good old days of C++ the general comment was that in your first program you learn how to overload the operators, in your second program, you learn not to.



Friday, April 13, 2012

Design V Hack

Implementing a feature can be done quick and dirty, or you can spend time and do it right.

Sometimes, it makes sense to do it quick and dirty. If you need a demo by Tuesday to get a foot in the door, or if you're not even sure what you are doing. Bang something together figure it out, write it properly later.


The cost of banging it out any old way that works is simply deferred. Later you pay the cost, either in a re-write or in maintenance nightmares.

If your "feature" has infected a lot of the rest of the code base, then a rewrite is expensive. If it's in structural code, which everyone depends on, then that nasty interface is already in use, and now, not only do you pay back the work you saved, you also pay back the interest and penalties. (You may have thought that credit cards were harsh). There's even a term for this :- Technical Debt

As you add hack after hack to a project, or even just messy code that's difficult to follow, the savings start to disappear. Now to change something, you need to spend more time understanding difficult code. Now it takes more time to change anything. The process continues until the code becomes intractable. Then each bug fix introduces new problems. Each change takes longer and longer.

People say they have no time for refactoring, automated testing, and just plain writing better code. I'd argue, you don't have time not to.

Wednesday, March 7, 2012

The Architectural IF

"IF" is one of the fundamental constructs of programming languages. (Yes, I know someone will know at least 3 languages without "IF", but I know a lot more which have "IF").

"IF", like a Biro is so ubiquitous that it's pretty much impossible to imagine life without it.

But as the Biro has been superseded by the Computer for works of any length, "IF" has been superseded in many ways by better ways of doing things.

If makes a nice living dealing with details. But often we can avoid it by simple design

 var myClientList = GetClientList();  
   
 if (myClientList != null)  
 {  
   sendAnnoyingEmailsTo(myClientList);  
 }  

What if GetClientList() never returned a Null. If something goes wrong, it throws and exception, if there's no clients, it returns an empty list. Now my Code looks like
 var myClientList = GetClientList();  
 sendAnnoyingEmailsTo(myClientList);  
   

This is a flavour of the null object pattern. There is no decision to make. There is no spoon "IF".
But that's a detail, it's the The Architectural "IF" that 'causes me to occasionally consider inventive and infantile revenges on both other programmers, and in fairness, on my past self. If you find a comment like this..
 } // closing if (clientObject is typeof(MajorClient))  
   

The comment indicates there is so much code dependent on the "IF" that you cannot see the other brace on screen. It also darkly hints at a programmer cheerfully ignoring virtual functions.

So why is the programmer doing this?
Surely (s)he:

  •  cannot be ignorant of virtual functions? 
  •  cannot fail to realise that a new dependency is being propagated throughout the code?
  •  must know that now every new Client Sub Class will need to be weaved in through every file that uses the "construct"?
The answer is almost certainly in the class hierarchy. It's broken. It's wrong. Something in there does not fit in well. The base class is OK for most of the sub classes, except for just this one case, and well maybe the case here too. So instead of fixing the problem, the The Architectural "IF" is brought to bear. Flick quickly to the matching brace, and you will almost certainly find something like this...

It may even be an "else if".

If your Architecture is creaking, "IF" can fix it for a while, but like using sticky-tape, the repair is at best temporary.

Tuesday, February 28, 2012

Write it Right


Shakespeare's Casear siad "cowards die many times before their deaths, the valiant never taste of death but once."


Write code badly and you will taste it again and again and again. (and it does not improve with subsequent re-heatings)


"Write It Right" and you only need to see it again when something relevant changes.


It may take twice as long to "Write It Right", but if you figure out how much time gets spent fixing bad broken code one "stoopid" at a time, twice as long seems like a bargain.


Add in secondary costs, Building Releases, Damage to Customer Confidence, Direct Cost of Bugs, and WIR seems so obvious it hardly needs to be said.


Yet, it would appear, it needs to be said again and again and again.


WIR

Thursday, February 9, 2012

Know what is executing when and where...

If we run this test, it throws an uncaught exception from the thread pool. Uncaught exceptions from the thread pool will crash your app.

The key to this is that the lambda is not executed here and now. The Lambda is passed to the thread pool.
The thread pool executes it, but only after we have set our classWithFunction member variable to null.

 class ClassWithFunction  
 {  
   public void fn()  
   {  
     System.Console.WriteLine("Hello World");  
   }  
 }  
   
 ClassWithFunction classWithFunction = new ClassWithFunction();  
   
   
 [Test]  
 public void testThreadPool()  
 {  
   ThreadPool.QueueUserWorkItem(state => classWithFunction.fn());  
   
   classWithFunction = null;  
   
   Thread.Sleep(2000);  
 }  
   


If you consider the Lambda in terms of a function, it all becomes a whole lot clearer. By the time ThreadPoolFunction is called, classWithFunction is null.
This is referred to as a modified closure. The Lambda does not get a copy of our variable, it actually uses our variable in place.

 class ClassWithFunction  
 {  
   public void fn()  
   {  
     System.Console.WriteLine("Hello World");  
   }  
 }  
   
 ClassWithFunction classWithFunction = new ClassWithFunction();  
   
 [Test]  
 public void testThreadPool()  
 {  
   ThreadPool.QueueUserWorkItem(ThreadPoolFunction);  
   
   classWithFunction = null;  
   
   Thread.Sleep(2000);  
 }  
   
 private void ThreadPoolFunction(object state)  
 {  
    classWithFunction.fn();  
 }  


And if you look closely, you will see that this IS different and it works quite nicely. Of course, the downside of this as a solution is that someone will surely come along and remove the "unnecessary" temporary variable.

 [Test]  
 public void testThreadPool()  
 {  
   ClassWithFunction tempClassWithFunction = classWithFunction;  
   ThreadPool.QueueUserWorkItem(state => tempClassWithFunction.fn());  
   
   classWithFunction = null;  
   
   Thread.Sleep(2000);  
 }  


Perhaps it would be better to move up to .net 4 and use the Parallel Task Library instead.

Monday, February 6, 2012

Important / Urgent

From the good old Important / Urgent time management graph, we can see how Code Quality is not "Urgent" but the fallout of poor code, Bugs, are both Important and Urgent.

If we ignore Code Quality, we borrow against future effort and build up a technical debt. We end up with more bugs which are important/urgent, which means less time for Code Quality, which means more bugs.....

Rinse, Repeat, buy an "Inside I'm Screaming" teeshirt.

Not Urgent Urgent
Important Code Quality Fix Bugs
Not important Fix "trivial" bugs Coffee

Clearly, this is an incomplete table, but you get the idea.

Wednesday, January 18, 2012

Tragic Heroes of Design

I doubt you could find anyone who has studied literature who could not talk about the "Tragic Hero". It has been a pattern in literature since Aristotle's time. Many of the aspects are repeated again and again in plays, stories and films. This is an early Design Pattern.

How is it then that when I interview people for Software Engineering roles, some have barely heard of design patterns. Many can only name Singleton, and few can simply state the basic idea that these Design Patterns are simply well known ways of solving problems. They are solutions that work well, are easy to maintain and have good properties in terms of specific criteria, which may be, for example, performance, dependencies or maintainability.

I'd expect any engineer with a lot of experience to at least know about a façade and a decorator, and be able to explain why a factory class is more than just a "place to put constructors together". I'd expect them to know what a Strategy Pattern was, or a Command Pattern.

These are not the "current fad", they are high level, well known ways of doing things. They are not a magic bullet, they are simple basic knowledge.

Assuming that you can read and write to a basic standard, how would you begin to compare Hamlet and King Lear without an understanding of "The Tragic Hero" as a starting point.

Why are people dismissive of the whole idea?

Monday, January 9, 2012

Unit Tests, Code Quality and Legacy Code

Some days you start adding unit tests to legacy code. Perhaps you are about to make some changes, and you'd like to be sure you don't break something else.

In your Test Setup, you find that first you need to set this, and you need to call that, and you need to make sure this data is set, and so on.

There's a number of problems here, but the one that I'm looking at in particular is that as you continue to try to get your tests to run, you find more and more prerequisites.

Suddenly your Test Setup is starting to get a little out of hand.

This comes down to code quality.

What is good quality code? It's fairly complex to define. As you try to add a "second client" to a given class, you find out if it's easy to have someone else use the code or not. In this case our second client is the Unit Tests.

Fair enough, if the code is doing something complex, there may be a lot of setup, but there are two problems here.
  • It's not clear what setup is required.
  • Perhaps the code should not be doing something so complex.
Of course TDD gets around this by writing the tests first, and many of the things that we consider "good quality" come from the constraints that Unit Testing forces on you.

In this same way, unit testing make code reviews more practical. Now you have a Clearer Metric for good code. If the code can't be tested, it's a good hint that the code is not Good Code.

Before Structured Unit Tests, you had the pantomime code reviews -

  • "that functions too long", 
  • "oh no it's not", 
  • "oh yes it is" 
Now it's simpler

  • "that functions too long", 
  • "oh no it's not", 
  • "How come it's got no unit tests"
  • "it's doing too many thing to test"
  • "so it's to long then"
  • "yes, I guess so"
A lot of the Code Quality which people used to call subjective, now has a practical metric. If it's hard to write unit tests for, then the code's not good code.