Web outage on AppDev

Yesterday, AppDev was down for about five hours. I'm afraid I don't know if this was related to the San Francisco power outage, because my web hosting provider can't (or won't) tell me what happened. Sorry for the outage, and if you're considering a web host, chalk this up as a less than ringing endorsement for their customer service.

(update)

More outages today. This time, it's the database server. So much for the power outage theory. This afternoon, I was notified of another outage (mon.itor.us), so I called customer service. "All lines are busy." Not a good sign. I finally got through to the automated attendant, which informed me that "call volume is high", and suggested cruising the FAQ's. Then it hung up on me.

So, needless to say, I'm looking for a new host.

Sigh.

Microsoft “7” — This can’t be for real, can it?

Microsoft is apparently calling their Vista successor "7", according to Techmeme and Engadget. Now, I know that Microsoft has a track record of picking some odd code names, but this one takes the cake.

I think if I were picking code names at Microsoft, I'd steer clear of Borg references. Then again, maybe we're seeing the first glimpses of their next ad campaign:

Microsoft "7"

Think sexy Borg:

... not Bill Borg:

Boy, did AT&T tick off the wrong person!

Today, Guy Kawasaki sounded off on his experience with AT&T customer service ("My iPhone Review"). It was the perfect zinger, coming in from left field when the entire media universe is just hours away from the iPhone love-fest crescendo.

A million other people have had this same conversation. Maybe not with AT&T. Maybe not in a chat window. Maybe they've been shafted by Windows Live OneCare. Maybe they bought a heat sink that squealed to an early death.

Everyone's been there. But not everyone has Guy Kawasaki's readership. You know this one is going to leave a mark. On behalf of everyone who's received lousy customer service, thank you.

This is an example of the sort of open information flow that has only been possible in the last few years. This is why you saw a mirror on Time's Person of the Year issue. Aside from the glee I take in seeing a faceless corporate automaton called out, I really marvel at Web 2.0's potential to radiate information into the common consciousness. I saw this particular story because I've subscribed to Guy's RSS feed, but this is a great post, and it's going to revererate across the blogosphere in a very short period of time.

Just in time for millions of i-groupies to storm Apple stores across the country, they have a little glimpse into their future. Hopefully, some of them will be a little wiser for it.

“We” is a Good Way to Get Nothing Done

IMG_3800I've noticed through the years that lots of people shoot themselves in the foot over and over again, and they never realize that they're doing it. It's the simplest thing in the world, yet it seems to constantly evade even seasoned managers. Paradoxically, it even seems to be a great way to reach out to people, empowering them to do great things, yet, it'll kill you every time.

So, what's the problem? Simple. If you make more than one person responsible for something, it's only by the greatest miracle that it'll get done at all. Doesn't seem to make sense, does it? After all, adding people to a task should make it move more quickly. Adding brains to a problem should boost creativity. Why does this break down? In short, if more than one person is responsible for a task, then nobody is responsible.

Here's what I'm talking about. If you've ever been in a meeting -- a hallway meeting or a board meeting -- and heard someone say, "I thought you were doing that!", then you've seen this phenomenon in action. As soon as there's any ambiguity about whose head is going to roll if there's a problem, you invite more problems. This isn't necessarily because people aren't trying hard, though this does provide a convenient avenue for scapegoating. No, it's really a problem of communicating and personal integrity, and thus, it scales up exponentially as the number of "responsible" parties grows.

