| Table Of Contents
|
Since the release of the .Net Framework in February 2002, the software industry has seen a huge increase in single developers entering the field. Microsoft (with all of it's problems) has changed the landscape for software development. Most low level details have nothing to do with business logic, which is what most developers are hired to do. Jeffery Richter's thoughts on the .Net Framework are: "...after using the .Net Framework for several years...I would never go back to the old ways of software development...I just can't believe that we programmers put up with it for as long as we did."
Okay, so much for my soapbox speech. Needless to say that if someone is wondering whether or not to learn a .Net language, I have a bit of advice: "Embrace the future".
With all of operating costs that are saved by being a single developer, there are also some disadvantages. The primary reason being that you are the entire creative engine for your company. If you're having a bad day, the whole company is having a bad day! Also, it's easy to get complacent, or the bad one: "sloppy" when no other developer is using your code directly.
I say these things because as a single developer, I have experienced these challenges many many times. Over the years, I have picked up tips and tricks that have been invaluable to my career. Please keep in mind that I'm not writing a syllabus on how things must be done. It's only my intent to pass along some wisdom, and if you see things differently, we can agree to disagree.
While writing code, it's easy to keep everything organized in your head. Just remember that a year from now, you WILL forget how you structured today's code.
Tip: Write code that targets an outside audience. If you think that another software company would not be able to understand it, there is a huge possibility that you won't understand it a year from now. Let's look at a "customer information" data structure that I have written for myself:
public struct Cust
{
public string Address;
public string Date;
public string ContactAddress
}
- The Address field stores the: Street, City, State and Zip Code. (It's compact right?)
- The Date field stores the customer's date of birth. (Why bother with a name like DateOfBirth when I know what it means?)
- The ContactAddress field stores the customer's email address. (Anybody can differentiate between Address and ContactAddress right?)
All of this "makes sense" to me now but we all know that this design would not pass the first meeting in a developer group. To target an outside audience I would need to make some obvious changes.
public struct CustomerInfo
{
public string Address;
public string City;
public string State;
public string Zip;
public DateTime DateOfBirth;
public string ContactEmail;
}
In one glance, any developer could grasp the scope of this structure. This also saves me time in future, should I need to review/edit this structure.
Sometimes I encounter a code section that's difficult to wrap my brain around. When that happens, I have learned that I save much more time by initially coding "dirty" (lengthy), figuring out the appropriate algorithm with the debugger by "stepping through" the code, then cleaning up my code. For example, if you are working on a line of code that has a complex calculation, break the calculation into several lines of code with separate variables so you can see how each line works with the other. The term "debugger" is actually a very narrow term when you can work on complex code in real time. (Just remember to clean up the dust)
Bill Gates once said: "...The number of lines of code is your enemy." What does that mean? The more untested code that you write will lead to longer debug sessions. (and more bottles of aspirin) This is simply because NO ONE has ever written a perfect app on the first round. In the past two years, I have developed of rule of thumb called: Centralize and Generalize.
Centralize
If a block of code is used more than twice, centralize it into a method or function. This rule would also apply to short but complicated algorithms. By centralizing, you are reducing the amount of code that must be debugged.
In the example below, I had been developing a text editor that allowed the user to specify the font in a variety of ways: popup dialogs, tool bar buttons, etc. With all of this code, I found myself using the same code block for each one. With the code below, I centralized that block into a function that returns a new font based on three parameters.
public Font BuildFont(FontFamily fontFamily, float size, FontStyle style)
{
if (fontFamily == null)
throw new ArgumentException("FontFamily cannot be null!");
if (size <= 0)
throw new ArgumentException("size must be greater than zero!");
bool errorCreatingFont = false;
FontStyle styles = FontStyle.Regular;
FontFamily ff = fontFamily;
if (((style & FontStyle.Bold) == FontStyle.Bold) && ff.IsStyleAvailable(FontStyle.Bold))
styles |= FontStyle.Bold;
if (((style & FontStyle.Italic) == FontStyle.Italic) && ff.IsStyleAvailable(FontStyle.Italic))
styles |= FontStyle.Italic;
if (((style & FontStyle.Underline) == FontStyle.Underline) && ff.IsStyleAvailable(FontStyle.Underline))
styles |= FontStyle.Underline;
if (((style & FontStyle.Regular) == FontStyle.Regular) && !ff.IsStyleAvailable(FontStyle.Regular))
{
if (ff.IsStyleAvailable(FontStyle.Bold))
styles |= FontStyle.Bold;
else if (ff.IsStyleAvailable(FontStyle.Italic))
styles |= FontStyle.Italic;
else if (ff.IsStyleAvailable(FontStyle.Underline))
styles |= FontStyle.Underline;
else
errorCreatingFont = true;
}
if (errorCreatingFont)
throw new Exception("Error creating font type: " + ff.Name + "!");
return new Font(ff, size, styles);
}
Since I have tested the BuildFont() function, I can reference it with the same confidence as any other .Net method.
Generalize
To generalize, write a function that can work in a variety of scenarios. Let me give you an example. Let's say that you are developing a function that filters the last name of your customers.
public string[] FilterCustomer(string[] lastNames)
{
if (lastNames == null || lastNames.Length == 0)
return new string[0];
List<string> filtered = new List<string>(lastNames.Length);
for (int i = 0; i < lastNames.Length; i++)
{
if (lastNames[i] != null && lastNames.Length > 0 &&
lastNames[i].ToUpperInvariant().StartsWith(someInternalVar))
{ filtered.Add(lastNames[i]); }
}
return filtered.ToArray();
}
I hope you can see that the function above is painfully restrictive. Primarily because it does not allow the caller to specify which names that should filtered. There is also some other things that we could change to make this a better generalized function. For example, the
lastNames parameter restricts the caller to use an string array. To generalize this, we could use IEnumerable as accepted input. Also, it would be good to allow the caller to specify if the filter should include or exclude the filter criteria. Let's take a look at the generalized version:
public string[] FilterCustomer(IEnumerable<string> lastNames, string startsWith, bool excludeMatch)
{
if (lastNames == null)
return new string[0];
startsWith = startsWith.ToUpperInvariant();
bool blnStartsWith;
List<string /> filtered = new List<string />();
foreach (string str in lastNames)
{
blnStartsWith = (str != null && str.Length > 0 &&
str.ToUpperInvariant().StartsWith(startsWith));
if (blnStartsWith != excludeMatch)
filtered.Add(str);
}
return filtered.ToArray();
}
The lastNames parameter can now accept any input that implements IEnumerable. That means that if the caller decides to use a generic List<string> instead of an array, we won't have to change the function's parameter definition. The important startsWith parameter allows the caller to specify the starting character(s). And finally, a powerful little parameter that allows the caller to choose what type of filtering results are returned. (Read xml comments for excludeMatch)
Obviously, there are many other things that could be done to generalize this even more but hopefully you get the idea.
By the way, let me give you a tip concerning: startsWith.ToUpperInvariant(). I learned from a Microsoft insider that ToUpperInvariant() executes faster than ToLowerInvariant().
I know that we must all be conscious of writing bloated/slow code, but I can tell you that your customers will give you much more grief over bugged software than slow software. Only write unmanaged code when you are certain of it's necessity! Writing unmanaged code is like diving into shark infested waters so be sure that you're a good swimmer! The .Net Framework is certainly not perfect but advances are being made every day to make it better.
One of my pet peeves is poorly formatted code. It may execute perfectly but if you have to be the compiler to read it, it's not formatted right. Here's a simple example of poorly formatted code. (My apologies if this is your coding style)
if (blnEditingInfo == false)
{
editButton1.Text = "Edit Customer";
}
else
{
editButton1.Text = "Save Changes";
}
What a waste of valuable screen space when it could be written like this:
editButton1.Text = !blnEditingInfo ? "Edit Customer" : "Save Changes";
While there is a lot of debate on the value of using "shorthand", I personally feel that it's a great way to condense several lines of code into one.
This rule has to be the hardest for me to follow. Being the boss and the employee, it's easy to create slave out of yourself. Just remember: "There will always be deadlines". It doesn't matter how hard and long you work to "get ahead" because another deadline will be waiting in line to demand your attention. This can quickly lead to "burnout", in which you have little motivation and nearly zero creativity! My advice is to give yourself a responsibility free day once a week. Sound like too much? I can assure you that the benefits are far worth it! If you feel like you're already suffering from burnout, take a look at this article. If you can't relate to it, your not in the software industry!
There are many other things that we could discuss but I feel that we have covered/uncovered some important areas that will make your programming career better. As a single developer, you never know when you may land a consulting job that requires you to work with a third party developer. The code concepts that we discussed will save you (and the third party) a lot of grief.
The software industry in certainly not a dead end street! There is always something new to discover and I welcome any tips or tricks that you have to share. Okay! I'm out of here. I'm going to try and work on that last topic...