Feeling All Rip Van Winkle

Lately I’ve been having dreams in which I’m having a nightmare from which I’m trying, but failing, to wake myself.  In real life, too, I have been sleepwalking the past six months. At least, professionally. I’m fortunate to have enough work to keep me afloat, but I haven’t challenged myself to grow, and so it’s been all too easy to become stagnant. There are times to yield to dormancy, but it’s all too easy to forget to wake up.

So, how will I challenge myself? Programming always offers new challenges because of the rapid pace of market developments. At the same time programming can be pure drudgery. Oh, I don’t mean in terms of the product the client sees and uses. It’s always invigorating to work with a human being to design something that will make a difference in their day. The actual programming, though, in some ways is no different than it was 20+ years ago when I started. I still love the methodical, chaos-ordering process. At times. At others, I am…bored by it. This is terrible to admit. My brother and I learned as kids that one did not admit to being bored or The Parents would find something for us to do. Besides, there is too much interesting stuff in the world to be bored.

A few years ago, diving into refactoring saved me from ripping my hair out over maintaining old code; but, it’s been a while since I’ve reached out in new directions. Is it my age? Am I done? I really don’t believe that. But no one is going to hand deliver intellectual excitement.

I’ve given a lot of thought to what tools I’d like to explore, and I’ve settled on learning Ruby on Rails. Partly because people I admire recommend it, and partly because it makes me think of Dorothy’s shoes. (Yes, I buy wine based largely on the label.)

Does anyone know where I can find a pair ruby red shoes in size US 7-1/2?

Advertisement

What’s normal?

Normalizing data tweaks the pleasure centers of my brain. Dopamine and experience with far too many legacy systems that were denormalized for no good reason drove me to the “normalize til it hurts” school.

With that state of mind, I read Jeff Atwood’s recent post on normalizing with only moderate interest. Mostly I thought to myself, yeah, well more people need to normalize more often, not less. It was as if someone had reported to an AA group that a glass of red wine a day might be a healthy source of antioxidants (or whatever).

Naturally life conspired to slap me upside the head with a real life exception, and, ironically, it was painful. Here is the normalized version of the data:

A certain organization gives work hour credits to students based on different criteria. There are different kinds of credits, and different credits have different business rules. This is the sort of data that I find most challenging to model in a way that is both flexible and practical. In one way, the data are fixed attributes. There are two types of work hour credits, and five types of technical training credits. Each has a different minimum value and maximum. In some cases the value can only be either the minimum or the maximum. There is also a limit on the total of the five possible technical training credits. Any of these business rules can be overridden by a manager.

I normalized because the data are types, of course. Even if the number of types is limited and static, I’ve had  too many cases of clients assuring me that there will never be any changes only to ask later for modifications. (Which is fine. I’m totally cool with changing needs. I just hate to have programmed in inflexibility when it would have been just as easy to make it flexible, and less costly for the client, in the beginning. Note the emphasis “just as easy.”)

I chafed a bit at the normalization. I could see it was going to be a pain, but programming for my comfort isn’t the goal, after all. The only reason a client should care if something is hard to program is that costs more time and money. I knew optimization wouldn’t be problem. It was so unlikely as to have essentially no weight in my choice.

All was fine til I started to implement the UI that the client wanted for this data. Gad, it was turning out to be a bother. I’m embarrassed it took me so long to realize that I needed to step back and consider whether the degree of normalization was causing far more trouble than it was worth, but I did. In 5 minutes, I had revised the structures to this:

Oh, and the UI? That took another 10 minutes. The pain was from spending four unproductive (read: unbillable) hours futzing around with the first design.

Lessons?

  1. Yes, sometimes denormalization is useful, and not always because of optimization pressures.
  2. It’s easier to denormalize than normalize a production system.
  3. Normalize til it hurts, but also, always be evaluating effort versus benefit gained by normalizing (or denormalizing)
  4. It’s not all or nothing. Some denormalization isn’t the same as fully denormalized. Just denormalize the bits that need it.
  5. I have a tendancy to focus so hard on solving a problem, that I frequently forget to ask if the problem I’m solving is the right problem. Stubbornness is a good characteristic in a programmer, but it’s a liability too.

For the record, I learn lession 5. every (*@)%^*Q day I write code. Though I see it earlier in the process.

Refactoring: Rename Method Example

Have you tried my refactoring challenge[1]?  Since I missed two days in my first week of the 100 Pushups challenge, I guess I can’t complain if you haven’t tried refactoring. Here’s another example that might prompt you to try it. (Refactoring, not push-ups.)

I’m preparing a simple utility so the code is stranger-stable. In prepping the code, the following lines gave me pause.

SaveOldCommand()
WriteCommand( lcNewCommandPrg )

When I have to stop, even briefly, and ask what code is doing, or worse, if I have to divert into another method, I smell a refactoring. At first, I read the code as “SaveCommand, WriteCommand.” Wha..aa? The “Old” in SaveOldCommand() explains it, but it’s buried in the middle of the function name. Similarly the explicitly named variable lcNewCommandPrg passed to WriteCommand is good, but, it comes at the end of the line. I can refactor this code to make it clearer. To do that I used Rename Method. (Whether or not this is the most compelling example, isn’t the point, you kibbitzers out there.)

In a nutshell, renaming a method starts by creating a new method with the new name. Then move the old method’s code into the new method. Finally, change existing calls to the new method. Stay with me: it works on legacy code, too. More on that later. Here are the steps:

  1. Test the code (baseline, right?)
  2. Create a new method with the new name, with the same parameters, and test.
  3. Copy the code from the old method into the new one, and test.
  4. Find all references to the old method and change them, one at a time, to the new method.
  5. Test after every change in step 4.