Let's walk through an example. Let's say you're chairing a committee of peers, and you ask them collectively to achieve some objectives, you're very likely to see one or more of the following scenarios play out:

  • The committee members sit down and rationally divide the work, then each goes off and completes the work as intended. Riiiigghhttt..... While this does sometimes happen just like that, it's only when all three of the people are responsible, reliable, and free from political motivation. It can happen, but you can't rely on it in the average organization.
  • One member steps up and takes responsibility for the group. He monitors the division of work, and he checks to make sure that things get done. This is essentially a project manager role for that group of tasks. Note that since this is an informal organization, you still need the cooperation of the other members. Any one of them can intentionally or unintentionally torpedo his individual task, putting the whole project at risk
  • Political infighting erupts over division of work, credit for success, blame for failure, and so on. This might seem very likely, but I've rarely seen it, since this sort of bickering draws negative attention in a hurry.
  • Nothing gets done. Variations on this theme would be very little gets done, or something gets done very, very late, or something gets done that has no significant resemblance to the intended objective. This scenario is quite common, because it's the easiest, and the responsibility structure allows any of the users to "opt out" of the work, and then re-surface to point fingers when questions arise.

All of this sounds pretty pessimistic, as if I expect everybody in any given business environment to be lazy, politically-motivated, ruthless back-stabbers. This isn't the case, though I've certainly seen it to be true from time to time. No, in most cases, these are dedicated, honest, hard-working co-workers who are simply trying to do their jobs to the best of their abilities. The best and most successful of these people will, by nature, be the busiest. No good deed goes unpunished, after all. These busy people probably won't have time for anything else. In fact, if you ask them directly to do something for you, there's a good chance they'll tell you that they're too busy.

If you ask them as part of a group to collectively do something together, however, there's a good chance they won't tell you that they're too busy because they assume that someone else is going to pick up that task. And therein lies the rub. Asking a group of people to do something allows the no-goods to squirm out of responsibility on purpose, and it allows the go-getters to wrongly believe that someone else is going to step up to the plate and do the right thing. Either way, there's a good chance that they ball is going to get dropped.

So be direct. It's not rude or imposing - it's the only way to get something done. Make sure that when a task is given, it's given to one responsible person (even if other people are asked to "help"). Make sure that person realizes that an assignment happened. And then follow up to make sure it's done.

It sounds simple, but you'll be amazed how infrequently it happens when you start looking for it.

Bug Tracking 101: Where TFS Blows It

As a developer, I love Microsoft Team Foundation Server. As a manager, I hate it.

Why? Bug tracking.

"But TFS does bug tracking," you say. Sure it does. So let me try to explain why Microsoft's brand of bug tracking just isn't going to get the job done. I'll start with my personal opinions about bug tracking.

Bug tracking is one of the basic life-support functions in software development. I'd rank it second after Source Code Control as far as critical support functions. To put it bluntly, there's just no reason not to have a bug tracking system these days. There are hundreds of very good systems out there, and you can get most of them free in a box of breakfast cereal.

It's pretty common, in my experience, for people to argue against logging bugs. There's a perception that logging a bug is a big, hairy deal, or maybe they want to keep the count low in their "real" bug database so their numbers look good. Bull-pucky, I say. Log everything. If a bug occurs and it doesn't get recorded, it never happened.

Sure, you're going to get some bad bugs. You're going to get some duplicates. You're going to get some bugs you can't reproduce.

So what?

If you log a bug and have to close it because it's a bad bug, you spend some time doing so. Bug if you don't log a bug, you can never reverse that decision. In fairness, you'll usually want to have *something* in a report that's actionable, but I really prefer to set the bar low so I sweep more bugs into the net. I can far more easily wipe out the bad than I can make up for lack of data if there's something going on that I'm not seeing.

You may have inferred by this point that I prefer to let most, if not all users and testers enter bugs. Right on. Again, this is to help gather information as close to the source as possible. And yes, again, this increases the chance for junk data. And yes, yet again, the alternative is no data. If you make it easy for lots of people to give you feedback, some of them will give you feedback. If you make it difficult, damned few will give you any at all. You're in the dark -- probably with a false sense of confidence about the state of your system. "Look - no bugs logged in the last week! We're perfect!" Sure, you are.

Ok, so that's how I feel about bugs. Feel free to argue amongst yourself, or even with me. 😉

