Tuesday, May 28, 2013

StructureMap Convention Scanner

Several jobs ago, an architect introduced his team (of which I was a member) to dependency injection, specifically using StructureMap. Admittedly it took me a while to get it, but once I did I became hooked and have been ever since. To this day I still prefer StructureMap, if for no other reason than I'm very familiar with it.

And over the years, my use of this tool has rarely seen drastic change. The pattern from that old team, which used the Common Service Locator (of which I use a smaller home-grown version), worked very well and has fit the bill for almost all of my control inverting needs. From time to time, however, I would come up with some new need and have to create something new to handle it.

To date, my biggest change was the use of a custom convention scanner. Nothing fancy, it just scanned assemblies and used my own custom naming convention for interfaces (I dropped the "I" and never looked back) and matched implementations to interfaces. The scanner itself looked like this:

public class DomainInterfaceNamingConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (IsntRegisterable(type))
            return;

        Type interfaceType = type.GetInterface(type.Name.Replace("Implementation", string.Empty));
        registry.AddType(interfaceType, type);
    }

    private bool IsntRegisterable(Type type)
    {
        return type.IsAbstract || !type.IsClass || !ImplementsACustomInterface(type);
    }

    private bool ImplementsACustomInterface(Type type)
    {
        foreach (var iface in type.GetInterfaces())
            if (iface.Namespace.Contains("Acme"))
                return true;
        return false;
    }
}

Simple enough. If the type is one of the types I want to register, it gets the interface that it's implementing based on the word "Implementation" at the end of that interface (my own convention, favored over configuration) and adds it to the registry. This has worked splendidly for years. Until I thought of something I wanted to support but couldn't with this.

This convention assumes something I'd always been assuming and had never bothered me. Namely that all instances are default instances. And in all fairness, I've never had a need for named instances. I was able to swap out different implementations by changing some custom configuration settings. The bootstrapper which references this convention scanner would dynamically build the assemblies based on those configuration settings.

So, let's say I have three implementations of a set of data repositories. Using a naming convention (favored over configuration again, and borrowed in large part from that same job long ago), the assemblies would be named something like this:

  • Acme.Infrastructure.DAL.SQLExpress
  • Acme.Infrastructure.DAL.XMLFiles
  • Acme.Infrastructure.DAL.Mock
These would be three valid implementations of my repositories, all transparent to the rest of the domain, and which one any given application instance uses would be a config setting.

But recently I found a situation where I might want the same application instance to use multiple implementations for the same dependency. Without going into too many details, let's say for the sake of argument that the application needs to move data from one place to another. If I can only use one implementation of a given dependency, then one of those two "places" would have to be a completely different dependency.

