Tuesday, April 24, 2007

To Flash, or Not to Flash

Considering the design of an all-Flash website? Struggling with the difficult choices involved? Risks got you stumped? Consult this handy chart to help you out.

It'll vastly simplify your decision making process!

Tuesday, April 10, 2007

Yet Another Amateur Opinion

So, here I am, reading The Braidy Tester, where I frequently lurk, because, well, I like the way the guy thinks. And recently, he posted an article that I agreed with so I finally mustered up the courage to kick down the closet door and post a response.

The basic premise of the article (which you can find here) was that we should occasionally entertain the notion of organizing project teams around features instead of skill sets. I agreed. In my response, I said:

Going against the grain is scary but sometimes vital in our industry. It takes courage, and always involves risk. But calculated risk isn't always a bad thing.

I'm not a fan of blindly adhering to "established best practices," "the Next Big Methodology (TM)," or established team models. Just because something is a best practice at Acme Corporation doesn't mean it's a best practice at Real World Inc. The business model is different, the staffing patterns are different, the problem domains are different, *everything* is different. You have to be willing, at some point, to accept he idea that a best practice for *them* isn't necessarily a best practice for *you.*

Be flexible. Do something different. Experiment. Find out what actually works, and makes you more effective. Make the leap. At the end of it all, if you find out that it didn't work, you'll at least come out of the experiment knowing something that you didn't know before. And the acquisition of knowledge is never a wasted effort.

(Note that that's not a vanity quote. It's done for clarity.)

The operative words in that post are "blindly" and "calculated." However, a subsequent poster referred to my opinion as "amateur."

Pecker-waving aside, how does one define an "amateur opinion"? I doubt very highly that the poster has any idea what my level of experience is. I also doubt that he carefully read my post, and skipped right over the key words, blindly and calculated.

For whatever it's worth, I'm always willing to accept the fact that there are an innumerable number of people out there that know far more than I do. They have scads more experience than I do. But the last time I checked, you didn't have to have a license or a degree to have an opinion.

So, despite the fact that I apparently lack the credentials to post an opinion, I'll simply refer the reader to the 1st Amendment. And then I'll ask the reader to read what I said, and then think about what I said, and then ask about my experience before labeling me an amateur.

I assure you, sir, I am not. I am familiar with the phenomena that have led me to this opinion.

I have worked for several companies where a methodology or practice was adopted simply because it was popular at the time; no thought was given to whether or not it was suitable for the environment. This was a failure on the part of management: they should have conducted a proper risk assessment beforehand to determine its feasibility and and suitability. An estimate of the ROI would have been handy. But instead, utter chaos ensued, schedules slipped, projects failed, and employees left because there was a sense that no one knew what the hell was going on and no one was in control.

Exceptions to the rule? Undoubtedly. But realistic examples nonetheless. It happens. But it shouldn't.

Many of the mainstream, established methodologies and best practices are popular and widely implemented because they do, in fact, work. But they don't always work everywhere. I refer you to the old adage, "There is no silver bullet." You have to find the one that works for you; you don't just pick one out of thin air (at least, I hope not): you do your homework, and find one that is suitable for your business model, and maximizes the return on your investment. Maximum return for the least amount of risk.

Case in point: at one firm where I've worked, post mortems were ruled out as a bad political move. The development team was so small that the only ones who would provide meaningful input was the customer. Management didn't want the customer critiquing the software development process. Yet a post mortem is a valuable tool, and an established best practice for improving your overall process when it's done right.

What about code reviews? If you've only got one developer in your company, they're not feasible because that developer can't be truly trusted to review his own code objectively. Who's going to do it? Are you going to outsource it?

Truly small companies can't always adopt methodologies and practices designed to support larger development teams. It doesn't make any sense. Companies with extremely short development cycles can't adopt the methodologies appropriate to those who have the time to implement BUFD, and so forth. You have to be selective. And sometimes, you have to develop a custom methodology that works specifically for your firm, that may entail bits and pieces borrowed from other established methodologies.

Trying to shoe-horn your company into a methodology that doesn't fit you will only give you calluses in the most uncomfortable places. I am certainly not advocating that every company in the world should do its own thing. I never once advocated that. What I did do was denounce blind adherence to TNBT and best practices that weren't well-suited to your business model. When the need arises to deviate from what everyone else is doing because what they're doing doesn't work for you, it makes sense to entertain the notion of doing something different. Even then, you should only engage a different way of doing things if the risk involved in doing so is manageable and acceptable.

But hey, if that kind of an opinion makes me an amateur, so be it. I can live with that.

—Mike

Thursday, April 5, 2007

Refactoring Garbage Disposal

One of the most common tasks in my data layer is cleaning up my connections, transactions, and data readers. I do it a lot. The established code block for cleaning up a disposable object looks something like this: 

   Public Sub ExecuteUpdate()

Dim connection As SqlConnection
Dim transaction As SqlTransaction
Dim command As SqlCommand

Try
connection = New SqlConnection
transaction = connection.BeginTransaction

command = connection.CreateCommand()
command.CommandType = CommandType.StoredProcedure
command.CommandText = "UpdateSomeData"
command.ExecuteNonQuery()

