Thursday, March 29, 2007

What Have You Learned Over the Last Year?

Software development is a dynamic field. It's also a vast field. We deal with a plethora of technologies that baffle most folks; when we start talking shop around our nontechnical friends, they tend to look at us with blank faces, blinking eyes, and that curious bit of spittle leaking out the side of their mouths.

And yet, we persevere. We work grueling hours, under often impossible schedules, with vaguely defined specifications ("What? Specifications you say? Hah!"), and management that is frequently more concerned with ship dates than code or product quality. None of these things is surprising to anyone who's worked more than six months in software development.

If you've had the pleasure to work in a company that actually cares about product quality and developer sanity, count yourself one of the lucky few. And do everything in your power to hold onto that job.

But this post isn't about our job quality. It's about what we developers do while we're solving incredibly complex problems with less than optimal tools, less time than we need and fewer hands and minds than we'd like.

We think, we create, we innovate.

Give some serious thought to some of the really tough problems you've faced over the last year. Not the ones where you had the right tools to solve the problem, or where you had the knowledge you needed to do it. Think about the ones where you were completely out of your depth, where you had no idea what you were doing, or how the heck you were going to to get out alive. You didn't have the knowledge. You lacked the tools. The clock was ticking. And you had a product to deliver.

And somehow, you survived and saved the day.

Experiences like this aren't new. They happen every day, in companies all around the world. Seemingly impossible demands are placed on developers, designers, DBAs, architects, testers, release engineers, technical writers, project managers, and everyone else involved in getting the product out the door. It's a monumental effort. In a lot of cases, development shops are severely understaffed, and the folks who work in them have to wear several hats--in the worst case scenario, one poor bastard gets the lucky job of wearing all the hats.  

And somehow, through all that mess, a product gets delivered. If it didn't, the company wouldn't be afloat. Sure, sometimes it doesn't happen. There are slippages, defects, embarrassments. But in the end, the product ships. Problems are solved. Work goes on. And the folks doing the work are learning. Evolving. Improving. Honing their craft.

If they're not, there's something seriously wrong.

At the end of every project, every major milestone, regardless of how well or poorly executed it was, how close to the predicted ship date it was, how riddled with defects it may have been, there's a chance to look back and reflect on what you learned from the experience. We learn from our mistakes and our successes. We learn what worked, what didn't, what can be improved, and what should be left alone.

This last year has been a whirlwind for me. I've learned a lot. For instance, a product that I thought was stellar when I first built it turned out to have lots of room for improvement. Sure, it was pretty, but it wasn't all that usable. And it was notoriously difficult to debug and maintain. And it was far too easy for users to enter invalid data. I'm pretty much coding in a vacuum here (and not by choice), so I didn't have the benefit of being able to ask my coworkers for advice. There were no Web designers to ask for input; no testers to rely on to catch my mistakes; no other developers to seek guidance from regarding better class models or unit testing methodologies.

But there were the users and their feedback. So I watched them, listened to them, and learned from them. I studied other user interfaces that I admired, and that users were passionate about. I studied other respected developers, designers and engineers. And I applied their philosophies to the subsequent releases.

The lessons I learned were simple, but breathtaking. Simplicity in design invariably leads to simplicity in testing. If it just works, it just works. Smaller, simpler interfaces are easier to test. Pages with thirty fields and all the validators to ensure they contain solid data are orders of magnitude harder to test. But smaller pages that contain fewer fields are faster, easier to use, easier to test, better encapsulated, and once you've determined that they're working, you're done with them. It radically changed my UI designed philosophy. And I'm happy to report that the software is far better as a result of it. It just works.

Every day, someone somewhere learns something that changes their skill set for the better. When things are looking down, and they think they want to get the hell out of Dodge, it helps to remember all the ways in which we've improved.

The learning never stops. The improvement never stops. But how often do we stop and think about what we've learned? Do we ever give ourselves credit for what we've become? Taking a moment to reflect on where we've been is a crucial part of directing our future; we can't just keep stumbling around repeating the same mistakes. So take a moment, look back, and ask yourself this very simple question: What have I learned over the last year?

You might just be surprised.

Monday, March 12, 2007

Wrap Those Session Variables!

An interesting topic came up on the ASP.NET newsgroup forums today. The question arose: "Is there a standard naming convention for session variables?"

