Sunday, December 13, 2009

Nullable Data Types Demo

The Problem

In several of the projects I’ve worked on, we used some token value to represent null for non-nullable types. These values would be extreme values (for int, we would use int.MinValue or int.MaxValue), or null token. Recently I’ve come across some controls that don’t like this scheme and prefer using nullable types (like int?).

The original code was written in C# 1.X, so nullable types didn’t exist; the code works, so I don’t want to convert the non-nullables to nullable and risk breaking the rest of the application.

In production, I’ve solved the problem by creating “shadow” nullable properties. The code in these properties would translate the null tokens to null. For the most part the translation code is inline.

Taking it to 11 with Generics

This weekend, I played with implementing this stuff with a generic helper class. So the code for a nullable property would look something like this:

public DateTime? DobNullable
{
     get { return NullableHelper.GetNullable<DateTime>(this.Dob); }
     set { this.Dob = NullableHelper.SetNullable<DateTime>(value); }
}

To do this I created a class called NullableHelper that contained translation functions: The to set you use this:

public static T SetNullable<T>(T? futureValue)
     where T : struct
{
     T rVal;
     if (futureValue != null)
          rVal = (T)futureValue;
     else
          rVal = NullForT<T>();
     return rVal;
}

This function echo back the value passed in UNLESS it is null; if it is null, it returns the null token. And to get, you use this:

public static T? GetNullable<T>(T presentValue)
     where T : struct, IComparable
{
     T? rVal = null;

     // I can't get the generic do == & !=, this works, so I'm 
     // going with it for now.
     IComparable ic = presentValue as IComparable;
     if (ic != null)
     {
          T CompareValue = NullForT<T>();
          if (ic.CompareTo(CompareValue) != 0)
               rVal = presentValue;
          // I already set rVal to null above.
          return rVal;
     }
     else
     {
         throw new ApplicationException(
             string.Format("NullableDataTypesDemo.NullableHelper.GetNullable<T>()" +
                  "\r\n'{0}' is can't be cast to IComparable",
                  typeof(T).ToString())
          );
     }
}

In this function I needed to cast T as IComparable to compare the value with the null token.

Both functions use a null token from the following function:

public static T NullForT<T>() 
     where T : struct
{
     T rVal;
     // Here you will need to have an if for each of the 
     // supported types, this function could be really big in production:
     if (typeof(T) == typeof(DateTime))
     {
          DateTime x = DateTime.MaxValue;
          object o = x;
          rVal = (T)o;
     }
     // Set other null tokens here
     else
     {
          throw new ApplicationException(
                 string.Format("NullableDataTypesDemo.NullableHelper.NullForT<T>()" +
                 "\r\n'{0}' is an unsupported Type", 
                 typeof(T).ToString())
          );
     }
     return rVal;
}