Back to TFS. Like I said, they have a bug tracker, so what's the problem? Not a thing, if you happen to be a developer with TFS already running as part of your IDE. If you're not a developer, here's what you've got to do to use TFS:

  • Buy a license. Yes, if you want to log a bug, you need a full TFS client license. Just to log a bug. JUST TO LOG A BUG.
  • Since you have to buy a license, you're probably going to need to justify that license to somebody (not that I've run into that brick wall recently, or anything....). Quick review: you're going to be asked to make a business case for purchasing a client license for someone so they can LOG A BUG!! Let me know how that goes for you, ok?
  • Now you need to get Team Explorer installed on the workstation of the person who's supposed to log the bug. Oh, no -- you can't just browse to a web page to do this (despite the fact that TFS makes heavy use of SharePoint). Is any of this making sense to you? Astute readers will observe at this point that there are add-on software components available to web-enable TFS. I'll observe right back at you that the very fact that this is an add-on tells you something about whether Microsoft gets how this software should be used.
  • Now, your user is ready to log a bug. After they open Team Explorer. On the machine where it's installed. Oh, they're at a different machine now? Maybe they're testing on a Mac? Nuts. Well, write everything down and log it when you get back to your desk. Poof! Your bug just disappeared, because 99 out of 100 users are just going to say, "why bother?" And they'd be right, of course.

Come on, Microsoft -- think this through and get it fixed. Now. Please.

In the mean time, use this.

Translators are a pain! Use Reflection to make them easier.

I'm working with a SOA architecture that's forcing me to bend, fold, spindle, and mutilate objects as they move across tiers. This is awkward, but it's a common side-effect of splitting applications across tiers. One of the implications of this design is that objects have to be translated, or copied value by value across tiers.

This work is pretty straightforward, but it's tedious, and worse, it doesn't add any value to the software -- it's just plumbing. Boy, I thought, wouldn't it be nice to be able to hook two equivalent objects up and move values from one to the other automatically.

Enter reflection. Now, before I show the goodies, you'll want to be careful where you use this. Reflection isn't as efficient as writing early-bound code, but when used sparingly, it can haul a lot of groceries with very little code. If nothing else, use this as a quick primer on reflection. It's not as hard as many people think, and it's a great technique to have in your toolbox.

Here's the code. If I were to enhance it, I might have it raise an event when it comes across a property it can't match, so a consumer could handle custom maps in-line. I think it would also be great to use this as the basis for a Visual Studio addin to manufacture early-bound code for translation - sort of like the translator wizard in the Web Services Factory, but a little more automatic. Have fun with it...

public class Translator
    {
        /// <summary>
        /// Use reflection to copy as many properties from the "from" object
        /// to the "to" object as possible, matching properties by name.
        /// </summary>
        /// <param name="objFrom">Copy from object</param>
        /// <param name="objTo">Copy to object</param>
        public static void Translate(object objFrom, object objTo)
        {
            Type tFrom = objFrom.GetType();
            Type tTo = objTo.GetType();

            //Get the properties of our type
            PropertyInfo[] fromProps = tFrom.GetProperties();

            foreach (PropertyInfo pInfo in fromProps)
            {
                try
                {
                    object[] fromValue = new object[1];
                    fromValue[0] = tFrom.InvokeMember(pInfo.Name, BindingFlags.GetProperty, null, objFrom, null);
                    tTo.InvokeMember(pInfo.Name, BindingFlags.SetProperty, null, objTo, fromValue);
                }
                catch (Exception ex) { }
            }
        }
    }

Mouse Balls

My current client is insistent that my team use their equipment while we're working on their software. I can't say I really agree with this, but I can understand the spirit of the request. More difficult to understand, however, is why they'd persist in this direction despite the fact that the hardware isn't up to the job. Processor speed is fair, but there's not enough RAM, and I think we'd have to have a personal memo from the governor to get a second monitor.

But the thing that's driving me absolutely batty is my mouse. Yes, it's an old-style mechanical mouse, complete with the rubber marble that turns the little rollers inside. The moving parts pick up junk and start to fail, and no amount of cleaning makes it quite right again. Every day, I end up taking the thing apart a couple times, to very little effect, and every day, I silently boil every time I move my mouse and the cursor just sits where it was, blinking in silent mockery of me and my defective hardware.