While the debate meandered around the various conventions, and their suitability, and whether or not one even applied to session variables, my chief thought was whether or not one should even see the names of the session variables.

Enter the basic premise of information hiding. The ASP.NET Session object is, essentially a keyed collection. (Let's not bicker about it's internal implementation. List, hash table, whatever. It's essentially a keyed collection, accessed by a string key.)

My point is that the individual keys used to access the items inside should not be replicated throughout your software. What if you choose to change the name of the key? What if the key's name isn't descriptive enough, and needs to be refined?

Sure, you could use a constant, but where do you put it? In a global constants file? That's rather icky. As anyone knows, a global constants file can quickly grow quite large, and navigating it can become a nightmare in and of itself. Then there's the problem of scoping. What if you have two constants with very similar names, and you want them both to have global scope? Now you get to mangle the name of one of them. Good luck choosing.

The ideal solution is to encapsulate the session variables in a class that manages them. This isn't as difficult as it seems, and it provides a host of benefits. The class is relatively straightforward, as shown below.

Option Explicit On
Option Strict On

Imports System.Web.SessionState

Friend NotInheritable Class MySession

Private Const CompanySessionKey As String = "Company"

Private Sub New()
End Sub

Private Shared ReadOnly Property Session As HttpSessionState
Return HttpContext.Current.Session
End Get
End Property

Public ReadOnly Property IsValid() As Boolean
Return Not Session Is Nothing
End Get
End Property

Public Shared Property Company As String
Return DirectCast(Session(CompanySessionKey), String)
End Get
Set(ByVal value As String)
= value
End Set
End Property

End Class

A few things to note about this class:

  • The class has Friend scope. It can't be accessed outside of the Web application. That's just sound security.
  • The Private constructor prevents instantiation. Since all of the other methods are marked Shared, it makes no sense to instantiate an instance of this class.
  • The Session property returns an instance of the current session. This property is marked Private and Shared, and completely hides how we're getting the session information.
  • The IsValid property returns True if we have a valid session object. This helps us to avoid calling the various properties on the MySession class if there isn't a valid session to begin with. This might be the case in an exception handler.
  • The Company property is marked Public and Shared, and is responsible for getting a value out of the session, and putting it into the session. It uses a class-scope constant to reference the session variable, ensuring that both the getter and setter reference the same session variable. Further, the property is strongly typed. When you call this property, the session variable is already converted to the appropriate data type.

Creating this class provides a clean, straightforward way to reference session variables in your code. For example, consider the following (not uncommon) code sample:

lblCompany.Text = DirectCast(Session("company"), String)

Using the class described above, this code is reduced to the following:

lblCompany.Text = MySession.Company

Now, that may not look like much to you now, but imagine what it will save you when you have lots of session variables. And imagine how much easier it will be to refactor those variables should the need arise to do so. 

Finally, you can provide code comments in a centralized place that document what the session variables mean and are used for. That, in and of itself, is a huge boon to productivity. A little clarity goes a long way.

And just to show that I eat my own dog food, I use this very class in my own production software. And it does work, and it does save lots of time. I don't have to remember what that cryptic string is that retrieves a specific session variable. Instead, I type "MySession." and IntelliSense presents me with a list of available session variables.

You might be wondering where the exception handling code is. There isn't any, and that's by design. If a session variable is missing in a getter, it doesn't do me any good to catch that condition and rethrow it--I won't be telling the caller anything that the exception won't already be telling them. My exception handling code in the pages and the fallback exception handler in global.asax are responsible for cleanly handling those exceptions.

It also doesn't do me any good to handle the NullReferenceException that will be thrown if I try to reference the Session and it's not there. Again, the global exception handler will take care of it. I could, of course, wrap the exception in a custom ApplicationException, and you always have that option. Then again, I could always perform the check by calling MySession.IsValid before attempting to retrieve any properties, and avoid the exception altogether.

So there you have it. It's not a hard class to implement, but it pays off pretty well. For a little upfront effort, you get a decent return on your investment. Your code's readability and maintainability improve remarkably, and you know that you can refactor it with a high degree of safety. Further, you can document those session variables in the code, close to the statements that get and set them. And if you decide that you no longer want to use certain session variables, you can easily deprecate them by applying the Obsolete attribute to the properties to quickly identify every point in the code that's using them.

So think about it. And if it's worth your time, implement it. I think you'll be glad you did.