February 2009 - Posts

On craft, dogma, pragmatism and better analogies

I’m leaping into a lengthy discussion, so I’ll start with the links:

Steve Smith wrote this post discussing the craftmanship vs. pragmatism.

Corey Haines wrote this response defending the craft movement.

And Steve responded again with this real world example.

Disclaimer: I know both Steve Smith and Corey Haines, and I have a lot of respect for both of them.

There are times when I believe we get off-track by using the wrong words, and the wrong analogies. Or, we point to the wrong evidence from the analogies.

Corey made the analogy between software and furniture: craftsmen are like the Amish craftsmen, and industrial software developers are like IKEA factories.

OK, that’s a reasonable start, and it points us to the wrong root diagnosis.

Instead, let’s look at craftsmen in the construction industry and watch everything they do.  I believe Corey’s analogy only examined the finished product.

A brief side trip to real home renovations

A few years ago we put an addition on our house. A portion of the house was one story, and a portion was two stories. We built above the portion that was single story and added two bedrooms.

The final product is high quality, and my wife and I were very happy with the additions.

During the process, the carpenters built and destroyed several bits of scaffolding to make their lives easier.

The first task was to remove the old sections of roofing on the one story portion. Some of that contained beams that were holding up the roof on the two story section. It was weeks until all the framing would be done, but something had to be added to hold up the roof. One carpenter spent a half hour putting up some temporary beams. The roof stayed up.

This kind of temporary scaffolding was repeated several times through the process. One of the last tasks was to cut the doors from the existing second story into the addition. Before that was done, the carpenters built temporary steps to get up into the new section. This needed to last a month or so.

Temporary planks were laid while the framing was being constructed, before the final floor was laid.

None of these temporary tasks had the same quality that was evident in the finished product. That goes for both ‘finish features’ and engineering quality. For example, the temporary supports for the second story roof would not have withstood sustained 50 mph winds coming from the east. A real risk, but small.

In every case, the temporary solution was removed as work progressed to create the real solution.

Okay, but that’s not software

I believe there is an important analogy for software.  The temporary scaffolding is analogous to technical debt.  There are times when speed is more important than perfect craft.  However, when that happens, you’ll incur technical debt. 

Steve McConnell has an old post about Technical Debt that’s very pertinent here. In it, he separates technical debt on a number of variables. Unintentional debt, which you incur due to low quality work is always bad. That’s what Corey is protesting against. It’s insidious because you don’t even realize you’ve incurred unintentional debt.

Steve Smith is talking more about intentional debt. Here, Steve McConnell points out the difference between long-term and short-term debt: long term is strategic, and short term is tactical. Further, he describes two kinds of short term debt: ‘large chunks’ and ‘small shortcuts’. The large chunks, because of their size, have more scrutiny, and require more justification. The ‘small shortcuts’ are the kinds of quick shortcuts you do when you’re not being careful: not following your own conventions.

The purpose of this discussion on technical debt is that debt must eventually be retired. The more debt you take on (of any kind), the more cost to retire that debt. Sometimes even large debt is smart: few of us would own a home without some form of mortgage. That kind of debt is like what Smith (using last names to distinguish between Steve Smith and Steve McConnell) is discussing in his second post. The company’s very existence depends on a short release cycle. In those cases, it’s wise to take on a manageable amount of technical debt.

Focusing on the technical debt (as opposed to craft vs. pragmatism) gets you to look at the issue in a different way. No debt is always the goal; I wish I didn’t have a mortgage payment. But, smart debt is wise (I’m glad my last several years of mortgage payments have been building equity, rather than saving the full cost before buying a house. That’s true even in today’s economy). But, if you take on debt, you must pay it off. You must plan part of the budget for debt servicing.

Time for the big finish

Going back to the house renovation project, I didn’t like the appearance of the temporary scaffolding.  It looked shoddy. But, I knew it was temporary, and it was the best plan to achieve the final goal. The true craftsman knows when the first effort must be the best, and when the best plan is to make a (temporary) less than optimal choice, incur the debt, and pay it off. To do that properly requires a frank and honest discussion on the tradeoffs, with a real commitment to paying off whatever technical debt is incurred in the process.