Yes. Step 4 is a good example of “And then a miracle occurs” programming. But, no fears, this technique is adaptable to more difficult situations. In this case, however, I know it’s safe to remove the old method. After I refactor, the code is:

BackupOld()
WriteNew( lcNewPrg )

It’s usually impractical to rename a method other than in early development, isolated utilities, or hidden methods. Legacy code is too hard to search thoroughly and is rarely testable with the degree of assurance that’s required. If code is a control or application that is used by customers I cannot change method names at all. However, I can safely create new methods and change the old ones to call the new.

Consider the following changes to the steps above:

  1. Test the code.
  2. Use Extract Method to move the code from the old method to a new, better named method, and replace the old code with a call to the new method. Test.
  3. Change existing references to the old method, testing after each change for as long as you can stand it.
  4. Test.

“What’s the point of just adding another line of code to execute,” you ask? Consider a class that you will continue to use, or a class that customers use. Even if old code still calls the new method, a more clearly named method will be easier to work with, and the code that calls it will be more readable. Also, you might use BindEvent (VFP) to bind the old method to the new.

For example, years ago I adapted a Visual FoxPro foundation label class to send email. The original class used a method named something unhelpful. I’d added hooks so instances and subclasses could manipulate the email before it was sent. As I used the class in different projects, I wanted a more clearly named method, and so created a SendMail method.

The trouble with removing the old method was that I used the class in several different projects, and while I could have used Agent Ransack to find code references (this was in VFP 7.0, which doesn’t have Code References), I didn’t have time to find, replace, and test all the changes needed. Plus, I really just wanted to make it easier to use in the future. Otherwise it worked just fine as it already existed. So, I chose to the second refactoring and used Extract Method.

Why do I mention this? I want to convince the skeptic that refactoring is flexible, doesn’t require a massive investment in time, and leaves code better but still stable. I’ll keep coming back to these facts time and again. I can’t imagine why anyone wouldn’t want to refactor whenever possible. But then, I can’t imagine why anyone would eat lima beans willingly, either.

[1]No, I’m not really delusional about the size of my audience, its interest in refactoring, or my powers of persuasion. I just like to pretend.

Code comments, b-aaaad

Jeff Atwood of Coding Horror is on my daily circuit of blogs (which I read right after the daily funnies, ‘natch). Coding without Comments had me singin’ out “amen, brother!”

Comments are a “bad smell” [1] code uses to whisper “refactor me, please, I beg you.” The Problem with Comments and Coders Who Love Them is one of the top five lessons I gleaned from Martin Fowler’s Refactoring [2]. Good comments explain why or how, not what. I love this smell. It’s got everything:

  • Glamor–comments attract the eye
  • Brains–comments encapsulate the solution by the very comment
  • Wit–the refactoring is often simple, quick, and it’s got great ROI

I’m taking the 100 pushup challenge (I’m up to two). Here’s is your challenge, and it’s only for a week. Watch for comments as you work, and use them to identify the places code can be broken down into smaller, more discoverable chunks.

There…right there…that code you’re mucking around in? Unsure how or where to add or change that feature? Is it a long section of code with comments interspersed explaining what different sections of code do? You probably have a case for … da ta daaaa … Extract Method. The general plan is simple enough. The comment will hint at what the new method should be called, and all the code that the comment applies to will go in that new method. In a nutshell:

  1. Test the code (so you know later if something’s gone wrong)
  2. Create a new, empty method, test (yes, it really is that easy to make a bug)
  3. Copy the code to the new method, test. Yes, if you get interrupted, no harm done and you’ll have a good idea of where you where the next time you visit.
  4. Look for variables that are initialized or used elsewhere. Are they used elsewhere? Then, pass as parameters. Otherwise, localize them to the new method.
  5. Hm. I feel like I’m forgetting something…Oh! Right! Test!
  6. Replace (or comment) the code that’s been moved to the new method with a method call. I comment until after the last test.
  7. And, last, class? Yes, very good. Test.

If this time is hard to justify either to yourself or your muckety-muck (hey, more muck), say it’s preparing the ground for adding features. At least you’ll see whether you are in the right section of code. If so, how can the effort be wasted? If you’re in the wrong place…even better. Yes, I mean that. It saves you screwing up some code that was just minding it’s own business (refactoring doesn’t change behavior), and you’ll waste less time the next time you visit.


[1] “Bad smells” are patterns in code that hint, but only hint, there is something violating good coding practice.

[2] Refactoring is the controlled process of changing code without changing behavior so that the overall design is improved. One doesn’t open up code at random and start refactoring willy-nilly. Only a nut would want to change code just to change code. And, yes, I’ve done it, and yes, I was…well…I called myself worse than “nut.” The only reason to touch code is because something needs adding or changing. Duh.

Discouraged.

The worst experience I had backpacking came on the first day out on a hike in the Chiricahua Mountians in Arizona. What I found discouraging was that for every 1000 ft. in elevation gained, we had to lose, and then regain, several hundred feet. The last time we dropped down to the last reliable water source for three days and filled our water pouches. (I don’t marvel that Geronimo evaded troops for a year in those mountains. I marvel that he was ever caught.)

I thought of this trip again this morning when I sat down to start again on code I’ve been working on for the past four days. I was disgusted and discouraged. As the late Ed Rauh used to say, I felt it really was time to look for that job in the food service industry.

It’s not important what the code is, or why it’s discouraging. I do rather hope I’m not the only one who has moments when they think “what in the hell am I doing thinking I can program?” There’s nothing to do but push on.

Some days coding is just a slog uphill with 50 lbs of water and seven days of supplies.

It was a helluva trip.