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.

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(intn=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:

...

publicstaticvoid encrypt(String s)

{

encryptThreadSafe(s, new PrintStreamThreadSafe(System.out));

}

publicstaticvoid encryptThreadSafe(String s, PrintStream p)

{

…

for(intn=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

...

publicstaticvoid encrypt (String s) throws IllegalArgumentException

{

if(s == null)

{

thrownew 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.