transaction.Commit()

Catch ex As SqlException
transaction.Rollback()

Finally

If Not command Is Nothing Then
command.Dispose()
End If

If Not transaction Is Nothing Then
transaction.Dispose()
End If

If Not connection Is Nothing Then
connection.Dispose()
End If

End Try

End Sub

If you work with a lot of disposable objects (and I'm guessing that most developers do), you get to do a lot of this kind of stuff. The checks for Nothing (null in C#) are mandatory--if you think that a command or transaction object won't ever be null/nothing, boy, are you in for a surprise. Just wait until you try to invoke Dispose on one of those objects and it's not there.


It didn't take long for me to realize that the Finally block in my data access layer was ripe for refactoring. (The drawback was that much of the initial code was generated by a tool. Sucky part, that. But newer code uses the refactored stuff I'm about to show you.)


I hate repeating myself. I do. I really, really do. So I looked at that code and decided that I needed a class that would help me to safely dispose of objects. I needed a garbage disposal--kind of like your in-sink erator, where you can simply toss vegetables, egg shells, ice cubes, or whatever suits you, and it safely whisks them down the drain.


There were numerous questions. Should it be a separate class, or a base class? A base class implementation is problematic. It means that anyone that wants access to the methods needs to derive from it; I could see lots of classes that would want to use it but I didn't want to insert the class into the inheritance chain.


(Global modules were ruled out right off the bat. Don't even suggest it. Global functions, like global variables, leave a really bad taste in my mouth. Let's not pick hairs about what the compiler does behind my back. Just let it go, okay? Leave me to my AROCCF* ways.)


I settled on a separate class with Shared (C# static) methods. That way, you could access them on demand, as needed from anywhere. After all, the methods needed to maintain no class state. Everything they need is passed in.


The resulting class provides four overloads of a single method: DisposeOf. Two of the overloads are type-safe versions designed to provide specific handling for the SqlDataReader and SqlConnection objects, ensuring that they're properly closed before being disposed of. One overload takes a paramarray of IDisposable objects, iterates over it, and invokes the last DisposeOf overload.


All of the DisposeOf overloads invoke the one basic implementation, which takes an IDisposable parameter. That method simply ensures that its argument isn't null, thereby avoiding a NullReferenceException. If the argument isn't null, it invokes IDisposable.Dispose on it.


The net effect is that the Finally block is reduced to this:

      Finally
Disposer.DisposeOf(command)
Disposer.DisposeOf(transaction)
Disposer.DisposeOf(connection)
End Try

 Or, in the best-case, scenario, to this:

      Finally
Disposer.DisposeOf(command, transaction, connection)
End Try

There is one caveat to using this class to dispose of data access objects: you should always dispose of your transaction and connection last, and always in that order. Then again, I don't think that's due to this class. I think that's just the proper order of disposal. The overloaded version that takes the paramarray disposes of the objects in the order that you pass them.


If you have other IDisposable objects that you frequently use that require special handling, you can easily extend this class to help you out. This class was built for VB .NET 1.1, since we don't have the using keyword. The class makes it easier to clean up objects that should be cleaned up.


The code for this class follows below. You are free to take this code and use it or modify it however you wish. You aren't required to mention me, credit me, or even acknowledge that I exist. However, if it blackens your eye, bloodies your nose, or blows your foot off, remember that I don't exist. :)


—Mike

Option Strict On

Imports
System.Data.SqlClient

' Provides methods to assist in the safe disposal of objects.
Public NotInheritable Class Disposer

Private Sub New()
' Prevents instantiation
End Sub

' Safely disposes of an object. Avoids a
' NullReferenceException.
Public Shared Sub DisposeOf(ByVal item As IDisposable)
If Not item Is Nothing Then
item.Dispose()
End If
End Sub

' Safely disposes of a SqlDataReader. Avoids a
' NullReferenceException. If the reader is open,
' it is first closed.
Public Shared Sub DisposeOf(ByVal item As SqlDataReader)
If Not item Is Nothing Then
If Not item.IsClosed Then
item.Close()
End If
End If
End Sub

' Safely disposes of a SqlConnection object. Avoids a
' NullReferenceException. If the connection is opened,
' it is first closed.
Public Shared Sub DisposeOf(ByVal connection As SqlConnection)
If Not connection Is Nothing Then
If Not connection.State = ConnectionState.Closed Then
connection.Close()
End If
connection.Dispose()
End If
End Sub

' Safely disposes of an array of disposable objects.
Public Shared Sub DisposeOf(ByVal ParamArray items() As IDisposable)
For Each item As IDisposable In items
If TypeOf item Is SqlConnection Then
DisposeOf(DirectCast(item, SqlConnection))
ElseIf TypeOf item Is SqlDataReader Then
DisposeOf(DirectCast(item, SqlDataReader))
Else
DisposeOf(item)
End If
Next
End Sub

End
Class

* AROCCF = Anal Retentive Obsessive Compulsive Control Freak

Monday, April 2, 2007

Refactoring Your Way to Enlightenment

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.