Wednesday, 3 February 2016

Iterative Design

Nature is a great designer. The world around us is full of amazing lessons in design.

Take the weaver bird, for instance. The bird gets its name from the elaborately woven nest (most elaborate among birds) it builds.

The weaver bird designs its nest pragmatically, by building it.

Pragmatic Design © Paul & Paveena Mckenzie, Getty Images






















After building some part, the bird would check the increment for strength and stability.

Strength check © Tim Jackson, Getty Images



















The bird will reinforce the structure if not satisfied with its strength or stability. Then it will continue to build the nest further.

Building further © Manoj Shah, Getty Images






















Thus, the bird iteratively switches between building further, and strengthening what is built. Eventually, it would complete the main chamber.

Chamber completed © Anup Shah, Getty Images


















The bird doesn't stop building, although the nest is functionally complete. It will secure the nest's entrance with a narrow passage, guarded with some sharp twigs, to protect it from the predators.

Securing the entrance © James Warwick, Getty Images


























What lessons, if any, can we learn from this, and use in the world of software design?

Any software has three sets of characteristics; the functional part which is about the features, the operational part which deals with aspects like security, performance and concurrency and the developmental aspects of being readable, easy to debug and extensible. These three characteristics are orthogonal, i.e. changing one will not usually affect the other. Since they are orthogonal, it is not very intuitive to consider them together, say security and being extensible, at the same time.

A good design is one where these three characteristics are visited iteratively, and one at a time.

In my last blog about incremental coding (http://agileiq.blogspot.in/2015/12/code-like-sloth.html), we saw how to build the functionality incrementally.

Our encrypting function is as shown below:

...
public static void encrypt(String s)
{
      s = s.replaceAll("\\s", "");
      int rows = (int) Math.floor(Math.sqrt(s.length()));
        int cols = (int) Math.ceil(Math.sqrt(s.length()));
        if(rows*cols < s.length())
              rows++;
        for(int n=0;n<cols;n++)
        {
              for(int m=0;m<rows;m++)
              {
                     if(n+m*cols >= s.length())
                           break;
                     System.out.print(s.charAt(n+m*cols));
              }
              if(n<cols - 1)
                     System.out.print(" ");
        }
            
        System.out.println("");
}
...

Let us continue with the same example to do some iterative design for the developmental and operational aspects.

A good programmer once told me that loops should do nothing, but only loop. Let us refactor the loop code by extracting it as a method for better readability.

...
for(int n=0;n<cols;n++)
{
      printColumn(s, rows, cols, n);
}
...

Next, we look at the concurrency design, and find that the print functions cannot assure it. We can use some indirection here and achieve concurrency as follows:

...
public static void encrypt(String s)
{
       encryptThreadSafe(s, new PrintStreamThreadSafe(System.out));
}

public static void encryptThreadSafe(String s, PrintStream p)
{
       

        for(int n=0;n<cols;n++)
        {
              printColumn(s, rows, cols, n, p);
        }
            
        p.println("");
}
...

We will look at another operational aspect now, by securing our code. In my experience, tools are an invaluable asset in secure design. You may use a combination of code scanning tools, like Coverity and application scanning tools, like Nessus for this.

In our code example, let us take care of a common security flaw, handling the null input, as follows

...
public static void encrypt (String s) throws IllegalArgumentException
{
       if(s == null)
       {
             throw new IllegalArgumentException("Null input");
       }
...

In our small example, after two or three iterations of handling the operational and developmental parts, our iterative design is satisfactory.

In bigger implementations, we would have to do more number of iterations to be satisfied with our design.

We can visualize the state of our design, any time, as below:

Design Radar





















Initially, the design would be skewed towards functionality. After some iterations of addressing the operational & developmental concerns, we would have a more well-rounded(!) design.

We can configure our CI system to regularly generate such snapshots of our design; where the functional indices may be based on the function test passing rate and suitable tools, like Coverity, Structure-101, Nessus, JMeter, Valgrind etc. may be used to suggest the operational and developmental health.

When we find that our operational and developmental design is poor, it would be better to pause adding new functions and have a Zero-Feature sprint to catch up.