Dependency Injection as premature optimization

star-trek-injection

Every programming framework out there these days is touting their mad support for dependency injection. I think James Shore is right when he says:

“Dependency Injection is a 25-dollar term for a 5-cent concept… it means giving an object its instance variables. Really. That’s it.”

It often just comes down to a question of how far out you abstract configuration information.

Say you have a method that makes a query to a database. If you had no abstraction at all, it might look something like this:

// 1 Layer
public void QueryDatabase()
{
    SqlConnection sqlConnection = new SqlConnection("mydatabaseserver.net,username=app_user,password=secret");
    // Do something with sqlConnection...
}

Everything is hardcoded right there inline. Simple, but the ultimate in inflexibility.

// 2 Layers
public void QueryDatabase()
{
    SqlConnection sqlConnection = new SqlConnection(Configuration.DatabaseConnectionString);
    // Do something with sqlConnection...
}

public static class Configuration
{
    public static string DatabaseConnectionString = "mydatabaseserver.net,username=app_user,password=secret";
}

This is much better. Now you can have a thousand different database calls in your code, but they all point back to one configuration. So if you move the location of your database, you only have to change one line of code. Have you ever edited the wp-config.php file for WordPress? That is where they store this sort of thing.

// 3 Layers
public void QueryDatabase(sqlConnection)
{
    // Do something with sqlConnection...
}

public static SqlConnection DatabaseConnection
{
    get
    {
        SqlConnection sqlConnection = new SqlConnection();
        if (Configuration.Mode == ApplicationModes.Production)
        {
            if (System.Net.Dns.GetHostName() == ApplicationServers.EastCoastBox)
            {
                sqlConnection.ConnectionString = "eastcoastserver.net,username=app_user,password=secret";
            }
            else
            {
                sqlConnection.ConnectionString = "westcoastserver.net,username=app_user,password=secret";
            }
        }
        else if (Configuration.Mode == ApplicationModes.PreProduction)
        {
            sqlConnection.ConnectionString = "devserver1.net,username=app_user,password=othersecret";
        }
        else if ((Configuration.Mode == ApplicationModes.Testing))
        {
            sqlConnection.ConnectionString = "localhost,username=app_user,password=othersecret";
        }
        return sqlConnection;
    }
}

QueryDatabase(Configuration.DatabaseConnection);

Now when we are calling the query, we can pass it in a database connection of our own design. We even have some logic now in our configuration class that picks the right one for us. OR, we can make up our own on the spot and use that instead if were are working with some new case. That’s pretty handy in a lot of situations, though it does make helper functions more verbose to call.

// 4 Layers
public void QueryDatabase(DataProvider provider)
{
    provider.Query(parameters);
}

DataProvider db1 = new DataProvider(Type.Oracle, Mode.PreProduction);
DataProvider db2 = new DataProvider(Type.MySql, Mode.Testing);
DataProvider db3 = new DataProvider(Type.MongoDB, "my custom connection string");
QueryDatabase(db1);
QueryDatabase(db2);
QueryDatabase(db3);

Alright, so this next example is a bit hokey, but it should work for the sake of illustration. Now we’ve gone so far as to make our application database agnostic. We aren’t tied down to anything anymore! We are super flexible and can substitute all kinds of things now. We have thrown off the chains that bound us and now we have virtually nothing left. We have to “inject” all the meaning, the instance variables, into our method so it can have something to work with.

So what is wrong with all of this? Nothing. The problem comes when you use the wrong one for the job. If you are writing a one-off script that you know you are going to throw in the trash before lunch, then using 1 layer is just fine! Writing a provider and an object factory for it would be a complete waste of time. In the same way, you might really need the flexibility of having many possible connection strings and even database types. Perhaps your project started small but now has grown and needs to accommodate multiple instances of itself. If that is the case, don’t lock yourself down with just 2 layers. Go for lots of dependency injection. It will make your life easier at many corners as you walk down the road.

So why do I say that a lot of dependency injection smells like premature optimization? Because I see many framework tutorials and boilerplates advocating that you do a LOT of 4-layer stuff – even for a 1-page CRUD application with just 2 tables behind it. “Look how we’re using best practices! Our code is so modular and beautiful!” No it’s not. You are violating the YAGNI (You Ain’t Going to Need It) principal. It’s ten times longer than it needs to be and is spread out over 20 files. It’s hard to read and that makes it harder to maintain, not easier. It also makes for baffling tutorial.

The situation is similar for fresh DBAs who try to normalize their tables too much. They make the schema hell to understand at a glance. The same goes for developers having too many layers of abstraction too early on. What should you do instead? Build something that works, then expand on it. Go from brittle to flexible. If you are a master who has done this all twenty times before, then you can probably start with something more in the middle. But a newbie? Forget it. Get all your queries to actually run first and then refactor it. Don’t start by chasing framework bunnies all day. Get your hands dirty with code that does something substantial out of the gate. The best thing about code is you can change it. You don’t have to make it perfect up front and in fact, you shouldn’t. It’s, as Donald Knuth says, the root of all kinds of evil.

2 thoughts on “Dependency Injection as premature optimization

  1. Icefront

    Basically i agree, BUT, i have trouble with one sentence:
    – “It’s ten times longer than it needs to be and is spread out over 20 files. It’s hard to read and that makes it harder to maintain, not easier.” – If your writing something simple, without DI, you’re not going to maintain it, but rewrite it, because all your dependencies are smacked in together.DI makes it easy to maintain code, no matter how small it is, and with practice and experience the overhead of using DI in small projects diminishes to almost nothing.

    DI is a design pattern. Using DI does not mean you have to use some DI container framework, or configure everything dynamically in XML files. What it does mean is that you define a proper level, a good enough INITIAL resolution of dependencies. If done right, It’s easy to evolve, add more dependencies, break down atomic components as the software grows.

    In my opinion, DI is the best practice i ever acquired in Programming (7 years ago), and unless I’m writing some really simple and short 30 liners or so, that I KNOW I won’t be needing them later, I always use DI to some degree.

    Reply
    1. coffeematt Post author

      Hey, thanks for the comment.

      Since writing this a couple of months ago, I’ve had the opportunity to work on a bunch of new projects involving code and frameworks outside of my comfort zone (Angular, Ionic, Cordova, and a database-agnostic C# project similar to the example I used in the post.) What I’ve realized (and this is one reason I was confused about what the big deal with ‘dependency injection’ was all about), is that people use the term to mean some pretty different things.

      When you are working with a monster, all-encompassing library like .Net or Java, where everything is always right at your finger-tips (session state variables, thousands of static helper methods, etc.), some of what people mean by ‘dependency injection’ is hard to grasp. In contrast, Angular controllers (they are a good example) don’t have access to hardly anything unless you ‘inject’ it into the constructor. The scope is always kept as small as possible in a lot of these new JS patterns. And it does encourage you, after time, to really couple things more loosely and swap things in and out. Just today refactor some of my code so I swap out the Android file handler for the iOS one since they take a different approach. So I guess I’m using DI a lot more now. Who knew?

      At the end of the day, I think my real gripe is with example code (in blog posts or on StackOverflow) where what should be a 20-line, single-method tutorial has been turned into a 100-line, six-object exercise in figuring out what the hell is going on. In that case, the coding convention has completely obscured the pedagogy. Tons of DI shouldn’t be used in a short proof-of-concept.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>