This function is probably the Achilles’ heel of this design. You will need an if (typeof(T) block for each type you want to support in this function. Most of the complexity points of this solution are borne by this function.

Conclusion

Over the weekend I came up with a cool way of translating from a non-nullable with null tokens to nullables. Will I implement this in production? Probably not, the code works and I don’t want to risk breaking it. If I need to do similar things in a future project, I may come back to this post.

The Whole Experiment

using System;

namespace NullableDataTypesDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create an instance of the demo entity
            DemoEntityClass x1 = new DemoEntityClass(
                1, 
                100m,
                "Jill Stephens", 
                NullableHelper.NullForT<DateTime>(), 
                NullableHelper.NullForT<int>());

            // Do some querying
            if (x1.IdNullable != null)
            {
                Console.WriteLine(string.Format("ID = {0}", x1.IdNullable));
            }
            if (x1.NumOfChildren == NullableHelper.NullForT<int>())
            {
                Console.WriteLine("Children Unknown (classic)");
            }
            // Set a DOB
            x1.DobNullable = new DateTime(1776, 7, 4);
            Console.WriteLine(string.Format("{0:d}", x1.DobNullable));
            // Set # of children and display
            x1.NumOfChildrenNullable = 2;
            Console.WriteLine("value of Number of Children: {0}", x1.NumOfChildren);
            // Unset # of children
            x1.NumOfChildren = NullableHelper.NullForT<int>();
            if (x1.NumOfChildrenNullable == null)
            {
                Console.WriteLine("Children Unknown (new)");
            }
            // Won't comile, 
            // NullableHelper.NullForT<DemoEntityClass>();
            // Some NullForT that won't compile
            // (you can fix these by adding code to NullForT())
            try
            {
                NullableHelper.NullForT<DemoStruct>();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            try
            {
                NullableHelper.NullForT<double>();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            // Force read line, so this won't go away in VS
            Console.ReadLine();
        }

    }
    /// <summary>
    /// My Demo Entity Class
    /// </summary>
    class DemoEntityClass
    {
        public DemoEntityClass(int id, decimal amount, string name, DateTime dob, 
            int numOfChildren)
        {
            this.Id = id;
            this.Amount = amount;
            this.Name = name;
            this.Dob = dob;
            this.NumOfChildren = numOfChildren;
        }
        public int Id { get; set; }
        public decimal Amount { get; set; }
        public string Name { get; set; }
        public DateTime Dob { get; set; }
        public int NumOfChildren { get; set; }

        public int? IdNullable
        {
            get { return NullableHelper.GetNullable<int>(this.Id); }
            set { this.Id = NullableHelper.SetNullable<int>(value); }
        }
        public decimal? AmountNullable
        {
            get { return NullableHelper.GetNullable<decimal>(this.Amount); }
            set { this.Amount = NullableHelper.SetNullable<decimal>(value); }
        }
        // string is nullable, so I am including this for completeness
        public string NameNullable
        {
            get { return this.Name; }
            set { this.Name = (string)value; }
        }
        public DateTime? DobNullable
        {
            get { return NullableHelper.GetNullable<DateTime>(this.Dob); }
            set { this.Dob = NullableHelper.SetNullable<DateTime>(value); }
        }
        public int? NumOfChildrenNullable
        {
            get { return NullableHelper.GetNullable<int>(this.NumOfChildren); }
            set { this.NumOfChildren = NullableHelper.SetNullable<int>(value); }
        }
    }
    /// <summary>
    /// My Demo struct
    /// </summary>
    struct DemoStruct
    {
        public int x;
        public int y;
        public int xy { get { return x * y; } }
    }
    static class NullableHelper
    {
        /// <summary>
        /// Gets the Null marker value for value types
        /// </summary>
        /// <remarks>
        /// In some old applications that date back to before nullable types
        /// maked fields as being null with extreme values (int.MaxValue for
        /// example).
        /// </remarks>
        /// <typeparam name="T">Tye value type being tested</typeparam>
        /// <returns>The Null marker value</returns>
        public static T NullForT<T>() 
            where T : struct
        {
            T rVal;
            // Here you will need to have an if for each of the 
            // supported types, this function could be really big in production:
            if (typeof(T) == typeof(int))
            {
                int x = int.MaxValue;
                object o = x;
                rVal = (T)o;
            }
            else if (typeof(T) == typeof(decimal))
            {
                decimal x = decimal.MaxValue;
                object o = x;
                rVal = (T)o;
            }
            else if (typeof(T) == typeof(string))
            {
                string x = string.Empty;
                object o = x;
                rVal = (T)o;
            }
            else if (typeof(T) == typeof(DateTime))
            {
                DateTime x = DateTime.MaxValue;
                object o = x;
                rVal = (T)o;
            }
            else
            {
                throw new ApplicationException(
                    string.Format("NullableDataTypesDemo.NullableHelper.NullForT<T>()" +
                      "\r\n'{0}' is an unsupported Type", 
                    typeof(T).ToString())
                );
            }
            return rVal;
        }
        /// <summary>
        /// Used in a set block to set a non-nullable
        /// field from a nullable value
        /// </summary>
        /// <remarks>
        /// Since the null values are gotten from NullForT, this function can be short
        /// </remarks>
        /// <see cref="NullForT"/>
        /// <typeparam name="T">Tye value type being set</typeparam>
        /// <param name="futureValue">nullable new value</param>
        /// <returns>non nullable value  with Null marker value to denote null</returns>
        public static T SetNullable<T>(T? futureValue)
            where T : struct
        {
            T rVal;
            if (futureValue != null)
                rVal = (T)futureValue;
            else
                rVal = NullForT<T>();
            return rVal;
        }
        /// <summary>
        /// Used in a get block to get a nullable field
        /// from the non-nullable value
        /// </summary>
        /// <remarks>
        /// This function is a little more complex because we need to cast
        /// presentValue to IComparable to compare it to the null token 
        /// </remarks>
        /// <see cref="NullForT"/>
        /// <typeparam name="T">Tye value type being set</typeparam>
        /// <param name="presentValue">
        /// non-nullable value with Null marker value to denote null
        /// </param>
        /// <returns>nullable value returned</returns>
        public static T? GetNullable<T>(T presentValue)
            where T : struct, IComparable
        {
            T? rVal = null;

            // I can't get the generic do == & !=, this works, so I'm 
            // going with it for now.
            IComparable ic = presentValue as IComparable;
            if (ic != null)
            {
                T CompareValue = NullForT<T>();
                if (ic.CompareTo(CompareValue) != 0)
                    rVal = presentValue;
                // I already set rVal to null above.
            }
            else
            {
               throw new ApplicationException(
                 string.Format("NullableDataTypesDemo.NullableHelper.GetNullable<T>()" +
                      "\r\n'{0}' is can't be cast to IComparable",
                 typeof(T).ToString())
               );
            }
            return rVal;
        }
    }
}

