Tuesday, August 23, 2011

Yes - Aim at Zero Technical Debt

Yes, this may sound too strict, but a breakdown of the kinds of Technical Debt may make it easier to agree with me.
It is zero of specific, very unhealthy types.

I recently stumbled upon a great post by Steve McConnell, which formulates very nicely the concept of technical debt.
(Some other great posts on this topic include developer-debt and knowing-costs-of-development-choices.)

To summarize it,
the taxonomy of technical debt is:
Non Debt
   Feature backlog, deferred features, cut features, etc. Not all incomplete work is debt. These aren't debt, because they don't require interest payments.
Debt
   I. Debt incurred unintentionally due to low quality work
   II. Debt incurred intentionally
      II.A. Short-term debt, usually incurred reactively, for tactical reasons
         II.A.1. Individually identifiable shortcuts (like a car loan) - Healthy
         II.A.2. Numerous tiny shortcuts (like credit card debt)
      II.B. Long-term debt, usually incurred proactively, for strategic reasons - Healthy

I couldn't agree more about the only healthy kinds being being the marked ones.
This leaves us with two unhealthy types.
As for the first, "Debt incurred unintentionally due to low quality work" - Hopefully you have some mechanisms in place to eliminate most of this - enough good developers in each area, mandatory code reviews etc. If not, then a whole other set of posts would be relevant.
Plus, if you’re reading this then you are probably already aware of the technical debt you are incurring :)

"Numerous tiny shortcuts"

This is the kind I want to talk about, and I realize now that my previous post on Demo Leakage may have been a sub type of.

This is the kind of debt that is accumulated  by "taking hundreds or thousands of small shortcuts--
generic variable names, sparse comments, creating one class in a case where you should create two, not following coding conventions, and so on". 
It may also include some crazy nested ifs, comparison to hard-coded inline strings, etc.
The way to present it to the business staff/upper management is depicted in a way that sounds very easy to grasp:
"..like credit card debt. It's easy to incur unintentionally, it adds up faster than you think, and it's harder to track and manage after it has been incurred"


Why is the kind bothering me?

Because the above cute description of technical debt doesn't seem to hit home with managers.

Because it is this type that makes us software craftsmen  cringe, it is this stuff that inspires posts such as this.
It is this type that we recognize us the ugly, and we know WE are going to pay off the debt, not the management that decided this debt was ok.

We know what the REAL interest rate is, even if we cannot quantify it exactly for the non technical staff.
So, why Zero Technical debt? It's Zero of this type. The other types we can handle.
The other ones will not necessarily be needed to be fixed in this version, and when they do it will probably be easily recognized and understood that it will take a significant effort.
I argue that we, developers and team leaders, should strive to be as close to zero as possible, by not introducing any of these shortcuts to begin with.I think that code containing these shortcuts is simply not ready for check-in.

Why is it not worth discussing and trying to decide if any such shortcut is worth it?

  • Because management will tend, more often than not, to think that the debt you're proposing is reasonable, because every time it's just +1$ on the credit card.
    They do not have the mental picture we do of the other $$ already there, and they definitely do not smell the code smells we sometimes do in order to realize that this one extra $ may topple everything over.
  • It's almost never worth the time estimating the "interest rate" - almost every such small hack could simply be solved during the time it would take to discuss it.
  • I think that the effort that goes into explaining this kind is not worth it. It usually involves intricate details of some implementation, and it's hard even for us to quantify the real damage.
  • And most importantly - you will almost never get the approval to pay off the debt. Until, that is, you "have to" add another feature.
    Then you're stuck in maintenance hell as well as under time pressure, with management that doesn't understand why this tiny addition requires 3 days of work.
    In my experience, you will never get a product manager's approval for a task called "refactoring X".
    We should not present it as something that can be weighed against other features. It is simply part of the work that needs to be done in order to call something "Done Done".

Concern for "over-engineering"

