Monday, July 23, 2007

A Terser Visual Basic?

There’s a rather interesting post going on at Panopticon Central right now, dealing with the terseness of Visual Basic.NET. In a rather obtuse attempt at reducing the “terseness” of the language, Paul Vick hacks a VB.NET compiler to coerce all the keywords to lowercase. While it’s a nice look, it doesn’t solve the common complaint that Visual Basic is inherently verbose.

Visual Basic’s verbosity problem likely stems from the fact that we VB developers commonly have to tell the compiler lots of things that it already knows or should be able to tell from the syntax. To see what I’m talking about, let’s take a piece of the original NValidator code that was written in VB and see what it looks like:

Namespace Validation

Public Class ConnectionValidator
Inherits ParameterValidatorBase

Friend Sub New(ByVal connection As IDbConnection, ByVal name As String)
MyBase.New(connection, name)
End Sub

Public Function
IsClosed() As ConnectionValidator
If Not Connection Is Nothing Then
If (Connection.State And ConnectionState.Closed) = 0 Then
Throw New ArgumentException("Operation requires a closed connection.", Name)
End If
End If
Return Me
End Function

Private ReadOnly Property
Connection() As IDbConnection
Get
Return DirectCast
(InnerValue, IDbConnection)
End Get
End Property

End Class

End Namespace


Now, it bears noting that every blue word in there is a keyword. That's a lot of keywords. So yes. Visual Basic is verbose. I suspect that the Visual part of Visual Basic has ceased to be "visual" as in "visual design" and has somehow become "visual keywords." But I digress.

First, lets give Paul the benefit of a doubt. Let’s see what it looks like with everything converted to lower case.

namespace Validation

public class ConnectionValidator
inherits ParameterValidatorBase

friend sub New(ByVal connection as IDbConnection, byval name as string)
mybase.New(connection, name)
end sub

public function
IsClosed() as ConnectionValidator
if not Connection is nothing then
if (Connection.State and ConnectionState.Closed) = 0 then
throw new ArgumentException("Operation requires a closed connection.", Name)
end if
end if
return me
end function

private readonly property
Connection() as IDbConnection
get
return directcast
(InnerValue, IDbConnection)
end Get
end property

end class

end namespace


Well, it does look a lot more like C#. And yes, the lower case does help to reduce the visual WHAM! of the keyword volume. But it’s the same number of words, so we haven’t addressed the terseness. To address the terseness, you actually have to reduce the number of words. So how do we do that? You get rid of the words that aren’t actually doing you any good.

First and foremost, get rid of all the words that accompany the End keyword. They’re superfluous. With proper indentation, that’s pretty obvious. So in this case, that’s End Namespace, End Sub, End Property, End If, End Function, and End Get. Believe me, if the End doesn’t have a corresponding block statement, the compiler will let you know.

Next comes the As keyword. Let’s accept the fact that identifiers can’t have spaces in them. Therefore, anything that follows an identifier in a variable, property, or method declaration must, by definition, be a data type. The As keyword isn’t getting us anywhere. Drop it.

The Then keyword on If blocks is equally useless. It needs to go.

The Sub, Function, and Property keywords are completely useless. They’re used to tell the reader whether or not a method returns a type. They certainly don’t do anything for the compiler: the compiler gleans this information from the method’s signature (it’s arguments and return type). If the method has a getter and a setter, it’s a property. So these could be removed as well.

Finally, the ReadOnly keyword is useless on our property, since it doesn’t have a setter on it. So let’s get rid of that, too.

Okay, I think that’s enough for now. We’re off to a good start. What does the fruit of our labor look like?

namespace Validation 

public class ConnectionValidator
inherits ParameterValidatorBase

friend new(byval connection IDbConnection, byVal name string)
mybase.New(connection, name)
end

public IsClosed() ConnectionValidator
if not Connection is nothing
if (Connection.State and ConnectionState.Closed) = 0
throw new ArgumentException("Operation requires a closed connection.", Name)
end
end
return me
end

private Connection() IDbConnection
get
return directcast(InnerValue, IDbConnection)
end
end

end

end

So there you go. Now you’ve addressed the problem of the verbosity. But the result doesn’t really look all that much like Visual Basic anymore. It’s almost a different language.

You could likely get around all this by making all the keywords we removed optional, but what’s the point? A large body of the folks who use Visual Basic have an ardent devotion to keeping the language just as it is; messing with it in any way runs the risk of stirring their ire and starting a war of religious proportions. Then you have to consider the problems that arise from one developer who uses the keywords, and one who doesn’t, and both of them working on the same code base.

And what happens when those old keywords become the goto of the 21st century?

Just leave it alone. If you want a terser version of Visual Basic, then make a new language. Personally, I’d love to be able to make the keywords lowercase, and not have to pamper the compiler any more than I have to. But I’d rather not waste any more time than I have to debating what’s sure to become a religious war when people realize what’s really involved in addressing the terseness of Visual Basic.

It’s not about the case of the characters. It’s about eliminating the keywords that don’t help the compiler or the developer. And the end result of that effort is a radically different language.

Wednesday, July 18, 2007

Why Does Software Cost So Much?

So my sister-in-law recently discovered that the computer a "friend" built for her has an illegal copy of Windows XP installed on it. (Big surprise, that.) When she asked me what she should do about that, I told her, quite simply, that she'd need to get a legal copy of Windows. When I told her what it cost, she was flabbergasted. She looked at me with disgust on her face and demanded, "I don't get it! Why is software so expensive?"

I looked her dead in the eye and said, "Do you really want me to explain it to you?"

Now, she knows that I write software for a living, and she still, in her naiveté, had the temerity to ask that question of me. She's well aware of how constantly stressed out and tired I am from the work I do. So she backed up a second, swallowed, and said, "No, I guess not."