Thursday, November 26, 2009

The Gu Rickrolled no one

You may have heard about Scott Guthrie’s Rick Astley related stunt at the PDC in Los Angeles last week. I don’t think it qualifies as a rickroll. As a developer who uses Microsoft technologies, I have sworn to The Gu’s super-duper deluxe uber-geekiness.

IMO, to qualify as a rickroll, you must willfully click on a link expecting something juicy and end up on suffering through “Never Gonna Give You Up”. You make a bad decision that makes you worthy of such punishment.

If you clicked on a link for Paris Hilton’s latest video (wink, wink) and instead got Rick, you have been rickrolled! If you clicked on a link marked Rick Astley’s famous video, you haven’t been rickrolled, you are getting what you expected.

At the PDC, The Gu’s victims didn’t have the free will not to click the link or take the red pill. In this situation, The Gu launched the video.

If one of The Gu’s minions changed out the “real” video with Rick Astley without his knowledge, then he is the only rickrollee.

It could be argued that the attending a Scott Guthrie presentation is bad decision that qualifies one to be worthy of being rickrolled.

Thursday, November 12, 2009

Rant on XML Comments

The purpose of .NET XML comment is to help you generate outside documentation. When you tell Visual Studio to generate the XML file, you get tool tips when you hover over your method and you can use a tool like SandCastle to generate MSDN style help files based on your XML Comments. XML comments are really designed for Black Box documentation of Frameworks and API.

An XML comment is not the same as a development comment. When I am looking at a SandCastle generated help file, I care about how to use your class or method; I don’t care about why you chose to use a bubble sort over a quick sort.

I’ve seen comments like this:

///<summary>
/// DV
///</summary>

where DV is the initials of one of the developers. If you were actually use the XML comment feature, this comment would show up when you mouse over the class in Visual Studio or in the help file you created with SandCastle, same with this comment:

/// <summary>
/// This is the FooBar class!
/// I originally designed on a napkin after midnight after drinking 
/// a fifth of JD & eating 3 orders of hot wings.
/// I was inspired to write this class after Ellen, the goddess of 
/// the 327th Ave NE Pub down the street from my studio apartment,
/// who rejected me after I drank myself silly summoning up the 
/// courage  to ask her out.
/// </summary>

With the VS tooltip feature you lose all formatting, so if you have a comment like

/// <summary>
/// Foobar Class
///
/// Combines Bars with Foos and converts them from Metric to Imperial
///
/// 2005-04-13 – RN – Original implication 
/// 2005-05-03 – RF – Bug #12239 – g to # conversion issue
/// 2008-03-27 – VW – re-Implement Linq to Foo framework
/// </summary>

will appear on 1 or 2 lines without the linefeeds. The tags are ignored when VS creates tooltips.

I know that most of the projects I work on are not frameworks or API, but it still feel the need to avoid using XML comments for things other what I think they are designed for.

I am uptight about XML comments because I have used them, once. We created a help file for a client using SandCastle; the customer was impressed, though I bet they never used it.

Sunday, October 11, 2009

How bad is too bad?

I have been assigned the task of updating a program that isn’t terribly well designed.