Would be the first response I would get to this from my manager  - but I think that's more of a danger in other types of debt.
Yes - there is the chance of some over-engineering, but you should have developers/team leaders in place who you trust to know and exercise the right balance.
As for the few cases that will escape this process and will get a bit over engineered -
Well, I would choose "wasting" an extra hour of design and a  solution that is " a little too generic" any day over wasting twice the time a week later on maintaining a hack.
And over time, 1 out of 10 times of a bit of over engineering balances out with the other 9.


And an anecdote for the end - one of the comments from the following post :
"...Likewise, if you ask any professional to do something they know is wrong or will lead to disaster, they will tell you "NO!"
-- Ask an Architect to place the walls such that they don't load the weight properly, or an electrician to wire the outlets in one big circuit, etc.
… Git R Done is good, but not if the 'Done' involves endangering the customer's software's long-term stability.
… And the only ones to blame for that is Us, the software developers, for not standing up and saying "NO!"
when the customer asks for a gun to shoot themselves in the foot."

Thursday, February 17, 2011

Demo Leakage (or 'Who moved my maintainable code-base?')


Background

I have recently been in several situations at work as well as discussions with friends from different projects and companies which all echoed of the same problems. This led me to realize that the role of demos in agile development can be very tricky and very dangerous to the code quality when abused.

So, what are demo's purposes?

There are two main types of demos:

  • End of iteration demo / Real value demo - presenting what was actually completed.
    The purpose in this case is showing real value to the customer, getting early feedback on actual features that were agreed upon, and not least important, as one of my managers recently realized - looking in the mirror and "facing the truth" about our real progress.
  • POCs - as suggested by the name, this kind of demo is usually only for proof of new concepts, usually implemented in the bare minimum way that allows the customer to receive some tangible idea of the features, but not more.
    This is a very important phase as it allows for very early feedback and if used properly, can achieve great customer satisfaction (and developer too, because who doesn't like receiving good reactions to software he's written ;)

Both kinds have many advantages, and I believe that the more frequent the better (though they should be balanced carefully with the overhead they create).
I will not elaborate much on this as these are widely accepted and practiced notions.

Where it gets tricky

BUT - what happens when there is no or little distinguishing of the two?
When it's not clear which one of them it is that you are working on, or even worse - when you’re expected to do "some of both"?

This seems to be a dangerous pitfall for teams to fall into, usually created by pressure from clients or upper management.
You start a new project and POCs of many ideas are required quickly for various good reasons - internal marketing, feedback collection etc.
But the project also has to get some real progress done quickly in order to.. well, show progress.
This is a real need that requires balance, which is hard to find.
As long as it at least realized that balance and distinction between the two are needed, it may be hard to do but it will still be possible to create a maintainable code base as you go.

When you first hear the words "of course a single iteration is not enough to build the feature properly, but can't you make sure that you demo the whole idea behind it and still write the code well and use it to continue building" - this is when you should get really worried.

This leads very quickly to hacks, patches, or simply lack of design that stay alive in the code base much longer than the developer would have ever imagined.
See phrases like "Coding Horror" and "Big Ball of Mud" to appreciate just how bad and demoralizing keeping throw-away code can be.
And then you find yourself, many iterations after the demo was shown, still working around bits of code you would have never considered to be worthy of checking in, wondering why..

A really good description from the above link of the dynamics of this happening:
"when you are prototyping a system, you are not usually concerned with how elegant or efficient your code is. You know that you will only use it to prove a concept. Once the prototype is done, the code will be thrown away and written properly. As the time nears to demonstrate the prototype, the temptation to load it with impressive but utterly inefficient realizations of the system’s expected eventual functionality can be hard to resist. Sometimes, this strategy can be a bit too successful. The client, rather than funding the next phase of the project, may slate the prototype itself for release. "

Possible solutions?

