Tuesday, December 20, 2011

Why Software Development is like Ironing a Thneed

 I’m being quite useful. This thing is a Thneed.
A Thneed's a Fine-Something-That-All-People-Need!
It's a shirt. It's a sock. It's a glove. It's a hat.
But it has OTHER uses. Yes, far beyond that.
You can use it for carpets. For pillows! For sheets!
Or curtains! Or covers for bicycle seats!
"

                                         – from “The Lorax” by Dr. Seuss

During my time in the military two decades ago I become highly skilled at ironing. And not only shirts and pants, but beds, hats, sheets and other items one would not normally consider “ironable”. I continue to this day to do my own ironing. I actually find it rather therapeutic.

Since having children I have also become reacquainted with the works of Theodor Geisel, more affectionately known as Dr. Seuss. It struck me the other day while ironing a particularly pesky shirt that software development is very much like ironing a Thneed.

So let’s make some assumptions about a Thneed based on the description above. Some poetic licence and imagination will be required.

  1. Nobody is entirely sure what a Thneed is, not even the manufacturer.
  2. When customers buy a Thneed they have only a vague idea of what they need it for and how it is going to make their lives better.
  3. No two Thneed’s are exactly alike; they are the snowflakes of garments.
  4. Thneed producers create new and improved Thneed’s all the time.
  5. A Thneed is too big and awkwardly shaped to fit on your ironing board and Dr. Seuss makes no mention of a Thneed-press.
  6. It is hard, if not impossible, to estimate how long it will take you or a highly trained team of Thneed-ironers to iron a Thneed, with any level of accuracy or confidence in your estimate.
  7. The market for Thneed-ironing accessories is confusing in its profusion, pace and super-competitiveness.
  8. Thneed-ironing methodologies were derived from shirt and pants ironing methodologies, but they are actually poorly suited.
  9. A Thneed is like most other garments in that it occasionally requires ironing.
  10. Thneed manufactures’ own Thneed’s are usually the worst ironed.

So based on the assumptions above, how does one go about ironing this Thneed thing? Well the trick is known by every person who has ever had to iron a shirt.

Manipulate the garment so that a small piece of it is flat on the ironing board and then iron that piece. Then get another piece flat and iron that piece, and so on and so on, until you have ironed the entire garment. If you are foolish enough to try to iron large sections of the garment at the beginning, you will become very frustrated and will probably run out of steam before you are done. You have to “divide and conquer” when it comes to ironing a Thneed; that is the winning strategy.

I don’t think that I need to actually spell out why this is like software development; if you have done any software development in your life you will know that I am right (or mostly so).

Maybe someday I will post “Why Architects are like the Lorax, and Users like the Once-ler”. On second thoughts maybe I won’t.

Happy Ironing!

Wednesday, December 14, 2011

Apache Hadoop on Windows Azure

The brilliant Alexander Stojanovic talking about his latest project, Isotope, which is Microsoft’s Hadoop distribution. Yes, you read that right; Microsoft is going to be packaging up HDFS, MapReduce, Flume, Sqoop, Hive, Pig, Pegasus, Mahout, Lucene and some new technology into a Hadoop distributed for the Windows and Windows Azure platforms.

Update (June 10th, 2012): It looks like Channel9 removed the video for some reason.

Monday, December 12, 2011

Ghost in the Wires

I just finished reading “Ghost in the Wire : My Adventures as the World’s Most Wanted Hacker” by Kevin Mitnick and William L. Simon. It reads like a spy thriller and I literally had a hard time putting it down. The stories of Kevin Mitnick’s social engineering exploits are truly amazing, and regardless of one’s ethical stance on hacking one has to respect his extraordinary audacity.

Given that Mitnick himself admits to being a master of social engineering, i.e. lying and manipulating people, I still cannot say for sure whether or not I believe that all his hacking was motivated by curiosity alone. But I don't think it matters really; I agree with Kevin and his supporters that he was treated rather poorly by the US Justice System.

In hindsight though, I can also totally understand why he was treated as harshly as he was; at the time they simply did not have the necessary understanding to ascertain just how big a threat he was, and so they had to assume the worst, and defer to sources who obviously had issues with him that went well beyond the morality and legality of his hacking activities. I also don’t think it mattered that he had apparently not used any of the access or data he obtained for nefarious purposes; it was simply the fact that he could have caused significant damage and loss if he so chose. It was just pure luck on the part of the targets of Kevin’s hacking that he was not malicious;  and I would hazard a guess that contemporary jurisprudence is not underpinned in any way by luck (though I am no expert in this area, so one never knows).       

This book is well worth the read and I highly recommend it for anyone interested in computer security. It shows that the weakest link in any system is unarguably always the human components, and that without strictly adhered-to policy that no system can be made secure, regardless of the size of the technology investment.