I think too many people espousing the software craftsman mantra have been burned by too many companies that incur technical debt, and refuse to pay it off until it threatens the company’s very existence. I know I’ve been involved in failed ‘legacy’ projects where the entire team couldn’t pay off the incurred debt. That memory colors your goals, and your trust with those leaders that push ‘the business goals’.

Incurring technical debt (intentionally) is not the same as low-quality work. Advocating against technical debt is not dogma. It’s only when the people asking to incur technical debt and the people advocating against it don’t have a frank discussion on the benefits and costs (both long term and short term) of the decision.

Looking at Azure

I’m going to build on my tradition of blogging my notes as I explore new toolkits. Just like I did with LINQ, I’ll be posting blog entries about what I’ve learned as I work with the Azure platform.

Dissecting HelloFabric

I’m skipping the HelloWorld sample because the only thing I learned was that the current Device Fabric is not compatible with the Current Windows 7 beta build. Two different pre-release components, so I’m not too surprised.

HelloFabric gives a good overview of some of the basic functionality provided by the cloud infrastructure.  When you run the sample, you’ll have see a simple webpage that demonstrates some of the Azure APIs.

Let’s look at the code first, and then peek at the cloud specific configuration. The first thing you see when you run the application is that it tells you if it is running in the cloud, or on a standard ASP.NET context.  There’s a bit of code in the the page load handler that looks at the ApplicationEnvironment to see where it’s running.

   1: // Check if we are running in the fabric
   2: if (ApplicationEnvironment.ApplicationModel == ApplicationModel.Cloud)
   3: {
   4:     Label3.Text = "Running in the fabric." ;
   5:     if (Request.IsSecureConnection)
   6:     {
   7:         Label2.Text = "Running over a secure connection.";
   8:     }
   9:     else
  10:     {
  11:         Label2.Text = "Not running over a secure connection. Please see the documentation to test this sample with SSL.";
  12:     }
  13: }
  14: else
  15: {
  16:     Label3.Text = "Running outside the fabric.";
  17:     Label2.Text = "";
  18: }                      

The next item you’ll see in the page is that the label contains a set of banner text from one of the config files:

   1: Label1.Text = ApplicationEnvironment.Settings["BannerText"];

A little further down the page, you’ll see that you can enter a message that will get written to the log. The code couldn’t be much simpler:

   1: String msg = TextBox1.Text;
   2: switch (DropDownList1.Text)
   3: {
   4:     case "Alert":
   5:         ApplicationEnvironment.LogAlert(msg);
   6:         break;
   7:     case "Error":
   8:         ApplicationEnvironment.LogError(msg);
   9:         break;
  10:     case "Warning":
  11:         ApplicationEnvironment.LogWarning(msg);
  12:         break;
  13:     case "Information":
  14:         ApplicationEnvironment.LogInformation(msg);
  15:         break;
  16:     case "Verbose":
  17:         ApplicationEnvironment.LogVerbose(msg);
  18:         break;
  19:     default:
  20:         ApplicationEnvironment.LogError("Unexpected kind");
  21:         return;
  22: }

Finally, there’s a button that creates a null reference exception so you can exercise the tools that you’ll use to debug code that runs in the cloud.

You’ll note that all the code I’ve shown so far use this ApplicationEnvironment class. The ApplicationEnvironment class provides some basic capabilities that abstract away the core operating environment. 

In its class constructor, it looks at the RoleManager and if the role manager is running, it determines that the operating environment is the cloud. Next, it looks to see if there is a web context. That would mean it’s running in a standard web environment. If neither of those facts are true, it assumes it is running on the desktop:

   1: protected void Button1_Click(object sender, EventArgs e)
   2: {
   3:     String msg = TextBox1.Text;
   4:     switch (DropDownList1.Text)
   5:     {
   6:         case "Alert":
   7:             ApplicationEnvironment.LogAlert(msg);
   8:             break;
   9:         case "Error":
  10:             ApplicationEnvironment.LogError(msg);
  11:             break;
  12:         case "Warning":
  13:             ApplicationEnvironment.LogWarning(msg);
  14:             break;
  15:         case "Information":
  16:             ApplicationEnvironment.LogInformation(msg);
  17:             break;
  18:         case "Verbose":
  19:             ApplicationEnvironment.LogVerbose(msg);
  20:             break;
  21:         default:
  22:             ApplicationEnvironment.LogError("Unexpected kind");
  23:             return;
  24:     }
  25: }