As I said, the balance is hard to find.
Several ideas could be to:

  • Decide in advance for each feature what the current iteration's purpose is.
    If it's for a POC, realize you are writing throw-away code and don't even try to do anything more.
    And be adamant about it being throw-away code.
    If it's not - be as equally adamant about not adding any quick fixes just so the demo contains one more feature which isn't really a feature yet. This is the "looking in the mirror" part.
    (and avoiding the "a bit too successful" danger)
  • Implement POCs separately - by different people on a separate branch, so there is no option for demo leakage to enter your real code.
  • Decide that for some features it isn't necessary to have the feature integrated into the real product at all and display the ideas with some form of interactive mockups

I'm sure there are more options, and sometimes even having them all doesn't guarantee an easy solution.

The bottom line I guess is to be wary of demo leakage and consciously take measures to avoid it.

Friday, April 24, 2009

A Funny Chart

A chart from GraphJam:



Gender Aware Localization in ASP.NET

Introduction

A while ago, I needed to implement localization depending also on the gender of the registered user, something obviously more relevant to languages other than English (for example, mine - Hebrew). After playing around a bit with writing my own custom ResourceManager/Provider classes, I decided to follow a suggestion I received for a much simpler way - create my own custom cultures for each required language.

Step 1 - Create your own custom culture

For each language required, create a custom "female" culture as a clone of the original culture. For example, the following code creates a custom culture named 'he-IL-f' which is a clone of 'he-IL'. Install the culture on your machine. You can verify the installation was successful by viewing your “C:\Windows\Globalization” folder after running the exe.

CultureAndRegionInfoBuilder cib =
new CultureAndRegionInfoBuilder("he-IL-f", CultureAndRegionModifiers.None);
// Populate the new CultureAndRegionInfoBuilder object with culture information.
CultureInfo ci = new CultureInfo("he-IL");
cib.LoadDataFromCultureInfo(ci);

// Populate the new CultureAndRegionInfoBuilder object with region information.
RegionInfo ri = new RegionInfo("IL");
cib.LoadDataFromRegionInfo(ri);

cib.Parent = ci;

//CultureAndRegionInfoBuilder.Unregister("he-IL-f");
cib.Register();

* The above code needs admin rights to execute, so if in Vista, run the exe “as admin”.

The fallback culture of the new culture will be the original one – this is why we have this line: cib.Parent = ci;
This enables us to only enter the strings that need changing in the "female" RESX files.

Step 2 - Create the correspondingly names RESX files

Notice that in the “female” RESX files, you only need to insert those strings which differ from their “male” version.

Step 3 - Set the thread's UICulture property

protected override void InitializeCulture()
{
string culture = "";

if (Request.Form["DropDownListCulture"] != null)
{
string gender = Request.Form["DropDownListGender"];
bool isFemale = (gender == "Female");

culture = Request.Form["DropDownListCulture"];
dir = (culture == "he-IL") ? "rtl" : "ltr";

if (isFemale)
{
culture += "-f";
}
}

Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);

base.InitializeCulture();
}

In my example I used the InitializeCulture event of the page to perform the logic. In more complex systems, we’d probably want to move this logic to a more centralized location (perhaps some event in the Application level). I receive two parameters from the form – language and gender. Of course, in a real application, we would have this information per user and use it automatically upon login.

Using the code / Points of interest

  • In the source code supplied, we register three custom cultures: en-US-f, he-IL-f, and fr-FR-f.
    You can verify this by checking your “C:\Windows\Globalization” folder after running the exe.

  • Note that the French culture was registered without this line of code: cib.Parent = ci;
    This means that instead of falling back to fr-FR, it falls back to the default culture (en-US, in our case).
    This is demonstrated when using the global resource ‘Talya’ (at the bottom of the page): its default in English is ‘talya’, and it is overridden in the he-IL and fr-FR cultures, but not in their female versions.
    When viewed with the he-IL-f culture, it falls back to the Hebrew resource ‘טליה’. However, when viewed with the fr-FR-f culture instead of falling back to ‘talya (French)’, it falls back to the English ‘talya’. This is because we didn’t set the French female culture’s parent to the French culture.

Link to complete article with source code for downloading:
Gender Aware Localization in ASP.NET