Yes, I've tried to obtain a new mouse. Paperwork has been obtained, filled out, and signed. As of my last trip up that mountain, I hadn't yet achieved enough signatures to move this $12 transaction forward. Yes, the time I've spent on paperwork and signatures exceeds (easily) the cost of a new mouse. Yes, I could probably go out and buy a damned mouse myself and bring it in. I very well may end up doing that the next time I order from Newegg or Amazon.

In the mean time, I have to marvel at this brilliant example of process gone astray...while I clean my mouse.

A Special Kind of Crazy

The franken-systems we often find ourselves immersed in are a product of a hundred bad decisions, compounded like interest until they yield a wealth of dysfunction. Such is the case in this example.

First, some background. I'm working on a system to do some event scheduling. At the beginning of the project, my team learned that we'd also be implementing a web service for a name and address system, since we needed some of that data, and the client kind of wanted a web service for that other system anyway. We could take as much time to build it as we needed, up to two weeks. A couple other guys on the team built this web service under the direction of a designer. This train of thought contains several bad decisions, for those keeping score at home.

Fast-forward to today. I'm working on updating address information in the scheduling system. What should have been a pretty simple effort had proven ridiculously difficult. The root of the current set of problems is the design of the aforementioned web service. Specifically, I'm beating my head on the wall over the state and county components of the address.

These are not, in fact, textual representations of the state and county, respectively. They're objects. Now, don't get me wrong - I'm a big fan of objects, and I can appreciate the elegance of a wide and deep hierarchy. It's a great way to explore a problem domain you may not be familiar with. Invoices have headers and detail lines, and a detail line has an item and a quantity and a description, right?

But is that really what you need for a state?

Let me run through some of the places where the choice of an object for the state has caused problems.

  • ASP.Net DataGrid data binding. It turns out that the DataGrid doesn't handle complex objects well, so it just couldn't cope with State as a whole object within an Address. There are multiple workarounds for this, of course, including replacing the grid, I'd expect. The direction I went on this was to craft a UIAddress object that flattened an Address the way I needed it. This resulted in extra code - not just to define the object, but to translate to and from the new, different address forms, but it worked for the UI.
  • Updates. It turns out that when you update the state value for an address, not only do you need to reach down in to the object (address.State.StateAbbreviation) -- no, that's not quite enough. Try to update that, and you'll get an error -- a key violation error -- because you haven't set the StateID to a valid value. That's right. You can't set an address to a different state unless you happen to know the database key of that state. Upon further examination, this made a bit more sense to me. After all, if we happen to grant statehood to Puerto Rico or maybe annex Canada or something, and we choose to abbreviate Puerto Rico as
    "TX" or "AB", well, then, we'd just have a mess on our hands, wouldn't we? So you can't just send in the state abbreviation -- that won't work at all. Sigh.
  • New objects. You can't just create a new Address() now. If you do that, you won't have initialized the corresponding State and County objects. Again, not the end of the world, but it's just a little bit more code you have to write whenever you use these objects.

The moral of this story is clear - consider the users of your APIs before they show up at the gates with pitchforks and torches.

When Tools let us Down

This afternoon, I was building a new class. I had a handful of data items I knew I needed, so I made fields for them, and then began constructing properties to encapsulate the fields. This is a well-known best practice, of course, because it lets you protect your internal representation of this data, and to therefore insulate consumers of your class from any changes in this underlying data.

Needless to say, setting up a property get...set block is slightly more time-consuming than just creating a field and being done with it. Along comes the Visual Studio C# refactoring tools to help encourage the proper behavior. Just right-click on your field, choose "Refactor...." --> "Encapsulate Field", and you can create the getter and setter in one swift action.