This led me down a distasteful path. Things which logically should just be repository implementations (because they're just persisting data) ended up being their own isolated dependencies, filled with DTOs that were littering my models.

The first example of this on a project was when I had to integrate some different calendar systems into our event data. We have a database for storing event data, and that's essentially the system of record for that data. It has repository implementations accordingly. However, the business wanted to manage events using a third party tool (in this case some crappy-but-functional desktop calendar application), and additionally wanted to publish events to a third party tool (in this case, Google Calendar).

Well, the DAL dependency was already taken up by the event data repositories. So I introduced a new dependency called CalendarManager and another called CalendarPublisher. I wrote implementations for them using these two third party systems, as well as mock implementations for testing. And essentially the process would be to read from the CalendarManager, persist to the Repositories, perform some domain logic, read from the Repositories, persist to the CalendarPublisher.

It worked, but it was distasteful. I already have models and repository structures for Events. These other dependencies should just be alternate implementations of the repositories for those models. But then one application instance wouldn't be able to use all three.

What I needed were named instances. But whenever I've seen named instances used in the past, they were on a class level instead of an assembly level. I don't want to have to specify every individual class in my bootstrapping code. For starters, that would favor configuration over convention which I don't want to do. But more importantly it would mean that any implementation or any interface that's added to the domain would have to be manually specified there. Unintuitive at best, error prone at worst. I'd much rather just have to specify the assemblies (of which there are several) instead of the classes (of which there are hundreds).

So how can I name my instances at the assembly level? How can I scan my assemblies and add them into the object graph from which I could essentially pull named instances like this?:

var eventSource = IoCFactory.GetInstance<EventRepository>("CalendarPlanner");
var eventDestination = IoCFactory.GetInstance<EventRepository>("GoogleCalendar");
var eventData = IoCFactory.GetInstance<EventRepository>();

Basically I'm looking to be able to specify an instance, or take a default. (In this case the default for the repositories would be the database implementation.)

After some back and forth on Stack Overflow and a lot of tinkering, I've ended up with this:

public class DomainInterfaceNamingConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (IsntRegisterable(type))
            return;

        var interfaceType = GetInterfaceType(type);
        var dependencyName = GetDependencyName(type);
        var implementationName = GetImplementationName(type);

        AddInstanceUnlessOverridden(type, registry, interfaceType, dependencyName, implementationName);
        AddInstanceOverrides(type, registry, interfaceType, dependencyName, implementationName);
        AddDefaultInstance(type, registry, interfaceType, dependencyName, implementationName);
    }

    private static void AddDefaultInstance(Type type, Registry registry, Type interfaceType, string dependencyName, string implementationName)
    {
        if (ConfigurationFactory.GetDefault(dependencyName) == implementationName)
            registry.For(interfaceType).Use(type);
    }

    private static void AddInstanceUnlessOverridden(Type type, Registry registry, Type interfaceType, string dependencyName, string implementationName)
    {
        if (!ConfigurationFactory.GetOverrides(dependencyName).Keys.Contains(implementationName))
            registry.For(interfaceType).Add(type).Named(implementationName);
    }

    private static void AddInstanceOverrides(Type type, Registry registry, Type interfaceType, string dependencyName, string implementationName)
    {
        if (ConfigurationFactory.GetOverrides(dependencyName).Values.Contains(implementationName))
            foreach (var dependencyOverride in ConfigurationFactory.GetOverrides(dependencyName).Where(o => o.Value == implementationName))
                registry.For(interfaceType).Add(type).Named(dependencyOverride.Key);
    }

    private static string GetImplementationName(Type type)
    {
        return type.Assembly.GetName().Name.Split('.').Last();
    }

    private static string GetDependencyName(Type type)
    {
        return type.Assembly.GetName().Name.Split('.').Reverse().Skip(1).First();
    }

    // etc.

}

Still favoring convention over configuration, I've continued with the assumption of my assembly names. Given that, it takes a minimal amount of reflection to get the name of the assembly for the implementation and use that convention to name the instances.

It was then irresistible to take it a step further and define some more custom configuration for these overrides. After all, one of the biggest reasons I have this setup is for testing. I like to create custom mock implementations for dependencies and then my testing instance (which has its own config file) can simply specify to use the mock implementations instead of the default ones. This is especially useful for automated integration testing because I can keep multiple config files for the tests and just have the build scripts deploy and run multiple instances of the test code with different config files. Thus isolating individual dependency implementations for testing while using mocks for everything else. This greatly reduces the number of variables in automated testing for me.

So now in that custom configuration section I can override defaults as well as override named instances. Thus, even if my code calls for this:

var eventDestination = IoCFactory.GetInstance<EventRepository>("GoogleCalendar");

I might decide to override that for a specific application, essentially telling it that "even if I ask you for this specific instance, give me this other one (such as the Mock) instead." Thus far this has prevented me from too tightly coupling the code asking for the implementations with the implementations themselves. The application isn't tightly bound to the actual implementation, it can override it. So as long as I keep my names fairly general, I'm happy with the level of coupling.