P.S. The book refers to a film that was made by Kevin’s supporters; it is called Freedom Downtime - The Story of Kevin Mitnick. It was obviously made on a shoestring budget, and apparently before hacking became a lucrative new line of business for organized crime world-wide, but it is worth watching. It also happens to be available in its entirety online, and I have embedded it below:

Friday, December 9, 2011

Processing.js Snowflake Fail

I probably should be writing a post about Microsoft’s SOA and BPM platforms, but I need a breather from that particular topic, so instead I am going to write about my recent frustrations with Processing.js. I was hoping to be able to create some flashy new sketches, but unfortunately my recent experiments have uncovered a critical bug in Processing.js that will only be fixed in the 1.5 release. 

My 3 year-old daughter can’t stop talking about snow so I decided to create a little snow generator for her and post it on this blog. I also wanted to experiment with Processing.js’ ability to load SVG files, which can then be used in a sketch. My idea was simple; create an SVG file that contains a number of shapes that can be randomly combined to create snowflake shapes. Then generate a collection of those random shapes and animate them. Not an ambitious project in the least.

My previous experiments with Processing were done with the stable 1.5.1 release of PDE. I thought I would try the latest alpha version of the Processing 2.0 PDE for this experiment, primarily because it has a JavaScript mode, and will export a web page that loads Processing.js and your sketch (and detect the necessary browser capabilities too!). It does not seem to provide an option to embed the sketch script directly in the HTML, so the sketch is always referenced as an external pde file.  It took me a couple of hours to create the SVG file in InkScape, and a sketch in PDE that did exactly what I wanted. While prototyping the sketch I was working in the Standard mode, i.e. it generates a Java applet, since that offers the best development-time performance.

When, after completing the sketch, I changed to the JavaScript mode my sketch failed to pretty much do anything other than draw the background gradient.

The original sketch looks like this when running:

snowfalkes

Processing provides a loadShape method that takes the path or URL to an SVG file, parses the SVG, and generates Processing-native PShape objects. There is currently no way to load SVG elements that are embedded directly in the HTML. Hopefully this will come in a future version of Processing.js. Processing also provides a  getChild method to get shapes nested within the root PShape. PShapes can be drawn directly to the screen or drawn off-screen to a PGraphics object which can then be used at some later time to draw to the screen by calling the image method.

To generate my snowflakes I created an array of PGraphics objects (each with a little wrapper) and then drew random snowflakes to each. I also added some noise and toy physics to make the whole thing a little more realistic. It looked great in PDE.

Note: I initially was sorting the array from smallest to largest and then drawing them in that order, but after comparing the results I could not see a difference and simply omitted the sort. I had to write my own sort function because the sort implementation that is provided in Processing will only sort arrays of int, float and String. 

There was only one small problem; off-screen drawing of PShapes is broken in the current build of Processing.js. I have filed a bug and it looks like this will be fixed for the 1.5 release of Processing.js. So this post obviously does not include the running sketch.

Another Note: I tried using the tint method to modify the apparent brightness of the snowflakes based on their scale each time the snowflake was drawn, rather than explicitly adjusting the stroke color. This KILLED the performance even when it was running in the Standard mode on my quad-core 8GB laptop with hardware-accelerated graphics. Another bug perhaps?

Rather than find a work-around in Processing.js, I will probably try to port this sketch to one of the other JavaScript graphics APIs, like Raphaƫl for example. And of course I will post the running result and code in some future post on this blog.

Since I cant show the final result, here is my Processing code for the sketch (usual caveats and disclaimers apply):

color bkground = #000080;
color bkground2 = #0000FF;
snowFlakeFactory factory;
snowFlake[] flakes; 

void setup()
{
  size(600, 200);
  smooth();
  frameRate(60);
  factory = new snowFlakeFactory("snowflakes.svg");
  flakes = factory.createFlakes(60);
}

class snowFlakeFactory
{
  color _background;
  PShape _template,
  _spoke, 
  _centerHex, 
  _centerCircle, 
  _star, 
  _longArms, 
  _mediumArms, 
  _shortArms, 
  _endCircle;

  snowFlakeFactory(String templateFileName)
  {  
    _template = loadShape(templateFileName);
    _template.disableStyle();
    _spoke = _template.getChild("spoke");
    _centerHex = _template.getChild("centerHex");
    _centerCircle = _template.getChild("centerCircle");
    _star  = _template.getChild("star");
    _longArms = _template.getChild("longArms");
    _mediumArms = _template.getChild("mediumArms");
    _shortArms = _template.getChild("shortArms");
    _endCircle = _template.getChild("endCircle");
  }