You’ll notice from the code above that the constructor also constructs a logger object based on where its running.  A further dive into the code shows that the RoleManager class contains methods to write to a log in the Azure fabric.

   1: public override void LogAlert(string msg)
   2: {
   3:     RoleManager.WriteToLog("Critical", msg);
   4: }

The other logger implementations should be familiar to any .NET developer.

The last item you saw was reading a string from the configuration file.  That’s also different in the fabric, and the ApplicationEnvironment class abstracts that away from you.  To read configuration items while running in the fabric you use the RoleManager.GetConfigurationSetting method:

   1: return RoleManager.GetConfigurationSetting(name);

Of course, to read something from a config file, you’ll have to put the data in a config file.  Azure changes that again.  You have to define your config settings in your service definition.csdef file:

   1: <ConfigurationSettings>
   2:   <Setting name="BannerText"/>
   3:   <Setting name="LogAllRequests" />
   4:   <Setting name="LogFailedRequests" />
   5: </ConfigurationSettings>

And, you assign values to them in the ServiceConfiguration.cscfg file:

   1: <ConfigurationSettings>
   2:   <Setting name="BannerText" value="Hello Fabric!" />
   3:   <Setting name="LogAllRequests" value="false" />
   4:   <Setting name="LogFailedRequests" value="true" />
   5: </ConfigurationSettings>

Of course, you saw that there are some other settings in those config files, and that’s how logging gets enabled, and that makes use of the other classes in the Common library used in this sample.

The two boolean values affect an IHttpModule implementation that logs all requests, or just some of the requests.  When the module initializes, it reads the configuration setting and attaches a handler for all requests, or just error requests. The code demonstrates how to configure logging at runtime, because there is an overhead associated with the extra logging functionality.

If you want to explore the fabric sample yourself, make sure you open the Device Fabric logger to see the logging capabilities. That’s where all the logging messages go in the end.

In some ways, Azure development, at the code level, looks boring.  It’s too familiar. But then, that’s the point: Azure builds on the existing tools and libraries we already use.

Book Review: Mike Cohn X 2

I’m covering two books by Mike Cohn in this post: User Stories Applied, and Agile Estimating and Planning.

I’m reviewing them together because I read both of them at the same time, and some of the content is inter-twined in my own mind.

Both books provide a wealth of practical advice on succeeding with an agile process. Throughout both books, the depth of Mike’s coaching experience comes through.  With every element of advice, Mike includes a discussion about why each recommendation is what it is. He also includes a lot of “you may be thinking of …” comments with reasons why straying from his advice may cost you and hurt your project.

Throughout both books I found myself almost thinking Mike was inside my head, helping me improve. When an author does that, he’s clearly succeeding.

The two books are aimed at different, but overlapping audiences.  Maybe because I’m in both audiences, I found both useful. 

User Stories Applied goes into more depth about the process of creating user stories that will help drive your project to success. You’ll find advice that you should share with your customers. It will help them learn what makes a good user story, and how to express their needs and feature requests in the form of user stories. In addition, you and your team will gain a better understanding about creating stories that show value, are not too big, and aren’t too small to provide real value. You’ll learn how to break epics into measurable deliverables for your team. Those skills will help you succeed with agile. Software project success starts with getting good input from stakeholders and customers, regardless of which process you choose. Mike’s guidance in User Stories Applied will help your team, and your customers get this crucial part correct.

Agile Estimating and Planning provides advice from a different angle. This book explains agile techniques from all angles: release planning, iteration planning, daily planning, and modifications when reality differs from estimates (you know, like it always does).  The best feature of this book is how Mike seems to anticipate counterarguments from those in any organization that would be opposed to adopting agile. He counters those arguments with clear logic and solid explanations about why following his advice will achieve better results. From different angles, you’ll find advice for developers, project leads, customers and customer proxies. Whichever role you find yourself in, read the entire book. Knowing how other team members should be approaching the process will help build a functioning team across processes.