Not shown here is the implementation of ConfigurationFactory, which is local to my IoC implementation project. But basically all it does is check the config file (using a standard .NET custom config section implementation) for any specified defaults or overrides. So, for example, by default the non-named instance for the repositories might be the SQLExpress implementation. In the config file I might then say to use the XML one as the default instead, so non-named instances become the XML one for that application. I may then also take it a step further and configure it to use the XML one even when the SQLExpress one is explicitly requested. (Which doesn't happen in the codebase at this time, but it does for other implementations such as the aforementioned Event stuff.)

All in all, I'm pretty happy with this implementation. And I'll be a lot happier once I go back through the code and re-implement these custom dependencies as repositories which they should have been all along. This will reduce the number of service DTOs in the system to almost none, making use of the existing models instead. And it will get rid of several mock implementations since I can just re-use the one I already have for the DAL.

Tuesday, April 30, 2013

I Love My Surface RT, But...

Each holiday season my employer gets a "tech gift" for everybody in the company. And last year was quite possibly the biggest one yet, a Surface RT. Before I go any further, I want to tell you that I love my Surface RT. I loves it very much. I love the feel of it. I love its usefulness as a Netflix appliance (huzzah for the built-in kick stand!). I love writing code for it. I love that I work for an employer who buys us things like that. All in all, I am very happy with my Surface RT.

But...

Well, I've come to realize why I'm so happy with it. It's not because the Surface is a compelling product or provides a rich experience or anything like that. It's not because the ratio of price to value is so good. It's not because it's a good product. No. It's because I'm a tech geek. And, as a tech geek, I like tech toys. It didn't have to specifically be a Surface, it could have been anything. Any shiny new toy would have been great. Even look at how I worded it in the above paragraph... "I love my Surface RT." I don't love the product, I just love that I have one.

Additionally, I've come to realize that my colleagues and I, who all share this love for our Surface RTs, are in a distinct minority. Most people do not love this product. And, honestly, I don't blame them.

The most direct experience I've had with this is watching my family. I have a wife and three daughters. (Only two of whom are old enough to even use such a device. The infant would just drool on it.) For a while, I didn't let anybody use my Surface. We have an iPad, we have a Kindle Fire (flashed with a real version of Android of course, since that Amazon-only stuff was crap), we have a handful of iPhones between us, we have computers and devices and toys aplenty. So I kept the Surface for myself.

My daughters didn't like that, of course. "Daddy, can we play with the new big phone?" (They started calling the iPad a "big phone" when I first got it. They were younger and, well, it was just like Daddy's phone but bigger. The name has kind of stuck.) "Sorry honey, that one's mine. I need to do important stuff on it. You can use the other ones, though." And they were happy enough to do so, but that lingering desire for the new toy was always there.

One day, somewhat recently, I opened up relations with China. I allowed the family unfettered use of the Surface RT. I presented it to them, power cord in hand, and instructed them to go forth and enjoy the device.

That lasted only a few minutes. Literally.

First, I had to create a user account other than my own. After all, one of the cool features of Windows 8 devices is the multi-user setup. And since my Windows 8 account is used across a handful of other computers and devices, I didn't want the kids polluting my stuff. So I set about creating an account. This was a painful process. It was clunky, unintuitive, and just overall dismal. But I put up with it. (I don't have any specifics on it to share at this time, it was a short while ago but long enough that the details are forgotten. Just know that, while the OS walks you through the initial setup rather well, setting up additional users is jarring and unpleasant. At least it was for me.)

The process of setting up a user account for my older daughter actually lasted longer than her interest in the device. Again, literally.

My older daughter took hold of the Surface and began to play. First thing's first, she wanted to play Minecraft. (She loves playing it on the iOS devices and on the family computers.) Sorry, not available. The app store doesn't have one, and the full version doesn't run on RT. So she looked for other games. There... aren't many. In the commercials they highlight Angry Birds, which is available. But who the hell cares about Angry Birds anymore?