  snowFlake createFlake()
  {
    snowFlake flake = new snowFlake();
    PGraphics graph = createGraphics(450, 450, P2D);
    graph.beginDraw();
    graph.background(0,0);
    graph.smooth();
    float br = 4000 * flake._scale;
    graph.stroke(br, br, 255);
    graph.noFill();
    graph.strokeWeight(13);
    graph.shapeMode(CORNERS);
    radialDraw(graph,_spoke, 225, 225, 0);
    if (heads())
    {
      if (heads())
      {
        graph.shape(_centerHex, 225, 225);
      }
      else
      {
        graph.shape(_centerCircle, 225, 225);
      }
    }
    if (heads()) graph.shape(_star, 225, 225);
    if (heads()) radialDraw(graph,_endCircle, 225, 225, 190 );
    PShape[] arms = {
      _longArms, _mediumArms, _longArms
    };
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 100);
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 130);
    if (heads()) radialDraw(graph,arms[(int)random(0, 2)], 225, 225, 160);
    graph.endDraw();
    flake._image = graph;
    return flake;
  }

  snowFlake[] createFlakes(int flakeCount)
  {
    snowFlake[] flakes = new snowFlake[flakeCount];
    for (int i = 0; i < flakeCount; i++)
    {
      flakes[i] = createFlake();
    }
    return flakes;
  }

  void radialDraw(PGraphics graph, PShape feature, float originX, float originY, float rad)
  {   
    float xOffset = rad * cos(PI/6);
    float yOffset = rad * sin(PI/6);
    graph.shape(feature, originX, originY + rad);
    feature.rotate(PI/3);
    graph.shape(feature, originX - xOffset, originY + yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX - xOffset, originY - yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX, originY - rad);
    feature.rotate(PI/3);
    graph.shape(feature, originX + xOffset, originY - yOffset);
    feature.rotate(PI/3);
    graph.shape(feature, originX + xOffset, originY + yOffset);
    feature.rotate(PI/3);
  }
}

class snowFlake
{
  float _posX;
  float _posY;
  float _scale;
  float _rotation;
  PGraphics _image;

   snowFlake()
  {
    _posX = random(width);
    _posY = random(height);
    _scale = heads()? random(0.01,0.03) : random(0.04,0.1);
    _rotation = random(0, PI/6);
    _image = null;
  }

  void drawFlake()
  {
    pushMatrix();
      translate(_posX, _posY);
      scale(_scale);
      rotate(_rotation);
      image(_image, -225, -225);
    popMatrix();
  }
}

void draw()
{
  drawBackroundGradient(bkground,bkground2);
  for(int i = 0; i < flakes.length; i++)
  {
    snowFlake flake = flakes[i];
    float gravity = flake._scale * (10 + random(0,5));
    float wind = flake._scale * (5 + random(-2,2));
    flake._posY += gravity; 
    flake._posX += wind; 
    flake._rotation += 0.01;
    flakes[i].drawFlake();
    if(flake._posY &rt; height + 20) flake._posY = -20;
    if(flake._posX &rt; width + 20) flake._posX = -20;
  }
  
}

void drawBackroundGradient(color c1, color c2)
{
  noFill();
  for (int i = 0; i <= height; i++) {
    float inter = map(i, 0, height, 0, 1);
    color c = lerpColor(c1, c2, inter);
    stroke(c);
    line(0, i, width, i);
  }
}

float _prob = 0.75;
boolean heads()
{
  float rand = random(0, 1);
  return (rand < _prob);
}

And here is the SVG:

<svg id="snowflakeTemplate" xmlns="http://www.w3.org/2000/svg" height="1000" width="700" version="1.1">
<g id="mainLayer" stroke="#000" stroke-miterlimit="4" stroke-dasharray="none" fill="none">
<path id="spoke" stroke-linejoin="round" d="M0-0,0,190" stroke-linecap="round" stroke-width="10"/>
<path id="centerHex" stroke-linejoin="miter" d="m-36.48-23.229,0,44.146,36.184,20.891,36.775-21.232,0-41.606-35.987-20.777z" stroke-linecap="butt" stroke-width="10"/>
<path id="star" stroke-linejoin="round" d="M53.858-31.273,49.838-86.48-0.015-62.659l-50.032-24.126-4.3663,55.188-45.979,31.37,45.814,31.287,4.3801,55.416,49.918-23.819,50.097,24.128,4.4815-55.112,46.094-31.294z" stroke-linecap="round" stroke-width="10"/>
<path id="longArms" stroke-linejoin="round" d="M49.142,11.646,0-11.646-49.142,11.559" stroke-linecap="round" stroke-width="10"/>
<path id="mediumArms" stroke-linejoin="round" d="M28.782,6.4858,0-6.4858-28.782,6.3986" stroke-linecap="round" stroke-width="10"/>
<path id="shortArms" stroke-linejoin="round" d="M-12.226,2.7043,0-2.7915,12.226,2.7915" stroke-linecap="round" stroke-width="10"/>
<circle id="endCircle" cx="0" cy="0" r="10" stroke-width="10"/>
<circle id="centerCircle" cx="0" cy="0" r="36.5" stroke-width="10"/>
<rect id="diamond" stroke-linejoin="round" transform="rotate(45)" height="8" width="8" stroke-linecap="round" y="-4" x="-4" stroke-width="10"/>
</g>
</svg>