In hindsight, however, I think she posed a fair question. I hear it alot. It's probably a fair statement that most folks have no real grasp of why software costs as much as it does. So I will try to point out some of the reasons why I believe that it is. These are my opinions, mind you; your mileage may vary.

Software is expensive for one very important reason: the people who make it have to live, pay their bills, and feed their families. Beyond all other reasons, that's probably why it's so darned expensive. It's a job, just like any other. And, like any other field, it's not just a simple task: it's a very complex task that requires lots of people, lots of resources, and lots of time.

(There seems to be this preconception out there that writing software is easy because using it is easy. It's just a game, right? How hard can it be?)

I can't speak for the open-source community who do everything over the Web, and develop software on a volunteer basis. But then again, this article doesn't necessarily apply to them. But certain things must be in place in order to develop software in a corporate environment:

  • You have to have somewhere to do it. If you're a freelancer, working out of your house, you have rent or a mortgage to pay. On the other hand, if you're a business, it means you need a lease.
  • For businesses with a building, you need insurance, so that you're covered in case your employees are injured on your premises. You need flood and fire insurance as well.
  • You have to pay utility bills (heating, cooling, electricity, water, etc.). 
  • You have to pay your local, state and federal taxes.
  • You have to have office equipment. Depending on how many people are involved, you may need LOTS of it: desks, chairs, bookshelves, computers, printers, fax machines, extra toner, phones, paper, notebooks, file folders, filing cabinets, trash cans, pens, and all that other jazz. You'll probably need whiteboards for collaboration. If you don't think that stuff is expensive, start browsing your local office supply store web sites. You'll be in for a big surprise.
  • You have to employees. Employees get paid. Depending on how complex your product is, it can take anywhere from a handful to hundreds of employees to develop your product.
  • You have to provide your employees with benefits (medical, dental, 401k, workmen's compensation, etc.), unless they're contractors. If they're contractors, they have to cover their own benefits which means they get paid more than W-2 employees.
  • You have to have lawyers to ensure that your intellectual property is protected from copyright infringement.
  • You have to spend money and invest time to develop a plan for what the product will do. This is often a long, arduous process that involves a lot of arguing, shouting, frayed nerves, and confusion.
  • You have to spend money and time creating mock-ups of what it will look like. This mockup goes through several revisions as people heatedly debate why they hate it, what they want changed, and what a crappy job you did in the first place. They'll want to know why you didn't understand that by "circle" meant an ellipse and that by "square" they meant a beveled rectangle with a shadow in the lower right-hand corner. They'll send you back to the drawing board. Repeatedly. What one user demands another will despise. No one will ever be completely satisfied.
  • You have to spend money to finally start developing the actual software. You'll do this with a severe shortage of time, money, and hands, and the developers will want to kill the management. They'll be unhappy because the specs will likely be hastily put together, the time will be insufficient, and they'll be grumbling about the likelihood of "crunch time scheduling" that requires overtime that they won't be getting paid for.
  • You have to spend LOTS of money testing the solution once it's put together. This is the hardest part of the whole thing. You will have made the mistake of thinking that it works. It doesn't. It never does. Just when you think you know what you're doing, a user is right there to prove you wrong. The system will have to be ripped apart, rewritten, retested, over and over and over again. And all of that takes time. An adversarial relationship will develop between the testers and the developers. Finger-pointing will rule the day. Some people will quit, costing you money as you have to replace them unexpectedly and retrain them. The schedule will slip, costing you further. And as long as you're doing that, you're paying for rent, utilities, taxes, payroll, benefits, office supplies, insurance, and everything else that goes with it.
  • You have to spend money to develop a marketing campaign for the product.
  • You have to spend money to package the product (put it on disk, print the materials that go inside the box, put it in the box, shrink wrap it).
  • You have to spend money to ship it to the retailers who will carry it, or negotiate deals with online resellers who will carry it for you.
  • You have to employ a customer support staff after the product ships. (Benefits, payroll, etc.)
  • You have to pay to support the customers when they call to complain about the product after it ships, or when they can't figure out how to install it or find the icon to start it up or find the CD ROM drive on their computer. You have to deal with irate customers who think it's your fault that the software has too many features when they're the ones that demanded them. You have to deal with customers who are angry and seem to think that it's okay to scream at you because they made a mistake and spilled soda on their keyboard, or the dog yanked the laptop onto the floor, or they didn't pay their cable bill and they want to know why your Web-based software doesn't work anymore. And you have to smile through it all while the bills keep piling up.

These are just a few of the reasons that software is so expensive.

And let's bear in mind one very important fact: writing software is not like writing an essay, or building a house, or fixing your car. Writing software is damned hard to do. Hell, I seriously suspect that disarming bombs is easier.

From where I sit, the computer is just a stupid box armed with the vocabulary of a three year old. It will do exactly what you tell it to do, very quickly, and very efficiently. If you tell it to do the right thing, it will do it very quickly, and very efficiently. Tell it to do the wrong thing (like, erase all files in the My Documents folder) and it will do it very quickly, and very efficiently. This is why I get irritated when someone says, "Oh, the computer hiccupped!" or "The computer screwed up." No, the computer did not. The computer did exactly what some programmer told it to do.

You have to teach a computer everything. Consider a simple task that we take for granted, such as "Transfer $100 from my savings account into my checking account." In its simplest state, a computer is completely incapable of doing that. It doesn't know what a transfer is, what an account is, what savings is, or what checking is. It doesn't know what a dollar is, or a bank is. It has to be taught everything. Software is about teaching that dumbass box with a 3-year-old's vocabulary how to do extremely complex tasks without making any mistakes.

In short, a computer programmer is an individual who sits down at a computer every day, and figures out how to handhold a mechanical idiot through tasks simple and complex, mastering the task of tutoring that idiot through those tasks until the idiot becomes a savant. It's grueling, frustrating, and infuriating. It takes time, and it's expensive. Depending on how big the task is, it might take lots of programmers to teach the idiot how to do the task.

So there you have it. That's why I think software is expensive.

Coincidentally, I also think it's why I and a lot of my peers in this field are psychotic (or very close to it).

Monday, July 16, 2007

Announcing NValidate

Yeah, so I bit the bullet. I decided to go ahead and port the parameter validation code to C# and make it open source. And I settled on a name. After an extensive search on the web to make sure the name wasn't taken, I chose the apt name of NValidate.

Yes, it's a play on words.

So I've registered the domain name (www.nvalidate.org) and will soon have the web site up for it. First thing I'll get out the door is the user documentation so that you can see what's coming.

The initial release will provide basic parameter validation tests for each of the following types in the Framework:

  • Boolean
  • Byte
  • Char
  • DateTime
  • Decimal
  • Double
  • IDbConnection
  • IDbTransaction
  • Int16 (Short)
  • Int32 (Integer)
  • Int64 (Long)
  • Object
  • Single
  • String

For those who haven't seen my earlier post, NValidate provides a way to streamline the code that tests method parameters for validity. It turns this:

protected int getPlus4(string zipCode)
{
const string ZipFormat = "\\d[5]-\\d[4]";
if (zipCode == null)
throw new ArgumentNullException("zipCode");
if (!((new Regex(ZipFormat)).IsMatch(zipCode)))
throw new ArgumentNullException("zipCode");

return int.Parse(zipCode.Substring(6, 4));
}


into this:

protected int getPlus4(string zipCode)
{
const string ZipFormat = "\\d[5]-\\d[4]";
Validate.That(zipCode,
"zipCode").IsNotNull().Matches(ZipFormat);
return int.Parse(zipCode.Substring(6, 4));
}


To ensure that the learning curve is small, the "interface" to NValidate is modeled after that of NUnit; that is, Validate.That is similar to NUnit's Assert.That. The difference is that Validate.That always throws an exception when the test fails, and the exception is always an ArgumentException (or one of its derivatives).


The tests supported by NValidate will be pretty exhaustive. I've got plans for a full suite of tests already planned. They're shown at the bottom of this post. If you can think of one that you do very frequently and it's not on the list, let me know, and I'll add support for it. This list of tests is based on the tests that I normally perform myself in software, plus a few that I gleaned from NUnit itself. I plan to expand the library down the road, adding more tests and more type support as demand for it increases. (That supposes, of course, that said demand actually exists.)


NValidate will support .NET 1.1 and 2.0; it will ship with compiled binaries and the full source code. Additionally, the license I select will permit its use for any reason, free of charge. If you find a bug in it, I would hope that you'll let me know so that I can fix it.



Proposed Tests for Initial Release of NValidate



  • Contains: String
  • DoesNotContain: String
  • DoesNotMatch: String
  • EndsWith: String
  • HasDay: DateTime
  • HasHour: DateTime
  • HasLength: String
  • HasMinute: DateTime
  • HasSecond: HasValidConnection
  • HasYear: DateTIme
  • IsBoolean: String
  • IsClosed: IDbConnection
  • IsEqualTo: All
  • IsFalse: Boolean
  • IsGreaterThan: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsGreaterThanOrEqualTo: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsInRange: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsLessThan: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsLessThanOrEqualTo: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsNegative: Byte, Char, Decimal, Double, Int16, Int32, INt64, Single
  • IsGreaterThan: Byte, Char, Decimal, Double, Int16, Int32, INt64, Single
  • IsNotEqualTo: All
  • IsNotInRange: Byte, Char, DateTime, Decimal, Double, Int16, Int32, INt64, Single, String
  • IsNotNull: IDbConnection, IDbTransaction, Obect, String
  • IsNotOneOf: All
  • IsNull: IDbConnection, IDbTransaction, Object, String
  • IsNumeric: String
  • IsOneof: All except Boolean
  • IsOpen: IDbConnection
  • IsPositive: Byte, Char, Decimal, Double, Int16, Int32, Int64, Single
  • IsTrue: Boolean
  • IsValid: All
  • IsZero: Byte, Char, Decimal, Double, Int16, Int32, Int64, Single
  • Matches: String
  • StartsWith: String

Saturday, July 14, 2007

Refactor Yourself

Take a moment to stock of where you are now. What skills do you have? How can you improve them? Every day of your career, you should be learning something, improving something, refining something. Your skill set should be undergoing constant refactoring. This can only make you more efficient. If the stuff you're learning isn't making you more efficient, discard it.

At some point, you have to have the guts to go against the grain. Just because a "best practice" works for someone else at some other company doesn't necessarily make it a "best practice" for you and your company. A "proven methodology" isn't necessarily going to be a "proven methodology" for you. Have the guts to challenge the status quo. If it's not making you more efficient, it's likely hindering you. Refactor it out.

If your team doesn't have the funds to learn some new technique, seek that knowledge personally. There is no reason that your company's inability to fund team education should hold you back. Buy books and read. Search the Internet. Read blogs and programming newsgroups. Experiment with code. Ask your peers. Never stop seeking knowledge. Never stop learning.

Take some of your old code, copy it, and then refactor the hell out of it. You'll be surprised what you can learn by simply refactoring code: more efficient ways to implement things that you did before (and will likely do again), better algorithms that work faster, use less resources, and are easier to maintain. Refactoring improves your skill set. Refactoring your own code, on your own time, is a personal competition against yourself to improve your own skill set.

You don't need to compete against anyone else. Coding cowboys, platform fanboys, methodology purists, conspiracy theorists...you shouldn't be worrying about them. You should worry about yourself. Make yourself as good as you can possibly be. Every day, ask yourself this essential question: "How can I improve myself today?" Find that way, and then do it. Set aside a little time every day to refactor your skill set.

Each day is an opportunity to make yourself a little bit better, a little more efficient than you were the day before. With each passing day, you have the opportunity to become smarter, faster, wiser, more valuable. But that means taking care to constantly revise your skill set. Have the wherewithal to discard habits and ideas that simply don't work. If you suspect you're doing something one way simply because you've always done it that way, or because that's the way everyone else does it, question it. If you can't see a tangible benefit to it, refactor it out.

Look, I'm not Gandhi or anything. But I can tell you this: I firmly believe that the key to success in this field is a personal commitment to growth. Don't trust anyone to just hand you knowledge, or to stumble across the skills you'll need. You have to actively reach out and take the skills and knowledge you need to be successful. It's an active task. It's not going to be something you just acquire through osmosis.

We all have to get to a point where we realize that we're not as efficient, not as smart, not as skilled, and nowhere near as good as we could be. There's always someone out there who's better than we are.

Our goal isn't to compete with them. Our goal is to constantly aspire to be better than we are right now, at this very moment.

Wednesday, July 11, 2007

A Parameter Validation Framework

I need some help here. I'm hoping you'll review the following "framework" and give me your feedback. I need to know (1) if it's a good idea, (2) if the architecture is sound, and (3) how it can be improved. If it's a generally useful idea, I'll open it up on an open source project forum where it can be expanded for general use. Otherwise, I'll go back to the drawing board. (I'm not sure at this point if it's a worthwhile idea.)

The Background

In reviewing my projects, I noticed that I tend to be lazy about validating parameters to methods. I reviewed a number of projects that I was working on and noticed that it tended to be true no matter what the project was, or what the parameter types were. When I sat back and thought about it, and paid attention to myself doing it, it came down to two primary issues that led to me avoiding it:

  • I was usually in a rush to get a product out the door. This tends to always be the case, since the product's schedule is out of my control, and there isn't much that I can do about it. I can only negotiate for a minor change in the project's time. Then, I'm left to work with the amount of time I'm given. So I try to do the best work I can in the time I'm allotted, and hope to high heaven that it's good work. That often means that I code like hell (classic software mistake), and hope that nothing breaks.
  • Parameter validation code tended to be verbose. When I really wanted to say, "Parameter y should be a positive number," the resulting code looked like this:
    If myParameter <= 0 Then
       Throw New ArgumentOutOfRangeException("myParameter", "Must be a positive number.")
    End If

These two issues alone might not seem like a big deal in and of themselves. But when it comes to encouraging code quality, I take them very seriously. I abhor sloppy code, especially when it's mine. I want to be sure that parameters are valid, and that when a method receives parameters, they are checked rigorously to ensure that I'm getting valid values throughout any system I'm developing.

The Goal

I set out to come up with a way to make it easier to validate parameters. I wanted a way to make it so easy to validate them that I would find it enjoyable to do so. I wanted it to make my code more legible, with a minimal amount of impact on its performance. Knowing that I basically lack discipline, I knew I needed a software solution that would exploit Visual Studio's capabilities to remind me to check for tests that I might not have normally thought of. (We have a tendency to inadequately document the requirements—again, due to time constraints—and I need to be able to think about logical tests that make sense for the parameter as I'm coding them.)

So the goals for the framework are:

  • Ease of use. This was the number one design goal. If the thing wasn't easy to use, it was pointless to build it in the first place, since it was being designed to encourage validation of parameters. If the framework made it harder to do that, it was defeating the purpose.
  • Understandability. The code should enhance the readability of the code.
  • Performance. The methods were going to be called with a high degree of frequency. It was critical that the methods have a very low overhead on the call stack. If at all possible, the number of objects being created should be kept to a minimum. However, because we're adding code, some overhead is unavoidable.
  • Reliability. It has to work. And it has to work reliably and predictably every time.
  • Extensibility. It has to support other data types.
  • Maintainability. The framework's code has to be easy to read, understand, and maintain. We do not want to have to bloat the development time of the projects that rely upon it with weeks of maintenance time just to fix and expand the parameter validation library.

The Solution

I set out to come up with a way to make it easier to validate parameters. I wanted a way to make it so easy to validate them that I would find it enjoyable to do so. I wanted it to make my code more legible, with a minimal amount of impact on its performance. I wrote a set of classes that, I believe, accomplished that. The resulting "framework" (if you can call it that) is based on the Assert.That model popularized by NUnit and JUnit. When you want to prove that a parameter is positive, you write the following:

Sub Foo(ByVal integerParameter As Integer)
   Validate.That(integerParameter, "integerParameter").IsPositive()
   ' Do something interesting with integerParameter
End Sub

It's short, sweet, and to the point. If integerParameter contains any value that is less than 1, an ArgumentOutOfRangeException is thrown, with an appropriately formatted message bearing the parameter's name.

The solution involves the following classes:

Class Description
Validate A class factory that instantiates strongly-typed parameter validator objects. These are derived from ParameterValidatorBase. Provides the heavily overloaded That method.
BooleanValidator Inherits from ParameterValidatorBase. Provides methods for validating Boolean parameters, such as IsTrue and IsFalse.
ConnectionValidator Inherits from ParameterValidatorBase. Provides methods for validating IDbConnection parmaeters, such as IsNotNull, IsOpen, and IsClosed.
DateValidator Inherits from ParameterValidatorBase. Provides methods for validating date parameters, such as Equals, IsBetween, IsGreaterThan, IsGreaterThanOrEqualTo, IsLessThan and IsLessThanOrEqualTo
EnumValidator Inherits from ParameterValidatorBase. Provides methods for validating enum parameters. This is the only parameter validator that relies on reflection. It's sole method is IsValid.
IntegerArrayValidator Inherits from ParameterValidatorBase. Provides methods for validating integer array parameters, such as IsNotNull and IsNotEmpty.
IntegerValidator Inherits from ParameterValidatorBase. Provides methods for validating integer parameters, such as Equals, IsGreaterThan, IsGreaterThanOrEqualTo, IsInRange, IsLessThan, IsLessThanOrEqualTo, IsNegative, IsNotNegative, IsOneOf, IsPositive, and NotEqualTo.
ListValidator Inherits from ParameterValidatorBase. Provides methods for validating IList parameters, including IsEmpty, IsNotEmpty, IsNotNull, and IsNotNullOrEmpty.
ObjectValidator Inherits from ParameterValidatorBase. Provides methods for validating any Object parameter, including IsNotNull and Equals.
ParameterValidatorBase The base class for all validators. Provides the name of the parameter and a protected property to store the parameter's value.
StringArrayValidator Inherits from ParameterValidatorBase. Provides methods for validating string arrays, including IsNotNull, IsNotEmpty, and IsEmpty.
StringValidator Inherits from ParameterValidatorBase. Provides methods for validating strings, including Contains (overloaded), Endswith (overloaded), IsNotEmpty, IsNotNull, IsNotNullOrEmpty, and StartsWith (overloaded).
TransactionValidator Inherits from ParameterValidatorBase. Provides methods for validating IDbTransaction parameters, including HasValidConnection, HasOpenConnection and IsNotNull.

Other parameter validator types are being added as the need for them arises.

When a call is made to Validate.That(), the framework instantiates an appropriately typed parameter validator object. The caller then invokes one or more of the methods on that object to prove that the parameter is valid. The parameter validator merely tests the condition specified by the method name; if the test evaluates to False, an appropriately typed exception (derived from ApplicationException) is thrown. The parameter validator is responsible for properly formatting the message and passing the name of the parameter; this is spiffy because some of the exception objects are inconsistent about the order in which the name and message parameters are passed to them. The framework standardizes the order through the use of overloads: parameter, name, message.

For example, the source code for the ConnectionValidator class looks like this:

Option Explicit On 
Option Strict On

Imports
System.Data

Namespace Validation

Public Class ConnectionValidator
Inherits ParameterValidatorBase

Friend Sub New(ByVal connection As IDbConnection, ByVal name As String)
MyBase.New(connection, name)
End Sub

Public Sub IsClosed()
If Not Connection Is Nothing Then
If (Connection.State And ConnectionState.Closed) = 0 Then
Throw New ArgumentException("Operation requires a closed connection.", Name)
End If
End If
End Sub

Public Sub IsNotNull()
If Connection Is Nothing Then
Throw New ArgumentNullException(Name)
End If
End Sub

Public Sub IsOpen()
If Not Connection Is Nothing Then
If (Connection.State And ConnectionState.Open) = 0 Then
Throw New ArgumentException("Operation requires an open connection.", Name)
End If
End If
End Sub

Public Sub IsValid()
IsNotNull()
IsOpen()
End Sub

Private ReadOnly Property Connection() As IDbConnection
Get
Return DirectCast(InnerValue, IDbConnection)
End Get
End Property
End Class

End
Namespace

The Pros & Cons


I've noticed that I am, indeed, far more likely to validate parameters now with this framework. I'm catching a lot more defects with it as well. The idea behind it seems to be working. However, it does have a few problems:



  • It's failing to meet its extensibility requirement. In order to add new types, I have to hand-code a new overload to the That method into the Validate class. I need to find a new way to do that.
  • It's not properly localized. (You can see the hard-coded English strings in the code sample above.)
  • It adds overhead to the stack trace, which can be confusing to users who don't know what it is.
  • It lacks a way to conveniently perform multiple tests on the same parameter validator object (aside from Visual Basic's With...End With block, which just looks unnatural). The current implementation tends to ask you to create multiple objects to work with the same parameter; although the objects maintain very little state (two variables, both object references) it's still more than I'm comfortable with.

Despite its drawbacks, most of which are addressable, its benefits appear to be worthwhile. My code quality is rapidly improving. Defect rates are dropping noticeably (and I feel comfortable attributing a good portion of it to better parameter validation).


The Request for Comments


So there you have it. At this point, I would really like to hear back from the community, and find out what you think of this thing. Should I be doing this? Should I be doing it better? How would you improve on it? What would your concerns be if you were using it or designing it yourself?


Remember, I'm the quintessential vacuum coder here, so your input is valuable to me.

Tuesday, July 10, 2007

Batman: Software Ninja

Scott Hanselman painted this lovely picture today:

...It's rarely a good idea for management to go get "The Smart Guy" and have him come crashing down through a stained glass on a zipline ready to save the day.

[snipped!]

A crappy project can't be fixed by a line by line code inspection, no matter how good a ninja one is. Sez me.

I disagree. Batman's a ninja, and I'd bet real money he has *something* on his utility belt or in the Batcave that can fix any project. Even mine.

That is the ninja to whom you were referring, right, Scott? The one who comes crashing down through stained glass on a zip line to save the day?

Man, I would totally love to see that. Batman, crashing into a meeting for a doomed project, knocking some heads around and pointing his gloved finger in someone's terrorized face. Speaking in that low growl, slowly, authoritatively, so that there's no doubt that you know what he wants you to do.

Who could resist team leadership like that? Who would dare to waste our time with pointless agendas and pissing contests? People would get in, say what they needed to and get out. No one would dare to tick him off. And if they did, the consequences would be dire. He'd have an aggressive plan for dealing with risks. If he didn't, he'd get one fast.

Then again, you know there'd just have to be a Joker, a Two-Face, and a Riddler out there somewhere.

/emote sigh

I so need a new life.

Monday, July 9, 2007

Usability Observation of the Day

Imagine a person who's trying to quit smoking. That person is already in a foul mood. On the one hand, he wants to quit. On the other, he doesn't.

He's going through the pangs of withdrawals. It's pretty serious stuff. He's shaking, sweating, eating everything in sight. He's driving his car like he's Mario Andretti, and he's not even out of the driveway. Anything could set him off at any moment. A simple "Hello" is returned with "WHAT THE F#$>@ DO YOU WANT?!" He's ready to do violence.

So what do you, as a nicotine patch vendor, do?

You seal the nicotine patch in a vacuum-sealed packet that requires the addict to use a pair of scissors or a knife to get the thing open.

Usability Advice: DO NOT advise an addict to arm himself with a sharp object in order gain access to the one thing that will quell his cravings.

<Queue up the music from Psycho right here.>

Rather, perforate the packets, and make them easy to tear open. When that guy needs that patch, he needs it NOW and he doesn't want to spend forty minutes on a mad search for something to open it with. By the time he finds something suitable, he'll likely want to stick it in someone's eye first, and then just sink giggling into a corner while he sucks the nicotine out of the patch.

Now, if you'll excuse me for a moment, I have to find a pair of scissors.

Friday, July 6, 2007

Feature Creep Accelerates Entropy

Many folks cruising around Digg recently have seen the numerous articles pointing to the claims by Steorn that their Orbo device can produce "free" energy, in direct violation of the Second Law of Thermodynamics. Basically, the device claims to produce more energy than it uses.

Scientists everywhere are choking on their degrees, and with good reason. Such a device would be either (a) a ridiculous grab for attention based completely on fantasy, or (b) a radical shift in our understanding of thermodynamics.

But this post isn't about the Orbo device. It's about a key principle of the Second Law of Thermodynamics: Entropy. Specifically, we're interested in how entropy affects software. To see where I'm coming from, however, let's review:

A measure of the amount of energy in a physical system not available to do work. As a physical system becomes more disordered, and its energy becomes more evenly distributed, that energy becomes less able to do work. For example, a car rolling along a road has kinetic energy that could do work (by carrying or colliding with something, for example); as friction slows it down and its energy is distributed to its surroundings as heat, it loses this ability. The amount of entropy is often thought of as the amount of disorder in a system.

entropy. Dictionary.com. The American Heritage® Science Dictionary. Houghton Mifflin Company. http://dictionary.reference.com/browse/entropy (accessed: July 06, 2007).

 

The car, rolling down the road, only has so much energy to work with. Some of that energy is kinetic (forward motion), and some is being dissipated as friction on the tires and by the air blowing past the car. But there's a finite amount of energy involved. If the driver taps the brakes, more energy is transferred to the brakes, but it's the same amount of energy. The car simply slows down, which has the net effect of reducing the severity of an impact with a tree.

So what the heck does this have to do with software?

All software suffers from entropy. You can only cram so many features into it before it has become so large that it has more unusable features than it has usable features. The code itself, at that point, becomes a chaotic mish-mash of bits and pieces of work cobbled together by people who came and went over time, riddled with cracks where their styles, philosophies, and standards didn't quite mesh well. The quality of the source code itself begins to degrade, becoming less and less maintainable, sprinkled with routines, variables, classes, and modules that are never used. The older the product gets, the more pronounced the entropy effect will be, until someone either cans the project, or demands a full rewrite.

Users tend to have intense, often emotional bonds with their favorite products. Those feelings tend to border on the fanatical when those products are easy and simple to use. They get really upset when you unnecessarily complicate them.

Not surprisingly, the products that seem the most resistant to entropy are the ones with the fewest features. They just work. Light switches are a wonderful example of a device that resists the effects of entropy. You just flip the switch, and the light comes on. But then, down the road, someone fancified them with dimmers. Now the knob pops off. Sometimes, the dimmer breaks. You need special bulbs. People cry out for plain switches. So the dimmer rarely gets used. Entropy claimed it.

Toasters, similarly, are a simple device that resist the effects of entropy. Insert bread; push a lever; electrical current is applied to the heating element; the rack pops up, and you have toast. It's simple. But when we complicated it with toaster ovens, microwaves, and bagel toasters, things got tricky. More features, like programmable times, and self-cleaning, and defrosting, and popcorn timers became complex and we couldn't figure out how to use them. So they became unusable. Entropy claimed them.

In software, similar things happen all the time. We toil for hours to get our features just right. We labor over their design, making sure that this feature we've envisioned is perfect, because we know that users will love it, they need it, they absolutely have to have it. But the truth is that users love simplicity. And the more features you add to a system, the sooner entropy will claim it.

Microsoft Word, for instance, has more features than anyone could possibly use in their lifetime. Yet an entire army of people labored for weeks and months to get them just right. Microsoft Office 2007 is a shining example of unusable energy outweighing the usable energy. It's the poster child for the entropy effect among software products.

When you're designing products, as I do for a living, think carefully about feature creep. The fewer features your product has, the better the chances are that those features will be used. Increasing the number of features increases the chances that most of them will be unused. It's entropy at its best.

Thursday, July 5, 2007

Vacuum Coding Syndrome: The Need for Peer Review

I stumbled across this interesting quote today as I was looking for information about the effectiveness of peer code reviews:

There are some thing's you can't unit test. You can't unit test design or architecture. And sometimes you can get unit tests wrong—you might assert the wrong things. There is always a place for human eye-balling of code.

Matt Quail, at JavaOne 2007

This statement resonates profoundly with me because of the situation that I'm in. As I've mentioned previously, I code alone. I have no peers with whom I work and can share designs and submit them for review. So when I make a bad design decision and then implement it, I'm stuck with it. Lately, I've been going over my code, and when I come across some of my older stuff, I'm frequently left wondering just what in the hell I was thinking.

There are designs and architecture decisions that I made three years ago that seemed perfectly reasonable at the time, and now I look at them with abject horror.

In one case, I knew that our database architecture was going to be massive, and that it was going to be changing frequently. I also knew that the data model classes were going to have to be constantly updated to stay in synch with the database, and that there was no way I was going to be able to do all that manual work without missing something. I needed to be able to update the database schema, push a button, have the classes regenerated, and then move along to focus on the business logic.

So I wrote a program that did that. It was, to be sure, a very fast, very efficient program. But it didn't generate very pretty code. In fact, it generated a lot of classes that we never use. For every table, it generated an insert, update, delete, exists, delete all, and select all stored procedure. For each stored procedure it had generated, it created a wrapper class that correctly created the SqlCommand, populated its parameters, and invoked the procedure. It also generated a data model class and a collection class. It also generated a strongly typed primary key class, and the DAC class that invoked the stored procedure classes.

Now that's a lot of code. At the time, it seemed like an act of sheer brilliance, and I marveled at the fact that if the schema changed I could simply regenerate the classes and know that they matched the schema. But the number of stored procedures quickly grew out of proportion to what we actually needed for the system we were building, and the vast majority of them were never used. And if those procedures were never being used, you can bet that the generated classes were never being used, and neither were the methods on the DAC classes.

That program, thankfully, was decommissioned early on, but it left a massive amount of code in its wake. Newer code no longer follows the model that it established, but is, in fact, leaner, smaller, and uses far fewer classes and stored procedures. It's a joy to work with the new code. But I groan and cringe every time I have to wade through that older code. 

It was a case of very efficiently created code bloat. It would very likely have been stopped early on had someone been there to either (a) review the design of the code generator, (b) point me to an existing tool that was better at it, or (c) convince me that we actually had time to just do it the right way in the first place.

In any case, I'd never do it that way again. I have been duly chastised by my own foolishness.

I'm absolutely certain that I'm not the only one suffering from Vacuum Coding Syndrome. I'm equally certain that I need to have someone reviewing my designs and my code. So as I sat chain-smoking today, looking at a piece of paper with an abominably badly written piece of code printed on it that I knew I had to fix, I realized that I really needed to start submitting my designs for peer review to someone. Anyone, at this point, really. Because I really need to stop making silly mistakes. I'm the one that has to live with them.

So I got to thinking about it. In the past, I've submitted small snippets of code onto Usenet for review, and the response has been mixed. I've submitted some of my good code onto The Code Project and the response has actually been pretty good (but that code works). Now my brain is churning over a different kind of forum.

What I need is a forum where developers can submit code and/or designs for peer review. That would be the whole point of the forum. The online community is overflowing with professionals who are far more experienced, far more intelligent, and far more capable than I am. It would be a great boon to lone developers if there was a site designed specifically to allow us to post our code samples (sans IP) and designs (in some ubiquitous format) for peer review. 

I've seen products like CodeCollaborator, which foster collaborative code reviews and allow users to work together on code reviews using software. You can chat, compare notes, diff the files, and so on. Why hasn't anyone thought to do this on the Web? I'm not envisioning everything that CodeCollaborator does on the Web; what I am envisioning is a simple forum, like The Code Project or Construx's Software Best Practices.

So here I am, scouring the web, looking for such a resource. If any of you happen to know of one, I'd be much obliged if you could point it out to me. I'd roll my own, but given my history of making poor design decisions, and my lack of access to resources capable of reviewing my designs...

Well, you know. <shrug/>

Tuesday, July 3, 2007

Email: The Productivity Killer

Was I asleep? Did I miss something? I was under the impression that email was something you sent when you wanted to communicate something that was good to know, or needed an answer, but didn't need your direct attention or immediate response this second. If it was something really important, you flagged it as urgent.

But suddenly, every email we send has become a ticking time bomb that has to be answered right now, this very second.

God help you if you don't.

We've got people who send emails, and wait anxiously, huddled over their keyboards, waiting for their response. If they don't get one within a few minutes, they pick up the phone or open an IM window and ask, "Did you get my email? Did you read it?" If you dare answer in the affirmative, they ask you why you didn't respond. If it was that urgent, why didn't you just call? 

We've become a society that hinges on instant communication, leaving instant message services open, cell phones on 24/7, and email applications open and minimized so we know the very second we get an email.

But here's the skinny of it: the vast majority of the email we receive is unimportant garbage. And yet, Microsoft Outlook feels utterly compelled to give me a nice little notification every time I get an email from someone who wants to tell me there's leftover cake in the coffee area, that I can get a lower rate on my mortgage, or that some obscure fellow from offshore wants me to engage in a money-laundering scheme with him.

That kind of distraction is a major productivity killer. It's every bit as bad as an instant message window popping up from your mom, telling you that the cat puked on the kitchen floor.

Why? Because it breaks your concentration, and usually at really critical moments when you've got your groove on.

Face it: You don't really need to know those things. Microsoft Outlook shouldn't alert you to every email that you should receive. It should alert you to those marked urgent or flagged with a read-receipt, and then, only those from people on your approved senders list (usually, your contact list or the Exchange server). But no, that might actually make sense.

I came into work today and decided to conduct an experiment. I'm normally distracted by a ton of little things that I am certain are the primary killers of my productivity. They usually boil down to three things: email, instant messenger, and the compulsive urge to browse the Web. So I set out to eliminate those things. I shut down my instant messaging applications. I checked my mail once, and then exited the application. I didn't minimize it, I closed it.

Then, instead of leaving the defect tracking software open in a browser all day, where I'm tempted to keep checking my feeds and peruse Digg, I exported the list of defects to Excel, and then closed the browser. I then kept the browser closed all morning.

I've gotten more work done this morning than I have in the past two days. Why? Because I can concentrate. My train of thought isn't constantly being derailed by instant messages, email notifications, and that highly tempting little bookmark that mocks me on my Firefox Live Bookmarks toolbar.

These kinds of things are serious productivity killers. They simply pull me away from what I should be doing. So you can bet that I'll be seriously mitigating their impact on my work life in the future. It's a sad fact that in our world, we can't escape them because they've become so entrenched in the way that we work and live.

But, I'll set aside specific times to check my mail each day (first thing in the morning, lunch time, and right before I go home). Instant Messaging software is out. And I'll significantly reduce my use of the browser, constraining myself to those uses for which it was intended.

Maybe, if I do those things, and am able to get my work done at the office, I won't have to take my work home. You know, where there's a completely different set of distractions.

Sunday, July 1, 2007

On Self-Control and Software Development

This essay was written months ago, and never posted. I resurrected it today, in light of certain recent entries.

Recently, as I was working to deliver a major release on a product I'm working on, I found myself sidetracked by a little project of my own.

You see, there's this little problem with one of the data fields in the database. It's not major, just an annoyance, like a four year old poking you in the ribs for an hour, asking repeatedly, "Does this bug you?"

Well, it's been bugging me for ages. And I found myself today doing database queries and pasting data into Excel to have Excel build update queries for me using formulas (nice little time saver that is) so that I could include those statements in the SQL script to accompany the next major release.

And then it hit me: no one asked for this. It's not included in the test plan for this release. It's gold plating. I'm doing this because I want to, not because the customer asked me to.

Whoa, there, cowboy. Get a grip on yourself. Set that stuff aside, and focus on what you need to do, and not what you want to do. There are far more important deliverables to worry about, and you don't have time to waste on unauthorized features or fixes. Especially when those fixes are for issues that don't negatively impact the application. (It was a display issue--first name before last name.) It's just fluff.

In reflection, I find myself experiencing these kinds of monumental self-control issues all the time. I get really excited about the things I could do for the customer, and I really want to do them for them. But the truth is that just because I can do something for them, it doesn't mean that I should do it.

Any change that I make to the product has the potential to introduce new defects into the system. That's why every change that I make to it must be tested.  It's why there's so much testing involved in software. (And if there isn't, something's seriously wrong.) And the testing doesn't just occur here, at my desk. It happens at the client. The product undergoes rigorous user acceptance testing. And testing isn't cheap--it consumes precious man hours, which equates to someone's hourly wages. And if I haven't gotten it right, it has to be fixed and retested. It can amount to massive amounts of money in man hours of testing.

Lets not forget the impact that the change has on updating the test plan, the release notes, requirements documentation, and user guides. Plus any associated costs with reprinting and redistributing them.

And what happens if the customer decides that my unauthorized change needs to be taken out? What if its impact on the system is so drastically negative that it must be removed? Can it be easily rolled back? And if it must be removed, what are the costs associated with doing so, and republishing all the updated documentation and builds?

Are you getting my point yet? The cost of a simple change isn't just what it takes me to code and test it at my desk. That's just the tip of a massive iceberg.

It takes a lot of self-control to prevent myself from adding features to a system when those features aren't (1) requested by the customer, (2) included in the project plan, and (3) absolutely critical to the current release.

The problem, I think, is that a lot of developers out there, myself included, don't get sufficient mentoring in the discipline of self-control when it comes to software development.

For example, we're all hailing the virtues of refactoring code to improve its maintainability, and I agree that that's a good and useful thing. But how many developers know that just because you can refactor a piece of code doesn't mean that you should? How many developers are out there bogging down project schedules because they're busy refactoring code when they should be developing software that meets the requirements for the project deadline?

(And here, I will sheepishly raise my hand.)

It occurs to me that before I ever modify a piece of code, before I ever touch that keyboard and write any new class or method, or create any new window or Web page, I should be asking myself, "Is this in the project plan? Is it critical to the current release?" If it doesn't satisfy one of those questions, I shouldn't be doing it.

The key to getting that product out the door on time is staying focused, and not getting sidetracked by fluff. Take it from someone with experience: it's easy to get sidetracked by fluff. Adding cool features is easy to do, because you're excited about it, and motivated to do it. Working on the required deliverables is hard work; it requires discipline and self-control. You have to stay focused and keep your eyes on the target. (You thought I was going to say "ball," didn't you?)

But we, as human beings, don't want to do what we need to do, we want to do what interests us, and what excites us. It takes an act of sheer will to resist that urge, to restrain ourselves, and get the real work done. I would imagine that one of the things that separates a mature developer from a novice developer is quite likely his or her ability to resist that urge to introduce fluff into software.

In the end, I think it might be a good idea if programming courses included curricula on self-control as a discipline for developers. And I mean that quite seriously. We need to have it drilled into our heads that we shouldn't be adding anything to the product that only serves our own sense of what's cool or useful. That's not to say that sometimes developers can't predict useful features before the users do; but they cannot and should not be introduced haphazardly into a product: they should be included as a planned feature as part of a scheduled release, so that they can be adequately tested and documented, and not just suddenly sprung upon someone as an easter egg.

There's a time and a place for everything. Gung-ho initiative has its proper place; software isn't one of them.