Except that this dialog takes longer to load (at least on this client's system) than it would take to type out the damned block to begin with. Thanks for the gesture, though.

In this case, the problem seems to be that the tool is solving a larger problem than I've got right now. It's able to seek out existing code that's already referencing this field and repair the effects of any name changes you make.

Ok, that's cool. But I don't need that right now. This is a brand-new class -- there are no references to these fields yet. But someone might need this, and I concede the fact that if I found myself needing functionality like this, I'd scream just as loudly if it was taken away from me.

So is there a better solution? I think so -- two, in fact, in this case, or maybe even three.

The first improvement I'd consider is to not spend time hooking up the "search all references" stuff unless I said I needed it.

In my case, though, there's an even better solution. The fields I was trying to encapsulate were already marked "private", which means that they can't be referenced outside the current class. That should simplify matters considerably.

Oh, and the third improvement, for extra credit, would be to multi-thread this dialog so I can run the simple functionality even if the advanced stuff is still loading.

Usability is everywhere.

George S. Patton on Refactoring

"The time to take counsel of your fears is before you make an important battle decision. That's the time to listen to every fear you can imagine! When you have collected all the facts and fears and made your decision, turn off all your fears and go ahead."

-- George Patton

I know, I know -- what in the world did Patton know about refactoring?

Nothing. But he knew a hell of a lot about getting things done. He knew that there is a time for planning and a time for acting, and he knew that if you prepared when it was time to prepare, you should feel confident in your actions because you've done your homework ahead of time. Had he lived to witness Agile development, he would have had the following advice about refactoring:

Preparation

Patton knew that no battle was ever won without proper preparation. For him, that meant training, discipline, and strategic planning. We could learn a few things from him.

  • Training. It's important to know the technology you're working with. This shouldn't be a news flash, but you won't be effective unless you are fluent in the development language and architecture in use. Please also be comfortable with development best practices such as those from McConnell.
  • Version Control. This had better be another no-brainer. In fact, if you don't have your code in an SCC system, give up now and go into sales. You're not going to make it in software development.
  • Unit tests. Make sure you have them, and make sure they're really exercising the system. It's sometimes difficult to hit 100% code coverage, but if you're not at 80% or better, you're not trying very hard, and you're badly exposed to regression bugs. You'll deserve whatever sad fate awaits you.
  • Objective. Patton knew the value of planning. In this case, you need to clearly understand what you're trying to accomplish with your refactoring. Maybe there's a pattern that isn't being followed consistently. Maybe there's functionality that should be extracted into an object or component. Whatever - just make sure you know exactly what you're trying to get done. When you start to work, you'll find it easy to be distracted - there's always something else to fix or improve. While Patton also understood the value of tactical flexibility, losing sight of your objective is the best way to ensure that you'll fail.

Refactoring

Now you're ready to work, so crank up the chain saw. Now is not the time to be timid - you've planned for this, so make it happen. While you're in there,

  • Don't leave dead code -- commented dead code is useless. It's available in SCC if you need it. You don't buy a book and expect to see all the edits. Outtakes can be funny, but not in the middle of the movie. You want your live code to be living -- not a monument to your past sins. You already decided that your old code needed to be refactored, so don't chicken out now. An agile methodology thrives on having frequent builds that are as near to production-ready as possible. You wouldn't want to ship with dead code in your project, so get rid of it.
  • Don't half-refactor. You're probably going to want to try to reach some interim stable points where you can build and run some unit tests, but beyond that, try not to stop until you're done. When you are refactoring, this is your chance to apply a consistent thought process, design, and coding style across the project. Future developers will thank you if you manage not to leave a Frankenstein-style mishmash of source code.
  • Keep notes. If you find additional areas that need work, make a note somewhere where you'll follow up.
  • Test. You need to see clean unit tests before you're done. Remember, commenting the test out doesn't count. If you can, run through some UI-level or System testing, too.

As always, scale the process to match the task - small refactoring jobs move through planning and refactoring in seconds, but large efforts can take considerable time. Remember, though, that source code control is never optional, and if you skip unit tests, you're really asking for trouble.

What other refactoring tips do you have?