Minimize Environmental Dependencies

Minimize Environmental DependenciesHopefully we’re not beating a dead horse here, but as we’ve stated in several other articles, one of your goals with software development should be to minimize environmental dependencies. This goes for everything from the environment required to build your software from source code into deployable binaries, to the requirements of the computers that will run your application.

Many people think that this is just some pie in the sky idea that sounds good on paper, but isn’t worth pursuing in practice. “Just install the the Microsoft ReportViewer Redistributable on every machine that needs to run my app, it’s just one little installation prerequisite, what’s the problem?” That attitude is an incredibly slippery slope that will grant you a free one-way ticket to build & deployment hell if you let it happen.

To help further drive the point home about why you should always strive to minimize environmental dependencies for your applications, here’s a little horror story about some real world pain and suffering caused by not subscribing to this philosophy.

At one point in my career I worked for a firm that had to maintain several legacy versions of their software due to contractual obligations. Developers would occasionally have to make bug fixes to these legacy versions of the code base, and being on the “build team”, I had to produce the deployable artifacts of this legacy software. At the time, there was only one computer left in the whole company that was capable of compiling this legacy software… and it was literally under someone’s desk in the middle of the office. The fate of the legacy versions of this software relied on a little old PC on the floor.

Naturally, being the good build team that we were, my team and I decided to minimize this single-system-risk by cataloging all of the dependencies we could find on this computer that were necessary to build the software and try to reproduce this “build server” on a virtual machine. Try as we might, we could not reproduce “the little workstation that could”. Several of us tried for weeks, pawing through the installed programs, shoveling through mountains of registry keys… we just couldn’t figure out what was so special about this “build server”. We even reached out to one of the (then working remotely) employees that helped create this original build server, but he couldn’t remember what made it special either.

Then, as fate would have it, the build server started having hardware issues. It was reporting hardware errors in the Event Log left and right until one day it just refused to power on. Our sysadmins said it was a motherboard failure. Normally a motherboard failure on a workstation is just a minor inconvenience – just throw it out and swap in a new one. But given this one magical workstation was critical to creating the release versions of this legacy application, this motherboard failure became a Priority 1 Big Problem ™.

So to be able to keep releasing software, this is essentially what we did:

This could be your build server

I literally had to plug in that “build server’s” hard drive into a spare workstation via some external harddrive adapter just to boot off of it just so I could compile the release version of this legacy application.

Obviously this is an extreme case, but hopefully it helps illustrate our point about why you should minimize external dependencies for building and running your software. By doing so, you ensure that your application, a.k.a. your organization’s intellectual property, will be able to be recreated from its source code almost indefinitely.

 
Comments

Can you give some examples of how to limit the dependencies? I know its a stupid question, but can you think of any real-world scenarios?

Hi francois,

No worries, it’s not a stupid question at all. Here’s an example from the development/build perspective:

For development, for a given application you should strive to make the codebase as self contained as possible. By that I mean, for a .NET application for example, you should be able to take a bare-bones Windows machine with nothing more than the appropriate version of the .NET framework installed and a source control client for whatever version control system you use. With that set of minimal tools you should be able to checkout your application’s source code, its 3rd party libraries, and its build system, in a single checkout/pull command. Then it should only take one command to kick off your build system to compile, test, and package your application.

Basically your goal should be to put as much of your application and its dependencies (source code, 3rd party libraries, the build system/scripts, etc) as possible into source control. Sometimes this isn’t possible to do for every single component – especially for some 3rd party libraries that require locally installed licenses for compiling against them, like certain 3rd party UI component libraries – but it’s something you should strive for.

Never give up trying to find a way to keep all of your 3rd party libraries and your build system in source control versioned right along side your application source code. Many times people get frustrated with things (like the Microsoft Report Viewer distribution I referenced above) and simply give up, install some redistributable package, and then have to notify all other developers (and update any sort of new joiner checklist/wiki page) that says something “Don’t forget to install the Report Viewer distribution otherwise you can’t compile our code”. That new joiner checklist for compiling your application should be four simple steps:

1. Install .NET Framework version X
2. Install Subversion client (or whatever your version control system happens to be)
3. Checkout code from some URL
4. Run msbuild foo.proj to compile, test, and package the application

Of course Visual Studio is handy for development as well, but Visual Studio should be an enhancement to your development process, not a requirement for building or testing.

In this case you should try to find the exact assemblies your application code depends on and drop those into some sort of “ThirdParty/lib” directory along side your source code in version control. And then verify that you don’t need that redistributable installed by doing a clean checkout/pull and compile on a new workstation or a VM or some system that doesn’t have the redistributable already installed. Don’t rely on the GAC for your dependencies! 🙂

Sorry, I think I’m starting to ramble now, but hopefully that answers your question. If not, let me know and I’d be happy to give more concrete examples.

Thanks!
Russ

Leave a Reply