The only thing she could find to do with it was watch something on Netflix. (Honestly, that's about 95% of my use of it as well.) This is where the kick stand comes in handy. You can place it nearby (such as on a night stand) and watch your favorite movies and TV shows. At least until the battery runs out. Which will be soon.

This trend continued until everybody in the house completely lost interest in the device. It currently sits unused on a table, devoid of battery life because nobody cares enough to plug it in.

Again, I love this device. It's great, as a toy. As a family computing device, not so much. This is pretty evident in the fact that my family just doesn't give a crap about it. Remember how great the built-in kick stand was? Each time my older daughter gets something to eat and sits down at the table to watch Netflix, does she grab the device with the built-in kick stand? No. She grabs the iPad, leans it against something, and places a napkin between it and the table to create some static friction to keep it standing up.

I want so much for this to be a more compelling device. I want to write software for it. I want it to open up .NET development throughout the tablet and mobile spaces even more than Mono does. (Just listening to that last sentence cements the fact that I am a tech geek, not an average consumer.) I want it to be successful. But it's not. Microsoft apologists in general will happily tell you that "the next one will be better" but I've learned not to hold my breath on that. (Pick a Microsoft product, and I'll show you an apologist who has said this about the shortcomings for any given version of it.)

Today a colleague showed us an amusing web site called iPad Death Watch. Apparently the Microsoft apologists are thriving here. To give you an idea, this is my favorite quote about Apple's iPad on that page:
"What an utter disappointment and abysmal failure of an Apple product. How can Steve Jobs stand up on that stage and hype this product up and not see everything this thing is not and everything this thing is lacking?"
It's so delicious to read it must be fattening. Honestly, I can't tell if that page is serious or satire. Imagining listening to someone say all the same things as Stephen Colbert, but it's not Stephen Colbert. Are they a comedian or are they insane? It'd be difficult to discern.

Amid all of the comments on that page, however, is an infographic which, again, leaves me wondering if it's serious or satire. For posterity, here is the infographic in its entirety:


I'll continue to try to use the term "Microsoft apologist" instead of "Microsoft shill" but it's going to be difficult.

So let's dissect this infographic a piece at a time...

  • Multiple Users
    • Easier and safer to share single device
      • "Safer"? Maybe. "Easier"? Definitely not. The process was painful. Maybe I wasn't doing it right? Kind of like iPhone 4 users weren't holding it right? Ya, if that excuse doesn't work for Apple then it doesn't work for Microsoft either. The principle of least astonishment left much to be desired in the interface here.
    • Same Windows user account experience but in a fun tablet size kids will love
      • I couldn't help but emit an audible chuckle when I read that. For one thing, it sounds like they're marketing a pill. But more to the point, my kids don't love it. At all. This also implies that people inherently "love" the "Windows user account experience" in the first place. I contend that nobody cares. People appreciate the benefits it can provide, but they don't care about the "Windows user account" part of it.
  • Metro core of RT
    • Addresses market that will likely be much more popular than traditional PCs within the next few years.
      • That's a creative way to say, "Our competitors have already enjoyed billions in profit from this market in the past few years, so we think there may be something to it."
    • Isn't aimed to be a PC replacement (i.e. - Incompatible with many desktop applications, partial driver support)
      • You're... not the best salesman... are you? "Incompatible with many desktop applications" is a benefit? No, it's a pain in the ass. One thing I have watched my family do is go to "desktop mode," get excited that it's a full computer, try to install something, and see an error message saying they can't use that. (And to rub salt in the wound, the same error suggests visiting the Windows App Store. Which has, like, 12 apps in the entire store. 4 of which are Angry Birds. It's absurd.) This is not a feature. This is a failure.
  • Mouse
    • When you want to get real work done, nothing beats a keyboard and a mouse.
      • First of all, don't quote yourself in your marketing material. Quote somebody else. Quoting yourself in an attempt to get your own point across is... not awesome.
      • Second, what's with the focus on "getting real work done"? Just a moment ago we were being sold on the idea that this "isn't aimed to be a PC replacement." Now we're being told that it's better because it can replace the PC? This isn't a product, this is an identity crisis. It reminds me of every failed attempt Microsoft has ever made to put a Start Menu on a phone. (And there have been many.) Remember that "market that will likely be much more popular than traditional PCs"? (Also known as that market where Apple has, with a single division of their business, out-profited the entire Microsoft corporate empire.) Trying to shove traditional PCs into that market isn't the way to go.
      • Finally, that last bullet point where he shows a negative is a bit... out of place. "No Mouse and Keyboard Center-based customization software offered just yet." Um... ok. Thanks. I guess the next one will be better?
  • USB Port
    • Connect: External hard drives, printers, keyboards, mice
      • It sounds an awful lot like this is really trying to be a traditional PC. I don't think I've ever wanted to print something from my iPad.
    • Transfer camera files
      • No, just no. This is not my PC. For internet-connected devices, files transfer (or should transfer) fairly seamlessly. (See Photo Stream) For non-connected devices (such as traditional digital cameras), I connect them to my PC. The PC is my central hub. From there they get disseminated to my other devices. (See Photo Stream) I would never even think to plug my digital camera into my iPad. It's a ridiculous notion. Again, and this is all over this friggin' infographic... Are you comparing this device with a PC or with an iPad? Microsoft doesn't seem to understand that there's a difference.
    • Charge phones
      • HA! The battery life is horrendous enough as it is. And now you're going to encourage people to plug powered devices into it? I bet the Surface RT powers down before the phone is even charged. Go ahead and try to plug a USB-powered spinning disk drive (like my old WD Passport, which I love) into one of these. I give it 5 minutes tops.
  • Task Switching aka "Windows Flip"
    • Easily flip between programs with Alt+Tab
      • Average consumers don't use Alt+Tab. They don't know or care about it. I did just discover in testing this one that Alt+Tab does include "metro" apps in the task switching, so that's cool. Point to Microsoft for that small bit of convenience. (It is the little things that make the compelling interface, after all.)
    • Windows 8 and RT also offer Metro-style "Switcher interface" (Win Key+Tab)
      • Ya, but it's only for Metro apps. So they've created a second kind of task switching which behaves almost like the first one, but differently. And they both exist on the same device. That's kind of jarring, don't you think? Oh, and also note that Metro apps don't show up in the traditional task bar, where people expect to see their apps. I guess this "Switcher interface" is the second task bar for a new class of apps. On a technical level I can understand this and it doesn't bother me. As a developer, this makes sense. But consumers will think it's stupid.
    • Apple could close the productivity gap between its iPad and the Surface... by adding one critical missing feature to iOS: Simply allow users to task switch by using Alt+Tab
      • Did you quote yourself again? But I digress...
      • Have you ever even seen an iPad? There is no Alt, and there is no Tab. There is no keyboard. I can't stress this enough, the iPad is not a PC. This identity crisis for the Surface and what it actually is is starting to get old. Besides, the iPad has a task switcher. Swipe to the side with 3 fingers. That feature has been there for a while now.
  • Fully-Functional Microsoft Office
    • Office Home & Student 2013 RT
      • Ok, I actually really like this part. Well, I would if I used Office for anything. (I do for work, but don't use my Surface RT for work because ([clears throat]) it is not a PC. But for people who do want to use Office on a tablet, including it was a pretty cool thing to do.
    • Unlike Office for iPad - will require monthly Office 365 subscription
      • There's an Office for iPad? I guess I didn't notice what with the iOS productivity apps so readily available. Pages, Numbers, and Keynote. Sure, they're "limited-functionality mobile apps" but, you know, they're on a limited functionality mobile device. (Not a PC?) As someone who also has a Mac (sort of a PC?) they play nicely with my setup. Oh, and they don't require a subscription to whatever Office 365 is. So, yes, iPad doesn't run Office. It doesn't purport to. My car also doesn't run Office. Is the Surface better than my car? (Note: My car is also not a PC.)
I don't fault Paul for this. He's in every way a Microsoft guy, and Microsoft is sending very mixed messages with their attempts to break into the non-PC market. The only clear message they seem to be sending is that they truly believe (whether intentionally or through a lack of understanding of the world around them) that the way to move into the non-PC space is to bring PCs there.

So that was enough ranting, and I got a little too emotionally charged on some of those responses. Maybe I'm the one who "doesn't get it"? I don't know. But my family doesn't get it either. Nor does just about anybody else I've met. And by "get it" I mean "buy a Surface or a Windows Phone 8." It's just not something people do.

Again, I love my Surface RT. I just don't see why anybody else would.



NB: My older daughter's birthday is coming up and we've bought her a simple Dell laptop as her very first computer. It will be running Windows 8. I can almost guarantee that the moment she turns it on and sees the Metro start menu, she's going to feel a sense of disappointment and think it's as bad as "the new big phone." I'll try to salvage that. Putting Minecraft on it will be my primary weapon.

Friday, April 26, 2013

The Smartest Guy On The Team

Does your software team have a "wiz kid"? A "rock star"? Have you been lucky enough to find that one amazing developer who seems to be able to solve all of your problems in ways so creative and clever that nobody else on the team can even keep up with his brilliance?

If so, you'd better fix that.

We've all heard the saying before, "What if Rob gets hit by a bus tomorrow?" (Or whatever his name is. I would have used a ____ instead of a name, but then this post would end up with a lot of ____s in it. There's two of them already, and they don't look good. So we'll call him Rob. I don't think I've ever actually worked with a Rob, so nobody should misconstrue this as an actual historical tale.) Well, if Rob is your superstar go-to guy then that bus should scare the hell out of you.

The problem isn't that you'd be devoid of Rob. The real problem is that you currently have Rob. And you let him run amok in your code. (Sure, he writes the code and has a certain level of "ownership" in the sense that team members should take a sense of ownership over their work, but as the business owner you actually own it. It belongs to you. And, ultimately, you are responsible for it.)

I am not suggesting that you shouldn't hire smart people or that all of your software should be farmed out to the lowest bidder in some faraway land. Quite the opposite. What I'm suggesting is that (and this may be difficult to hear) Rob might not be as awesome as he tells you he is. Don't get me wrong, Rob might be a very intelligent guy. But that doesn't mean he writes good code. He might even right very clever, even downright brilliant code. But that doesn't mean it's good code.

This reminds me of one of my favorite quotes:
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." - Brian Kernighan
Writing good code isn't a measure of brilliance or cleverness or creativity. Code is a tool. It's a utility to perform a task necessary to the business. In general, developers love to see clever and creating things. We're kind of geeky, and we really enjoy things like that. So it's often difficult for us to tone it down when writing actual production-quality code, but tone it down we must. Because the bottom line for production-quality code isn't cleverness, it's pragmatism.

But I digress, let's get back to the case with Rob. Consider a scenario...
Manager: Have you finished adding that feature?
Not Rob: It's turning out to be more difficult than expected. The code in that part of the system is really a mess, and the slightest changes are introducing all kind of unexpected effects.
Manager: I don't think that code is a mess. Rob wrote that whole module himself.
Not Rob: Well, regardless of who wrote it, it's a mess. The effort to add that feature is going to also involve fixing a lot of what's already there.
Manager: Nevermind, I'll just have Rob do it. He's better at this sort of thing.
It sounds eerily non-contrived, doesn't it? The characters could take any number of shapes:
  • Manager and Rob are old buddies and they'll be damned if anybody is going to tell them that their buddy did a bad job.
  • Rob helped found the company as the go-to tech guy and everybody just instinctively calls on him because he has a lot of clout.
  • Rob was a highly-paid consultant some time ago and the company never learned how to live without him.
  • Not Rob is young and Manager doesn't believe that anybody young can ever be more right than anybody old.
  • Manager doesn't know Rob, but knows that Rob was a legend when he worked there and Manager would rather bring him in as a consultant than have to deal with the mess himself.
  • etc.
Regardless of how the drama between the characters has unfolded, the situation is the same. If Rob gets hit by a bus tomorrow, Manager is lost without him. Now, I've been putting down Rob quite a bit in this post, and that might not be fair. I'm just reacting (in a somewhat Pavlovian way, perhaps) to the Robs with whom I've worked in the past. Most of them were genuinely useless and drove any codebase they owned into the ground. Some, on the other hand, were genuinely good (even perhaps brilliant in subtle ways) and I hope I learned something from them. Of course, while I like to think I can tell the difference, it's clear that Manager can't.

So, when you have a Rob, what you have is one of two things (or perhaps both):
  • Rob writes terrible code, he just somehow convinces you that it's good. Without your knowledge or consent he is amassing on your behalf a ton of technical debt. And someday you're going to have to pay that debt.
  • Rob is employing coding techniques with which your other team members are unfamiliar or for whatever reason they do not understand. While Rob is, in a purist sense, "doing the right thing" by striving for better code, he is doing so at the cost of supportability.
Or, to put it another way:
  • You need to get rid of Rob as soon as possible.
  • You need Rob to educate and train your other developers.
Again, these two are not entirely mutually exclusive. One of my favorite examples of this sort of thing on a team is dependency injection. For whatever reason, most teams don't use any sort of dependency injection at all. Maybe the design doesn't call for it, but in many cases it really does. But what I've found is that most developers (many of whom have been developers for a very long time) simply don't understand it, beyond the buzz word itself.

So what happens when Rob takes the helm and re-factors everything to invert the dependencies? He may be doing a huge favor to the codebase, but if the rest of the team doesn't understand it then at what cost is that favor? Rob's being a cowboy, and he needs to slow down. He's doing something good, but he needs to do it in the right way. He needs to educate and train the rest of the team.

Of course, we haven't actually seen Rob's code at this point. He might be trying to do the right thing, but he might not be doing it right at all. The best case scenario in this situation is when he educates the team on what he's trying to accomplish and how he's going about it, and another team member chimes in with, "Ohhh, now I see what you're doing... But wouldn't it be better if you did it this other way instead?" Ding ding ding, now we have teamwork and collaboration. (It's nice to dream, isn't it?)

But we are rarely on that team. More often, at least in my experience, we're on the team where Rob is by definition right for no other reason than because he's Rob. And this is a very bad place for a team to be because... What if Rob get hit by a bus tomorrow?

So, I thought of an interesting way to test this for a team. (No, I'm not going to hit Rob with a bus. But you should be aware that I used to live across the street from an MBTA bus driver and I don't think I ever saw that man sober. So, you know, you take your chances.) How can we effectively simulate someone on the team getting hit by a bus?

Surprise vacations!

(Before I explain what I mean by that, let me first state that I recognize that this probably isn't a very good idea for a company policy. But it might just make for a very interesting and revealing company experiment.)

The idea is simple... Any team member who has vacation time available is permitted to take that vacation time any time and is not to notify the team until the day that vacation starts. Were you expecting Rob to come in to work today? Surprise! No Rob for you. So... What do you do now?

If your team dynamics are balanced, you're just somewhat shorthanded for the day. This is inconvenient, but you should be able to handle it. (It's not like you're the only business where this happens. Ever eat at a restaurant that was noticeably shorthanded that evening? Ever eat at one where you didn't notice? Which one had better management?)

If your team dynamics are not balanced, if Rob is truly indispensable for your company, then guess what? You failed, at least for now. But I have good news for you. Rob wasn't hit by a bus. He'll be back shortly. And in the meantime, you get to identify the specifics of where and why Rob is indispensable and come up with a plan to fix it.

Because you have a single point of failure in your system. And you'd better fix that.

Sunday, March 10, 2013

Details Are Important

There's a potential for a runtime error, so I should log it...
This is in a catch block, so I also have the additional context of an exception. So let's add another function argument to use the overload...
:::sigh:::

Thursday, March 7, 2013

The Only Way to Go Fast is to Start Well

As happens from time to time, there's a cross-blog debate stirring in response to something somebody posted. It's not quite as widespread as The Software Craftsmanship Debate from a year (or two? has it been that long) ago, but it's spreading. The catalyst this time was a post by Robert Martin entitled The Start-Up Trap.

The article is somewhat emotionally charged and comes across as being a bit dogmatic, which is Martin's style in general. (I'd be lying if I said I didn't exhibit a similar style.) But looking past the tone one can find the point pretty plainly... Businesses often delude themselves into believing that they don't need to write their software well. They're not "at that stage" or they need to focus their efforts on other things.

(What's worse, many times it's not the business which actually makes this decision. Many times it's made by the developers on behalf of the business without the business actually knowing. But that's another issue for another time. The point there is about professional behavior, and Martin has a whole book on that called The Clean Coder.)

Reacting to the dogmatism, there have been various responses to his post which try to focus more on pragmatism. One such post by Greg Young caught my attention as being one of the more equally-emotionally-charged responses. Another by Jimmy Bogard boils down to a much more level-headed statement, which I think really captured the pragmatism effectively.

And so, to reply to accusations of favoring dogmatism over pragmatism, Martin wrote a response. I feel like this settled the dust a bit, which is good. After all, we're talking about pragmatism. Do what's in the best interests of the business, that is the bottom line. There is no room for dogma on the bottom line. Or, to put it another way:
One developer's desire to be right does not outweigh the business' desire to reduce operational costs.
But in all of the back-and-forth, I think there's a very important issue to which we have only been alluding. And this issue is very much at the core of the communication (or, in many cases, the miscommunication) between all of the advocates on this debate. That is, if the question is:
When does TDD slow you down?
Then the answer is:
When your code isn't designed to be tested.
This, I think, is the great misconception around whether TDD speeds you up or slows you down. Young's article alludes to this when he describes his business venture and the application they prototyped. Martin validated this when he talked about a small one-off utility he wrote for one-time use (where he "fiddled with it" more than "designed it"). Neither of these applications were meant to be tested.

One can argue about the business sense of whether or not an application should be designed to be tested. Young uses an argument which I feel is very often overused, making it difficult to discern when it genuinely applies (which it indeed may have in his case, as I said it's difficult to discern). The argument that "it worked therefore it must have been correct."

Yes, many businesses succeed with crappy software. (Just look at Microsoft. Sorry, couldn't resist.) So, if they succeeded, how could the software have been bad? Evidence (the success) suggests that it was good, right?

This argument is just that, an argument, because to my knowledge there are no real numbers on either side of the equation. There are no double-blind studies, no usable case studies which aren't affected by tons of other variables. Did the company succeed because of the software, or did they succeed through the blood, sweat, and tears of their tireless employees despite the software? Most importantly, could they have been more successful with better software? How do you define and quantify success without anything else to compare it to?

I'm moving into a tangent. Sorry, I do that sometimes. Back to the main point... TDD only works when it's applied appropriately. It's not a magic wand which always makes all software better. This sounds a lot like the pragmatism being proposed by the various responses to Martin's original post. Which makes sense. But it's only part of the message.

Many times, when somebody says that they don't have time for TDD or that it will slow down their project, it's because their code wasn't designed to be tested, which itself is probably the deeper and more important issue here. The developers who make this argument often find themselves faced with the same difficulties when trying to write tests for their code:

  1. The code needs to be re-designed to accommodate the tests.
  2. The tests are slow to develop and not very effective, not providing any real business value in and of themselves.
Well, when faced with these difficulties, of course you're not going to want to write tests. They're making things harder and who wants that?

The problem here isn't that TDD is slowing you down, it's that your code is slowing you down. (Again, I fully recognize that sometimes you just need to fiddle something together until it works, and the various bits of what we'd call "good architecture" aren't necessarily required.) You're decrying TDD because it doesn't work for you, and you're assuming that TDD is to blame for this.

TDD is not just something to put on a checklist. It's not just a line item added to the project plan.
  • Is code written? Check.
  • Are tests written? Check.
  • I guess we're done, then.
No, this is an oversimplification. If the code is not testable then what value are the tests going to provide? You're writing tests (or trying to, anyway), but you're not practicing TDD. So to decry the merits of something you haven't even tried is a bit misguided. (I'm reminded of religious zealots who decry science with no understanding of the scientific method. You know, speaking of dogmatism.)

One does not simply "write tests" to practice TDD. One must also write testable code. Without that, the tests will indeed appear to be a hindrance. This isn't because writing tests is slow or because TDD is all dogmatism with no pragmatism, this is because the code is already rotting. The misdirected attempt to add tests and claim that it's TDD is suffering as a result of that rot.

TDD is not alone in this. Any good practice, when one attempts to apply it as little more than a checklist item to an already rotting codebase, will appear to be a hindrance. After all, it's hard to fix something that's broken. It's especially hard when we can't admit that it's broken or don't understand why it's broken. Of course, if we don't fix it, it will only become more broken.

This is where the communication between the opposing sides of the debate tends to break down.
"Writing tests adds work to the project, it slows us down!"
"No, writing tests speeds you up, you just don't know it!"
In between these two statements is the difference in the codebase itself. And since the code is the one and only real source of truth, we should stop arguing and go look at the code. Any time we have this argument, the best thing we can do is sit down over the code and pair program. Maybe one of you is right for a reason unknown to the other one. Maybe the other one has a point the first one didn't previously see. Discover it in the code.