The key theme running through this book is that agile plans must be constantly revisited, because reality changes constantly. Agile planning is not something you do at the beginning of a project, it’s a series of ongoing activities throughout the project. That point is stresses repeatedly, and it’s worth it.

If you are looking into agile, or you’ve tried an agile process and haven’t had the success you’d hoped for, you must read these books. It will help.

Duck Typing with dynamic in C# 4.0

One of the features I find very limiting in C# (and VB.NET) generics is the way constraints work.

As you probably know, you can only specify generic constraints using interfaces, a single base class, struct or class, and the new() constraint.  That makes certain constructs very difficult.  For example, consider the following method:

   1: public static int Sum(this IEnumerable<int> sequence)
   2: {
   3:     int answer = default(int);
   4:     foreach (int d in sequence)
   5:     {
   6:         if (answer.Equals(default(int)))
   7:             answer = d;
   8:         else
   9:             answer = d + answer;
  10:     }
  11:     return answer;
  12: }

pedantic search note: Don’t copy this code, use Enumerable.Sum() instead.

Obviously, there’s nothing special about int in the above code. If I create a type that supports addition, I should be able to produce the Sum() for that collection. See the Vector struct and the similar extension method here:

   1: public struct Vector : IEquatable<Vector>
   2: {
   3:     public double X
   4:     {
   5:         get;
   6:         set;
   7:     }
   8:     public double Y
   9:     {
  10:         get;
  11:         set;
  12:     }
  13:  
  14:     public static Vector operator +(Vector left, Vector right)
  15:     {
  16:         return new Vector() { X = left.X + right.X, Y = left.Y + right.Y };
  17:     }
  18:  
  19:     public bool Equals(Vector other)
  20:     {
  21:         return X == other.X && Y == other.Y;
  22:     }
  23:  
  24:     public override bool Equals(object obj)
  25:     {
  26:         if (!(obj is Vector))
  27:             return false;
  28:         return Equals((Vector)obj);
  29:     }
  30:     public override string ToString()
  31:     {
  32:         return string.Format("X = {0}, Y = {1}", X, Y);
  33:     }
  34: }
  35:  
  36:  
  37: public static Vector Sum(this IEnumerable<Vector> sequence)
  38: {
  39:     Vector answer = default(Vector);
  40:     foreach (Vector d in sequence)
  41:     {
  42:         if (answer.Equals(default(Vector)))
  43:             answer = d;
  44:         else
  45:             answer = d + answer;
  46:     }
  47:     return answer;
  48: }

Well, that’s a lot of Ctrl-C/Ctrl-V reuse.  I hate that. Unfortunately, it’s difficult to create a nice generic method using this pattern.  The presence of a ‘+’ operator cannot be specified as a constraint. (A similar issue would occur with any other operator that could be overloaded.)

C# 4.0 and dynamic should make this possible.

Editors note: The following sample does not compile with the PDC bits for the C# 4.0 compiler. From my reading, I believe it should be valid C# 4.0. I’ll update this entry if I’m mis-interpreting the 4.0 dynamic features.

Instead of using generics (e.g. Sum<T>), using a dynamic type should make this work with any type that does support the ‘+’ operator. I should be able to create the sum of a sequence of integers,  or a sequence of Vectors, or even a sequence of strings. The sequence of strings would concatenate the strings, because that’s how + is implemented for string.

 

   1: public static dynamic Sum(this IEnumerable<dynamic> sequence)
   2: {
   3:     dynamic answer = default(dynamic);
   4:     foreach (dynamic d in sequence)
   5:     {
   6:         if (answer.Equals(default(dynamic)))
   7:             answer = d;
   8:         else
   9:             answer = d + answer; // CTP:  Error CS0019
  10:     }
  11:     return answer;
  12: }

The current CTP does not provide dynamic binding for operators yet, but I believe it’s planned.  See here. And here.

The more I think about dynamic types in C# 4.0, the more interesting uses I se for them.

Search

Go

Blog Group Links

Nascar style badges