How deep do I dive in trying to fix this program? Do I take all of the inappropriate code from the Form Classes and move them into Business Classes? Do I get rid of all the SQL queries in code? Do I tear the whole thing to shreds and start over with my brilliantly planned super object oriented design applying all 23 GoF design patterns? My inner geek screams yes, Yes, YES! But according to Joel Spolsky, rewriting software just to do it can be the biggest mistake “you” can make. (http://www.joelonsoftware.com/articles/fog0000000069.html).

I mean, the program basically works. If the world around the program wasn’t changing, it could probably go unchanged. But we’re replacing one system that this program talks to and replacing it with another one.

Looking at the code, I would date the program to around the 2003 timeframe with stuff added later as the program evolved. I see:


  • Int32 in place of int, String in place of string, etc. (I remember reading and hearing experts recommending that practice in the early days of .NET)

  • Long unstructured functions in the form class. Sort of like VB6 in C#, we know that a lot of that went on in the early 0's.

  • There is some object orientation grafted on to the edges of the program but the core is pretty old fashioned pre-OO .NET 1.0 code.

  • Uses finally in places where rational modern C# coders would use using.

So it isn’t wonderful code, but, for the most part, it works. But like any spoiled brat, I just wanna do it. Why: just because.

The program was written in the ancient times of yore (5 years ago). When .NET first came out, OO was just starting to catch on in the mainstream. Right now I am hearing rumors that the next big thing is going to be Functional programming; the justification is that it is easier to parallelize so elements of the Functional Fad will probably stick around for the long term. What will my elegant OO code look like to the Functional masters of the future?

Tuesday, September 22, 2009

SQL Funny Business

Today at work we were talking about mixing old (table, table where join criteria) and new (table join table on join criteria) style joins in the same query. So that got me to thinking about what ON really does. I ran the following queries against the infamous Northwind Database:

-- The modern style join:
SELECT *
FROM dbo.Orders o
 JOIN [Order Details] od ON od.OrderID = o.OrderID 
WHERE o.CustomerID = 'SUPRD'

-- The classic style join:
SELECT *
FROM dbo.Orders o, [Order Details] od
WHERE od.OrderID = o.OrderID
 AND o.CustomerID = 'SUPRD'

-- Inverted modern join (join criteria in WHERE, selection criteria in ON):
SELECT *
FROM dbo.Orders o
 JOIN [Order Details] od ON o.CustomerID = 'SUPRD'
WHERE od.OrderID = o.OrderID

All three queries gave me the same results set.

Is the ON clause just a different place to shove selection criteria (a phantom where). I like the modern style. You can put all the join information together. You could mix things up when you practice Job Security Based Programming.

I think it would be fun to have a obscure SQL programming competition, like the obscure C programming competitions in the days of yore.

Monday, August 31, 2009

Is it too late to wrap it into a function?

Today I was refactoring several properties in a Business Class from int to int?. I was in a hurry, so I was implementing the 10 properties using cut and paste inheritance (as if that is ever really faster).

After the 8th or 9th properties, the light went off in my mind: I should write a couple of functions to do the hard work and call the functions from the property get and set functions. Was it too late to to do it right?

I said NO.It did take me longer to write the functions than it would have taken to cut and past the 2 remaining properties. However, the code is easier to read, validate and change.

I guess I justify my earlier sloppy coding time as an opportunity to think of the right way. During the first go round I was planning to do the second one; I needed to do it wrong to see how to do it right.

Friday, July 31, 2009

New Team, New Ways of Thinking

I have recently started working on a new team.

The new team uses a different architecture, different naming conventions, file organization, source control, etc. The new team is uptight about different things than the old team.

I am learning to think differently, reevaluate my old ways and adapt the new things from the new team.

In the long run, I think it is a good thing; I need change to avoid ruts and learn new ways of approaching problems

Monday, June 15, 2009

Access 2007 Trap: Life without @@identity()

I came across some interesting behavior in Access 2007 that tripped me up for a little while.I needed to add a record to a table and then get the primary key value of the newly added record.

I wrote the code that I expected to work and I always got back the same number for the primary key every time I ran the code; the value of the primary key value of the first record.But I wanted the primary key value of the last record, the record that I just created. So, I added .MoveLast to get the last record.

   Dim rs As Recordset2
   Dim recordId As Integer
   Set rs = Application.CurrentDb.OpenRecordset("Table_1")
   With rs
       .AddNew
       !field_1 = "Field_1"
       !field_2 = "Field_2"
       ' I expected the the primary key value to be loaded here:
       .Update
       ' When you open a recordset, there is an implied "MoveFirst" call
       ' For whatever reason, Access doesn't refresh values after writing
       ' the record:
       .MoveLast
       ' Without the MoveLast, you get the record_id of the first record:
       recordId = !record_id
       .Close
   End With
   Set rs = Nothing

I guess the thing that screwed me up is that I expected all of the fields in the current record of a RecordSet2 point to the same record. In my mind, when I call Update, the value of the primary key should be retrieved and ready for me to reference.

In ADO.NET, the DataSet has the AcceptChanges() method. This is logical to me because a DataSet is disconnected. I guess a Recordset2 is "loosly" connected.

Access continues to weird me out.

Friday, May 22, 2009

Stackoverflow Flair without the image

Scott Hanselman tweeted about how cool it would be to have a Stackoverflow flair without a tje gravatar. This is my solution using CSS

<html>
<head>
    <title>Stack Overflow Flair Demo</title>
    <style type="text/css">
.valuable-flair
{
    background-color: #ffffff;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 12px;
    height: 50px;
    padding: 3px;
    width: 50px;
}
.gravatar
{
 display: none;
}

.valuable-flair .badge1
{
    color: #ffcc00;
}
.valuable-flair .badge2
{
    color: #c0c0c0;
}
.valuable-flair .badge3
{
    color: #CC9966;
}

</style>
</head>
<body>
<script src="http://stackoverflow.com/users/flair/3819.js?theme=none" type="text/javascript"></script>
</body>
</html>

Thursday, May 14, 2009

ASP.NET Cookieless sessions and JQuery AJAX

I was trying to get some jquery ajax calls to work after converting a site to cookie-less sessions (don't ask). I noticed the following code wouldn't work:

$.post("/bin/getsomedata.dll", {'id': id},
   function(data)
   {
       doSomething(data);
   }
);

I changed it to:

$.post("<%=Response.ApplyAppPathModifier("/bin/getsomedata.dll")%>", {'id': id},
   function(data)
   {
       doSomething(data);
   }
);

and all was groovy

I also noticed that when I turn off cookieless sessions, everything still works. It appears that Response.ApplyAppPathModifier() only alters the URL when I am in cookieless sessions

Thursday, May 07, 2009

Photoviewer

Today I needed to write a pop up form that would show any image without any extra IE (or Firefox, Chrome, Safari, etc.) toolbars, etc. To get a web form to display an arbitrary image, I have a simple page that takes the URL of the image in the query string. The web form takes that string and uses it to set ImageUrl of an asp.image control.

photoviewer.aspx:
<%@ Page Language="C#" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">    private void Page_Load()
    {        string file = Request.QueryString["file"];
        if (file != null)
            myImage.ImageUrl = Server.UrlDecode(file);
    }
</script>
<html>
<head>
    <title>My Image Viewer</title>
</head><body>
    <form id="Form1" method="post" runat="server">
    <table style="border:0;">
        <tr>
            <td>
                <asp:Image runat="server" ID="myImage" EnableViewState="false" />
            </td>    
        </tr>
        <tr>
            <td>
                <input type="button" onclick="javascript: window.close();" 
                    value="close" />
            </td>
        </tr>
    </table>
    </form>
</body>
</html>

To get rid of extra browser features and to size the popup, I call window.open() I set the height and width to values that will ensure that I have enough room for the image (I can use to get the dimensions of an image and add some buffer around the image so the popup isn’t all picture). I suppress status, toolbar, menubar. Location and scrollbars by setting them all to “0”.

feature_win.js
// declared outside of the scope of the function so it will persist on the page:
var newWindow;
function featureWin(height, width, url)
{
    var lHeight = height;
    var lWidth = width;
    
    if (newWindow && !newWindow.closed) 
    {
      newWindow.close()
    }
    newWindow = window.open(url, null, 'height=' + height +  
       ',width=' + width + ',status=0,toolbar=0,menubar=0,location=0,scrollbars=0');
}

Here we put it all together.

default.html
<html>
<head>
    <title>Test My Image Viewer</title>
    <script type="text/javascript" src="feature_win.js"></script>
</head>
<body>
<a href="javascript: featureWin(825, 1050, 'photoviewer.aspx?file=Garden.jpg');">
    Garden</a>
</body>
</html>

Sunday, April 26, 2009

URL Builder Functions

I am working on an internal web application that does some state management through the query string. That being the case, I have to add, remove and change paramters in the query string. Here are the functions that I use to edit the URL to change these varables

// Takes a URL with parameters and replaces the current value of a given parameter 
// with the new value provided
public static string replaceParameterInUrl(string theUrl, string param, string newValue)
{
    StringBuilder sb = new StringBuilder();
    string[] parts = theUrl.Split(new char[] { '?', '&' });
    sb.Append(parts[0]).Append("?");
    for (int i = 1; i < parts.Length; ++i)
    {
         string thisParam = parts[i];
         string[] paramParts = thisParam.Split(new char[] { '=' });
         if (paramParts[0] != param)
             sb.Append(thisParam).Append("&");
    }
    sb.Append(param).Append("=").Append(newValue);
    return sb.ToString();
}
/// Takes a URL with parameters and replaces the current value of a given parameter 
/// with the new value provided
public static string removeParameterFromUrl(string theUrl, string param)
{
    StringBuilder sb = new StringBuilder();
    bool isFirstItem = true;
    string[] parts = theUrl.Split(new char[] { '?', '&' });
    sb.Append(parts[0]).Append("?");
    for (int i = 1; i < parts.Length; ++i)
    {
        string thisParam = parts[i];
        string[] paramParts = thisParam.Split(new char[] { '=' });
        if (paramParts[0] != param)
        {
            if (!isFirstItem)
                sb.Append("&");
            sb.Append(thisParam);
            isFirstItem = false;
        }
   }
   return sb.ToString();
}

If I was going to use these functions in a public facing site,I'd probably sort the parameters alphabetically, so there would be less flux in the URLs that Google would see

Sunday, April 19, 2009

On Small, Low Margin Projects

Part 3: Framework and Starter Application

To get off to a quick start, I would imagine finding or building (or a combination of the two) an application framework before I start selling my services to the small business-person. Most small business applications need CRUD and List forms; user accounts and various levels of access; logging;; an about window; error and audit logging; etc.

The platform doesn’t really matter as long it is robust enough to solve the business problem. You need something that is robust enough to solve the current business problem and accept the changes that will take place (sooner: you misread a requirement, later: new work and more revenue). The toolset should support modern scalable designs; the software may be the seed of an enterprise system.

I know that this is like having a hammer and making everything into a nail. At this kind of margin, you can’t afford to handle any technology that the customer may want to use. If it isn’t a nail and can’t be made into a nail, have someone else do it; margins are too tight to take anything.

The first few projects will probably be losers as you work out the kinks in the framework and your process.

With a starting point, we can get our customer something to show them after the first sprint. We can show the customer something quickly and have something to talk about when we plan the second sprint. If we come back to them quickly with something to show, they will feel involved in the process.

Monday, April 13, 2009

On Small, Low Margin Projects

Part 2: Short Interactions and Feedback

In a previous post, I wrote about some of the issues I have experienced with small projects and suggested that Waterfall, Cave of Solitude solutions tend to blow up in your face.

If we come back to the customer every 2 to 4 weeks, we can both learn about the other side of the transaction:


  • I can teach the customer how the process works, what is possible, what is easy and isn’t going to happen. If you are going to buy custom software you need to learn the process. It is like buying a new building; you aren’t going to use the gold plated faucets in the penthouse when we are still pouring concrete (I will restrain myself from going on for hundreds of words of half baked analogies).

  • The customer can teach me more about his business, what he expected the software to do and I missed on the first time around. What is important? What is a nice to have? I may have the opportunity to see the processes that are too complex to explain.

  • Customers like to feel that they are part of the process. If you are going to spend a few grand on something, you want a sense of what is going on. Buying custom software is a big part of the company’s activities and the owner may want to boast about it on his blog on in the local watering hole.

If we started at ground zero and only used Scrum like sprints but no initial framework, the first couple of sprints would give the customer little sense of satisfaction.

Sunday, April 12, 2009

On Small, Low Margin Projects

Part 1: The Problem

I have been involved in some small (low margin) projects for small business customers using a classic Waterfall type process where we get all of requirements from the customer and go into our digital cave for a few months to write our masterpiece. When we came back to the customer (usually late) to show them their shinny new program, we find that we got it wrong. On one program, we get calls from the customer as he discovers features.

The customer can’t tell us what they want in language we understand; they may know what they want, but not in our language. The customer may not have any experience buying custom software. We don’t know the business and its problems well enough. I think the effects of these problems can be mitigated by delivering more often (a la Agile).

If we were to go more agile, we would need a way to get something to the customer quickly. I don’t think a customer in this market would be impressed if, after the first sprint, I showed him a base list window, a base edit window and basic security. We need a base framework and possibly some modules that exist before the first sprint for the customer.

Wednesday, March 25, 2009

Earn CEO Pay Level from Home

As I was riding the bus to work I saw the sign above. I didn't make the call because I know that it some sort of a scam; I am still curious as how you would do this. Since some CEOs are now earning the princely sum of $1, I thought I would share some of my ideas:

  • Go to several convience stores and look for the little cup that says "Give a Penny, Take a Penny" and take all of the pennies. After 10 or 20 stores, you may earn your CEO's pay

  • Rummage through your couch for change.
  • Rummage through your car for change.
  • Rummage through your friend's and family's couchs and cars.
  • Use your God given talents, for example:
    1. Get your trusty old Marine Band Harmonica, a hat, cardboard and a Sharpie.

    2. With the Sharpie, write "Give me a CEO's Pay Level and I will stop playing" on the cardboard.
    3. Get on a bus, subway or some other place where it diffcult to leave, display the sign, put out the hat and start playing.

    If you play like me, there is a high risk of incarceration; with Correctional Industries pay rates, you can earn your CEO's pay in 5 or 6 hours of work

Saturday, March 14, 2009

Trying to get socially modern

What I'm doing, part 1.5

Besides Stack Overflow, I've been trying to actually write the blog and use twitter.  I'm actually writing to this blog (sort of) and I have created over 15 updates in twitter as jrcs3.  I'm following almost 10 people and have more than 2 people following me. So far I've taken a lot more than I've given.

I developed tunnel vision over the past few years. Get the code out and get on with my life. I work mainly in C# 2.0 in both ASP.NET and WinForms (no WPF yet). I also have a client that needs me to work in Access 97 and a SQL 200 ..... enough all ready

Anyway, with the ecomomy really freaking out, I need to look outside of my formally safe world of oldtime geekdom and look out to the wider world. I can go on about the scary stoff, but I am also intereted in the new opportunities that the ecomonic stress can create.

Sunday, March 01, 2009

StackOverflow

What I'm doing. Part 1

For the past 6 months or so, I have been playing with StackOverflow. I don't know that I've really gotten into it, but I still find it interesting.

Structured Discussion

It's like a discussion board but there are more rules and other forms of structure. Some of it is built into the application but most of it is enforced by the powerful menders of the community (see Badges and Reputation). Duplicate questions can also be closed as duplicate, so the answer is gathered in 1 place (though I would like to see them tighten up the support for this so it is easier to get to the parent question).

The discussion is pretty tightly focused on programming; whenever a non-programming question is asked it will either be voted down or closed by the powerful. It is good thing that the site stays on task, but there are times when "fun" questions are closed and I would have liked to see them live on. But, what is fun for one is noise for someone else; I can find "fun" someplace else.

Voting

Users of the system have to power to vote for or against any question or answer on the system. Questions with a lot of votes are considered "hot" and the questions with a lot of down votes end up in some sort of puritory. The users of the site decide what gains capital on SO. Voting also generated (and destroys) reputation.

Badges and Reputation

This is one of the official ways of controlling our behavior are Badges and Reputation.

For the most part, you get (and loose) repuptation through voting. Your reputation is dispalyed next to your profile. You are given more power as you gain repretation. The early powers are basic things like voting, leaving comments, the later powers are the godlike power of an administrator. As you do things to earn reputation, you are invested in site and are more likely to use your powers good.

You can earn badges by doing certain things that the sight owners want you to do. Your badge info is next to your reputation on your profile. Like the Olympics, you they come in bronze, silver and gold. The bronze metals are for firsts (getting through basic training, solder's first parashoot jump, etc.), moderate metrics of your questions and answers. Silver is for bigger first and bigger metrics. Gold for the big stuff.

Wednesday, February 18, 2009

What I'm Doing

The problem: I need to write to learn, but when I write, try to hard to make it perfect and eventually give up. The words get in the way of the idea. In fact, I spent several cycles trying to get this paragraph right and I am not satisfied with it even now.

I need to write to get over the hump of the perfect. I need to write %$#! so I can eventally write well. One way to learn is to explain it. Yada Yada Yada.

So I'm going to write a series of blog posts about what I'm doing. Mabie something will stick . . .