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.


3 comments:

  1. The Analogy and the approach is very well explained Rajith.

    Wont there be too much rework on the tests (automated) and the number of test cycles itself ?

    Though integrating the static scans and different functional tests helps to certain extent, the testers have to rework on the same scripts multiple times.

    Any thoughts on how that can be optimized ?

    ReplyDelete
    Replies
    1. Rework or "learning", it is a matter of perspectives :)
      Anything substantial in nature is always built using this do + learn cycle, iterating through it using small batches.
      Companies deal with this creative cycle in 2 ways:
      Some deny the need for learning, treat is as rework and would relentlessly try to reduce it, often creating more problems for future. Others, only a few companies now, accept is as an essential part of the creative process and iterate through this do + learn cycle using small batches, viz. the natural way of building things.

      Your choice!

      Delete
    2. Agree with you Its on the perspectives that people have !

      Conceptually it is great and would apply to the tests too, as testers learn the product over a period of time. But in actual practice, when we talk to different customers on the testing prospects, the expectation of automation is to improve on the efficiency aspects and have minimum rework on the regression test suite.

      These expectations too need to change with this mindset !!

      Thanks for sharing our views :)

      Delete