97

Let it be known that I am a big fan of dependency injection (DI) and automated testing. I could talk all day about it.

Background

Recently, our team just got this big project that is to built from scratch. It is a strategic application with complex business requirements. Of course, I wanted it to be nice and clean, which for me meant: maintainable and testable. So I wanted to use DI.

Resistance

The problem was in our team, DI is taboo. It has been brought up a few times, but the gods do not approve. But that did not discourage me.

My Move

This may sound weird but third-party libraries are usually not approved by our architect team (think: "thou shalt not speak of Unity, Ninject, NHibernate, Moq or NUnit, lest I cut your finger"). So instead of using an established DI container, I wrote an extremely simple container. It basically wired up all your dependencies on startup, injects any dependencies (constructor/property) and disposed any disposable objects at the end of the web request. It was extremely lightweight and just did what we needed. And then I asked them to review it.

The Response

Well, to make it short. I was met with heavy resistance. The main argument was, "We don't need to add this layer of complexity to an already complex project". Also, "It's not like we will be plugging in different implementations of components". And "We want to keep it simple, if possible just stuff everything into one assembly. DI is an uneeded complexity with no benefit".

Finally, My Question

How would you handle my situation? I am not good in presenting my ideas, and I would like to know how people would present their argument.

Of course, I am assuming that like me, you prefer to use DI. If you don't agree, please do say why so I can see the other side of the coin. It would be really interesting to see the point of view of someone who disagrees.

Update

Thank you for everyone's answers. It really puts things into perspective. It's nice enough to have another set of eyes to give you feedback, fifteen is really awesome! This are really great answers and helped me see the issue from different sides, but I can only choose one answer, so I will just pick the top voted one. Thanks everyone for taking the time to answer.

I have decided that it is probably not the best time to implement DI, and we are not ready for it. Instead, I will concentrate my efforts on making the design testable and attempt to present automated unit testing. I am aware that writing tests is additional overhead and if ever it is decided that the additional overhead is not worth it, personally I would still see it as a win situation since the design is still testable. And if ever testing or DI is a choice in future, the design can easily handle it.

16
  • 64
    Sound like you need to move to a different company... May 23, 2012 at 10:13
  • 24
    Using DI is (at least in Java, C#, ...) almost a natural consequence of using unit tests. Does your company use unit tests? May 23, 2012 at 10:43
  • 27
    Or simply not focalize on DI. DI is different from "maintainable and testable". When there are different opinions on a project, you must compose and sometimes accept you don't decide. I, for instance, have see disasters amplified by inversion of controls, DI, and multiple layers of frameworks making complex things that should be simple (I can't argue in your case). May 23, 2012 at 10:43
  • 34
    "...I could talk all day about it." Sounds like you need to stop obsessing over something that is abused more often than not because of dogmatic adherence to some white paper of the day.
    – user7519
    May 23, 2012 at 19:04
  • 21
    From the sound of it, your coworkers have objective arguments that speak against the use of DI containers: “we don't need to add this layer of complexity to an already complex project” – are they maybe right? Do you in fact benefit from the added complexity? If so, that should be possible to argue. They claim that “DI is an unneeded complexity with no benefit.” If you can show an objective benefit then you should win this quite easily, no? Testing is mentioned quite often in the other comments; but testing does not fundamentally need DI, unless you need mocks (which isn’t a given). May 23, 2012 at 20:35

18 Answers 18

62

Taking a couple of the counter arguments:

"We want to keep it simple, if possible just stuff everything into one assembly. DI is an uneeded complexity with no benefit".

"its not like we will be plugging in different implementations of components".

What you want is for the system to be testable. To be easily testable you need to be looking at mocking the various layers of the project (database, communications etc.) and in this case you will be plugging in different implementations of components.

Sell DI on the testing benefits it gives you. If the project is complex then you're going to need good, solid unit tests.

Another benefit is that, as you are coding to interfaces, if you come up with a better implementation (faster, less memory hungry, whatever) of one of your components, using DI makes it a lot easier to swap out the old implementation for the new.

What I'm saying here is that you need to address the benefits that DI brings rather than arguing for DI for the sake of DI. By getting people to agree to the statement:

"We need X, Y and Z"

You then shift the problem. You need to make sure that DI is the answer to this statement. By doing so you co-workers will own the solution rather than feeling that it's been imposed on them.

8
  • +1. Brief and to the point. Unfortunately, from the arguments put forward by Mel's coworkers, he will have a hard time convincing them that it's worth to even just try - what Spoike said in his answer, more of a people problem than technical issue. May 23, 2012 at 10:46
  • 1
    @Trustme-I'maDoctor - Oh I agree that it's a people problem, but with this approach you are showing the benefits rather than arguing for DI for it's own sake.
    – ChrisF
    May 23, 2012 at 10:56
  • True and I wish him luck, his life as a developer will be easier with proper testability etc. :) May 23, 2012 at 11:02
  • 4
    +1 I think your last paragraph should be the first. That is central to the problem. If being able to Unit Test was being pushed and converting the whole system over to a DI model was suggested, I'd say no to him also. May 23, 2012 at 11:16
  • +1 very helpful indeed. I wouldn't say DI is an absolute must for unit testing but it does make things a hell of a lot easier.
    – Mel
    May 23, 2012 at 12:41
56

From what you are writing, not DI itself is taboo, but the use of DI containers, which is a different thing. I guess you will get no problems with your team when you just write independent components which get other components they need passed, for example, by the constructor. Just don't call it "DI".

You may think for such components not to use a DI container is tedious, and you are right, but give your team the time to find that out for themselves.

3
  • 10
    +1 a pragmatic workaround to a political / dogmatic deadlock
    – k3b
    May 23, 2012 at 12:15
  • 32
    It's worth considering wiring up all your objects manually anyway, and introduce a DI container only when that starts to get hairy. If you do this, they won't see DI containers as added complexity - they'll see it as adding simplicity.
    – Phil
    May 23, 2012 at 13:49
  • 2
    The only problem is that the pattern of passing loosely-coupled dependencies into dependents is DI. IoC, or the use of a "container" to resolve loosely-coupled dependencies, is automated DI.
    – KeithS
    May 24, 2012 at 21:07
52

Since you asked, I'll take the side of your team against DI.

The case against Dependency Injection

There is a rule I call "Conservation of Complexity" that is analogous to the rule in physics called "Conservation of Energy." It states that no amount of engineering can reduce the total complexity of an optimal solution to a problem, all it can do is shift that complexity around. Apple's products are not less complex than Microsoft's, they simply move complexity from the user space into the engineering space. (It is a flawed analogy, though. While you cannot create energy, engineers have a nasty habit of creating complexity at every turn. In fact, a lot of programmers seem to enjoy adding complexity and using magic, which is by definition complexity that the programmer does not fully understand. This excess complexity can be reduced.) Usually what looks like reducing complexity is simply shifting complexity somewhere else, usually to a library.

Similarly, DI does not reduce the complexity of linking up client objects with server objects. What it does is take it out of Java and move it into XML. That's good in that it centralizes everything into one (or a few) XML file(s) where it is easy to see everything that is going on and where it is possible to change things without recompiling the code. On the other hand, that's bad because the XML files are not Java and therefore are not available for Java tooling. You cannot debug them in the debugger, you cannot use static analysis to trace through their call hierarchy, and so on. In particular, bugs in the DI framework become extremely difficult to detect, identify, and fix.

It is therefore much harder to prove a program to be correct when using DI. Lots of people argue that programs using DI are easier to test, but program testing can never prove the absence of bugs. (Note that the linked paper is around 40 years old and so has some arcane references and cites some out-of-date practices, but many of the basic statements still hold true. In particular, that P <= p^n, where P is the probability that the system is free of bugs, p is the probability that a system component is free of bugs, and n is the number of components. Of course, this equation is oversimplified, but it points to an important truth.) The NASDAQ crash during the Facebook IPO is a recent example, where they claimed they had spent 1,000 hours testing the code and still did not uncover the bugs that caused the crash. 1,000 hours is half a person-year, so it's not like they were skimping on the testing.

True, hardly anyone ever undertakes (let alone completes) the task of formally proving any code is correct, but even weak attempts at proof beat out most testing regimes for finding bugs. Strong attempts at proof, for example L4.verified, invariably uncover a large quantity of bugs that testing did not and probably would never uncover unless the tester already knew the exact nature of the bug. Anything that makes the code harder to verify by inspection can be seen as reducing the chance that bugs will be detected, even if it increases the ability to test.

Furthermore DI is not required for testing. I rarely use it for that purpose, as I prefer to test the system against the real software in the system rather than some alternative testing version. Everything I change in test is (or might as well be) stored in properties files that direct the program to resources in the test environment rather than production. I'd much rather spend time setting up a test database server with a copy of the production database than spend time coding up a mock database, because this way I will be able to track problems with production data (character encoding issues are just one example) and system integration bugs as well as bugs in the rest of the code.

Dependency Injection is also not required for Dependency Inversion. You can easily use static factory methods to implement Dependency Inversion. That is, in fact, how it was done in the 1990's.

In truth, although I've been using DI for a decade, the only advantages it provides that I find persuasive are the ability to configure third party libraries to use other third party libraries (e.g. set up Spring to use Hibernate and both to use Log4J) and the enabling of IoC via proxies. If you're not using third party libraries and not using IoC, then there's not much point to it and it does indeed add unwarranted complexity.

9
  • 1
    I disagree, it's possible to reduce complexity. I know because I've done it. Of course, some problems/requirements force complexity, but above that most of it can be removed with effort. May 23, 2012 at 19:00
  • 11
    @Ricky, it's a subtle distinction. Like I said, engineers can add complexity, and that complexity can be taken away, so you may have reduced the complexity of the implementation, but by definition you cannot reduce the complexity of the of the optimal solution to a problem. Most often what looks like reducing complexity is just shifting it somewhere else (usually to a library). In any case it is secondary to my main point which is that DI does not reduce complexity.
    – Old Pro
    May 23, 2012 at 19:07
  • 2
    @Lee, configuring DI in code is even less useful. One of the most widely accepted benefits of DI is the ability to change the service implementation without recompiling the code, and if you configure DI in code then you lose that.
    – Old Pro
    May 23, 2012 at 20:58
  • 8
    @OldPro - Configuring in code has the benefit of type-safety, and the majority of dependencies are static and have no benefit from being defined externally.
    – Lee
    May 23, 2012 at 21:11
  • 4
    @Lee If the dependencies are static, why aren’t they then hard-coded in the first place, without the detour via DI? May 24, 2012 at 12:54
25

I'm sorry to say that the problem you have, given what your coworkers have said in your question, is in political nature rather than technical. There are two mantras that you should keep in mind:

  • "It is always a people problem"
  • "Change is scary"

Sure, you can bring up a lot of counter arguments with solid technical reasons and benefits, but that will put you in a bad situation with the rest of the company. They have a stance already that they don't want it (because they're comfortable with how things are working now). So it might even hurt your relationship with your fellow colleagues if you keep being persistent about it. Trust me on this because it has happened to me and eventually got me fired several years ago (young and naive that I was); this is a situation you don't want to get yourself into.

The thing is, you need to attain some level of trust before people will go so far as to change their current way of working. Unless you are in a position where you have lots of trust or you can decide these kinds of things, such as having the role of an architect or tech lead, there is very little you can do to turn your colleagues around. It will take a lot of time with persuasion and effort (as in taking small improving steps) to gain trust in order to get your company culture to turn around. We're talking about a timespan of many months or years.

If you find that the current company culture is not working with you then at least you know one other thing to ask on your next job interview. ;-)

24

Don't use DI. Forget it.

Create an implementation of the GoF factory pattern that "creates" or "finds" your particular dependencies and passes them in to your object and leave it at that, but don't call it DI and don't create a complex generalised framework. Keep it simple and relevant. Ensure the dependencies and your classes are such that a switch to DI is possible; but keep the factory the master and keep it extremely limited in scope.

In time, you can hope that it will grow and even one day transition to DI. Let the leads arrive at the conclusion on their own time and do not push. Even if you never get to that, at least your code has some of the advantages of DI, for example, unit testable code.

4
  • 3
    +1 for growing along with the project and "don't call it DI" May 23, 2012 at 13:52
  • 5
    I agree, but in the end, it is DI. It's just not using a DI container; it's passing the burden to the caller rather than centralizing it in one place. However, it would be smart to call it "Externalizing Dependencies" instead of DI. May 23, 2012 at 18:06
  • 5
    It's often occurred to me that to really grok an ivory-tower concept like DI, you have to have gone through the same process as the evangelists did to realise that there was a problem to solve in the first place. Authors often forget this. Stick with low-level pattern language and the need for a container may (or may not) emerge as a consequence.
    – Tom W
    May 29, 2012 at 10:52
  • @JuanMendes from their response I'm thinking that externalizing dependencies is specifically what they're opposed to because they don't like third party libraries either and want everything in a single assembly (monolith?). If that's true then calling it "Externalizing Dependencies" is no better than calling D.I. from their vantage point.
    – Coder Guy
    Apr 10, 2015 at 19:01
22

I have seen projects that took DI to an extreme in order to unit test and mock objects. So much so that the DI configuration became the programming language in itself with as much configuration needed to get a system running as actual code.

Is the system suffering right now because of a lack of appropriate internal API's that cannot be tested? Are you hoping that switching the system over to a DI model would allow you to unit test better? You don't need a container in order to unit test your code. Using a DI model would allow you to unit test it easier with Mock objects, but it is not required.

You don't need to switch the whole system over to be DI compatible at once. You shouldn't be arbitrarily writing Unit tests either. Refactor code as you come across it in your feature and bug working tickets. Then ensure that the code you've touched is easily testable.

Think about CodeRot as a disease. You don't want to start arbitrarily hacking away at things. You have a patient, you see a sore spot, so you start fixing the spot and the things around it. Once that area is clean you move on to the next. Sooner or later you'll have touched the majority of the code and it didn't require this "massive" architectural change.

I do not like that DI and Mock testing are mutually exclusive. And after seeing a project that went, for lack of a better word, crazy using DI, I'd be weary also without seeing the benefit.

There are some places that makes sense to use DI:

  • The Data Access Layer to swap out strategies for storying data
  • Authentication and Authorization Layer.
  • Themes for the User Interface
  • Services
  • Plugin extension points that your own system or third-party can use.

Requiring each developer to know where the DI configuration file is (I realize containers can be instantiated through code, but why would you do that.) when they want to perform a RegEx is frustrating. Oh I see there is IRegExCompiler, DefaultRegExCompiler and SuperAwesomeRegExCompiler. Yet no where in the code can I do analysis to see where Default is used and SuperAwesome is used.

My own personality would react better if someone showed me something that was better, then trying to convince me with their words. There is something to be said about someone that would put in time and effort to make the system better. I don't find that many developers care enough to make the product truly better. If you could go to them with real changes in the system and show them how it made it better and easier for you, that is worth more than any online research.

In summary, the best way to effect change into a system is to slowly make the code you are currently touching better with each passing. Sooner or later you'll of been able to transform the system into something better.

3
  • "DI and Mock testing are mutually exclusive" That is false, they are not mutually inclusive either. They just work well together. You also list the places DI is good, service layer, dao and user interface - that's almost everywhere isn't it ? May 23, 2012 at 11:28
  • @Andrew valid points. Of course, i wouldn't want it to go too far. I wanted the automated testing for the business classes the most, as we have to implement very complex business rules. And I do realize I can still test them without container, but personally, it just makes sense to use a container. I actually did a little sample that showed the concept. But it wasn't even looked at. I guess my mistake was I didn't create a test that showed how easy testing/mocking is.
    – Mel
    May 23, 2012 at 12:39
  • So much agreement! I really really hate DI configuration files and the effect it has on tracking program flow. The entire thing turns into "spooky action at a distance" with no apparent connections between pieces. When it becomes easier to read code in a debugger than in source files, something is truly wrong.
    – Zan Lynx
    May 24, 2012 at 18:03
19

DI does not require invasive Inversion of Control containers or mocking frameworks. You can decouple the code and allow DI via a simple design. You can do unit testing via the built in Visual Studio capabilities.

To be fair, your coworkers have a point. Using those libraries badly (and since they're unfamiliar with them, there will be learning pains) will cause problems. In the end, code matters. Don't screw up your product for your tests.

Just remember that compromise is necessary in these scenarios.

2
  • 2
    I agree DI does not require IoC containers. although I wouldn't call it invasive (this implementation at least) since the code was designed to be container agnostic (mostly constructor injections) and all the DI stuff happens in one place. So it still all works without a DI container - I put in parameterless constructors that "injects" the concrete implementations to the other constructor with dependencies. That way I can still easily mock it. So yeah I agree, DI is actually achieved by design.
    – Mel
    May 23, 2012 at 12:47
  • +1 For compromise. If nobody wants to compromise, everyone ends up suffering.
    – Tjaart
    May 25, 2012 at 10:34
14

I don't think that you can sell DI any more, because this is a dogmatic decision (or, as @Spoike called it more politely, political).

In this Situation arguing with widely accepted best practices like SOLID design principles is not possible any more.

Somebody who has more political power than you has made a (maybe wrong) decision to not use DI.

You on the other side have opposed this decision by implementing you own container because using an established container was forbidden. This policy of faits accomplis that you tried might have made the situation more worse.

Analysis

In my opinion, DI without testdriven development (TDD) and unit testing has no significant benefit that outweighs the initial extra costs.

Is this issue really about

  • we do not want DI?

or is this more about

  • tdd/unittesting is a waste of resources?

[Update]

If you see your project from the classic mistakes in project management view, you see

#12: Politics placed over substance.
#21: Inadequate design. 
#22: Shortchanged quality assurance.
#27: Code-like-hell programming.

while the others see

#3: Uncontrolled problem employees. 
#30: Developer gold-plating. 
#32: Research-oriented development. 
#34: Overestimated savings from new tools or methods. 
1
  • we are currently not doing any unit testing in our team. I suspect that your last statement is spot on. I have brought up unit testing a few times but nobody showed any real interest in it and there were always reasons why we cant adopt it.
    – Mel
    May 23, 2012 at 12:26
10

If you find yourself having to "sell" a principle to your team, then you're probably already several miles down the wrong path.

Nobody wants to do extra work, so if what you're trying to sell them looks to them like extra work, then you're not going to convince them by Repeated Invocation of Superior Argument. They need to know that you're showing them a way to reduce their workload, not increase it.

DI is often seen as extra complexity now with the promise of potential reduced complexity later, which has value, but not so much to someone who's trying to reduce their upfront workload. And in many cases, DI is just another layer of YAGNI. And the cost of this complexity is not zero, it's actually pretty high for a team that isn't used to this type of pattern. And that's real dollars being shoveled into a bucket that may never yield any benefit.

Put it in its place
Your best bet is to use DI where's it's glaringly useful, rather than just where it's common. For example, many apps use DI to select and configure databases. And if your app ships with a database layer then that's a tempting place to put it. But if your app ships with a built-in user-invisible database which is otherwise tightly integrated into the code, then the chances of needing to replace the DB at runtime approach zero. And selling DI by saying, "look, we can easily do this thing that we will never, ever, ever want to do," is likely to get you on the "this guy has bad ideas" list with your boss.

But say instead you have debugging output that normally gets IFDEF'ed out in release. And every time a user has a problem, your support team has to send them a debug build just so that they can get the log output. You can use DI here to allow the customer to switch between log drivers -- LogConsole for developers, LogNull for customers, and LogNetwork for customers with problems. One unified build, runtime configurable.

Then you don't even have to call it DI, instead it's just called "Mel's really good idea" and you're now on the good-ideas list instead of the bad one. People will see how well the pattern works, and start using it where it makes sense in their code.

When you properly sell an idea, people won't know that you convinced them of anything.

8

Of course, Inversion of control (IoC) has a great number of benefits: it simplifies the code, adds some magic that is doing the typical tasks, etc.

However:

  1. It adds a lot of dependencies. Simple hello world application written with Spring can weight a dozen of MB, and I've seen Spring added only to avoid a few lines of constructor and setters.

  2. It adds the abovementioned magic, which is hard to understand for the outsiders (for example, GUI developers that needs to make some changes in business layer). See the great article Innovative Minds Don’t Think Alike - New York Times, which describes how this effect is underestimated by the experts.

  3. It hardens to trace the usage of classes. IDEs like Eclipse has great abilities to track usages of given classes or method invocations. But this trace is lost when there's dependency injection and reflection invoked.

  4. The IoC usage normally doesn't end with injecting dependencies, it ends with countless proxies, and you get stack trace counting hundreds of lines, 90% of which are proxies and invokes via reflection. It greatly complicates stack trace analysing.

So, myself being a great fan of Spring and IoC, I've recently had to rethink the questions above. Some of my collegues are web developers taken inside the Java world from the Web world ruled by Python and PHP and the obvious for javaist things are anything than obvious for them. Some of my other collegues are doing tricks (of course undocumented) which makes the error very difficult to find. Of course IoC misusage makes things lighter for them ;)

My advice, if you'll force usage of IoC in your job, you'll be made responsible for any misusage anyone else will make ;)

1
  • +1 as with every powerful tool, it is easily misused and you MUST understand when NOT to use it.
    – Phil
    May 23, 2012 at 13:52
4

I think that ChrisF is into something right. Don't sell DI in itself. But sell the idea about unit testing the code.

One approach to get a DI container into the architecture could be to then slowly let the tests drive the implementation into something that sits well with that architecture. E.g. lets say you are working on a controller in an ASP.NET MVC application. Say you write the class like this:

public class UserController {
    public UserController() : this (new UserRepository()) {}

    public UserController(IUserRepository userRepository) {
        ...
    }

    ...
}

That allows you to write unit tests decoupled from the actual implementation of the user repository. But the class still works without a DI container to create it for you. The parameterless constructor will create it with the default repository.

If you coworkers can see that this leads to much cleaner tests, maybe they can realize that is a better way. Maybe you can get them to start coding like that.

Once they have realized that this is a better design than the UserController knowing about a specific UserRepository implementation, you can take the next move, and show them that you can have a component, the DI container, that can automatically provide the instances you require.

4
  • Great! I am currently doing this because i really want the automated unit testing in, even if I cant get DI. I forgot to mention that unit tests are also not done in our team :( so if I will be the first one.
    – Mel
    May 23, 2012 at 11:08
  • 2
    In that case, I'd say that's the real problem here. Find the root of the resistance to unit testing, and attack that. Let DI lie for now. Testing is more important IMHO. May 23, 2012 at 11:42
  • @ChristianHorsdal I agree, I wanted it to be loosely coupled to allow easy mocking/testing.
    – Mel
    May 23, 2012 at 12:30
  • That idea is really cool... Great sell for testing May 25, 2012 at 12:46
2

Perhaps the best thing to do is step back and ask yourself why you want to introduce DI containers? If it's to improve testability then you your first task is to get the team to buy into unit testing. Personally I'd be much more concerned about the lack of unit tests than the lack of a DI container.

Armed with a comprehensive suite or suites of unit tests, you can confidently set about evolving your design over time. Without them, you face an increasingly fraught struggle to keep your design in-step with evolving requirements, a struggle that eventually many teams lose.

So my advice would be to first champion unit testing and refactoring then, in turn introduce DI and finally DI containers.

2

I am hearing some big no-nos from your team here:

  • "No third-party libraries" - AKA "Not Invented Here" or "NIH". The fear is that, by implementing a third-party library and thus making the codebase dependent on it, should the library have a bug, or a security hole, or even just a limitation that you need to overcome, you become dependent on this third party to fix their library in order for your code to work. In the meantime, because its "your" program that failed spectacularly, or was hacked, or doesn't do what they asked for, you still carry the liability (and the third-party devs will say that their tool is "as-is", use at your own risk).

    The counter-argument is that the code that solves the problem already exists in the third-party library. Are you building your computers from scratch? Did you write Visual Studio? Are you eschewing the built-in libraries in .NET? No. You have a level of trust in Intel/AMD and Microsoft, and they find bugs all the time (and don't always fix them quickly). Adding a third-party library allows you to quickly get a maintainable codebase working, without having to reinvent the wheel. And Microsoft's army of lawyers will laugh in your face when you tell them that a bug in the .NET cryptography libraries makes them liable for the incident of hacking your client suffered when using your .NET program. While Microsoft will certainly jump to fix "zero-hour" security holes in any of its products, they make it very clear that they are only liable for very specific damages in very specific cases.

  • "We want to stuff everything into one assembly" - actually, you don't. In .NET at least, if you make a change to one line of code the entire assembly containing that line of code must be rebuilt. Good code design is based on multiple assemblies that you can rebuild as independently as possible (or at least only have to rebuild dependencies, not dependents). If they REALLY want to release an EXE that is just the EXE (which does have value in certain circumstances), there's an app for that; ILMerge.

  • "DI is taboo" - WTF? DI is an accepted best practice in any O/O language with an "interface" code construct. How does your team adhere to the GRASP or SOLID design methodologies if they're not loosely-coupling the dependencies between objects? If they don't bother, then they're perpetuating the spaghetti factory that makes the product the complicated morass it is. Now, DI does increase complexity by increasing object count, and while it makes substituting a different implementation easier, it makes changing the interface itself harder (by adding an extra layer of code object that has to change).

  • "We don't need to add this layer of complexity to an already complex project" - They may have a point. A crucial thing to consider in any code development is YAGNI; "You Ain't Gonna Need It". A design that is SOLIDly engineered from the beginning is a design that is made SOLID "on faith"; you don't know if you'll need the ease of change that SOLID designs provide, but you have a hunch. While you may be right, you may also be very wrong; a very SOLID design could end up being viewed as "lasagna code" or "ravioli code", having so many layers or bite-size chunks that making seemingly trivial changes become difficult because you have to dig through so many layers and/or trace through such a convoluted call stack.

    I support a three-strikes rule: Make it work, make it clean, make it SOLID. When you first write a line of code it just has to work, and you don't get points for ivory-tower designs. Assume it's a one-off and do what you gotta do. The second time you look at that code, you're probably looking to expand or reuse it, and it's not the one-off you thought it'd be. At this point, make it more elegant and understandable by refactoring it; simplify convoluted logic, extract repeated code into loops and/or method calls, add a few comments here and there for any kludge you have to leave in place, etc. The third time you look at that code it's probably kind of a big deal. Now you should adhere to the SOLID rules; inject dependencies instead of constructing them, extract classes to hold methods that are less cohesive with the "meat" of the code, make sure class hierarchies are logical and that derived classes "look" like their parents if you were to substitute one for the other, etc.

  • "It's not like we'll be plugging in different implementations" - You, sir, have a team that doesn't believe in unit tests on your hands. I posit that it is impossible to write a unit test, which runs in isolation and has no side effects, without being able to "mock" the objects that have those side effects. Any non-trivial program has side effects; if your program reads or writes to a DB, opens or saves files, communicates over a network or peripheral socket, launches another program, draws windows, outputs to the console, or otherwise uses memory or resources outside its process's "sandbox", it has a side effect. If it does none of these things, what DOES it do, and why should I buy it?

    To be fair, unit testing is not the only way to prove a program works; you can write integration tests, code to mathematically-proven algorithms, etc. However, unit testing does show, very quickly, that given inputs A, B and C, this piece of code outputs the expected X (or not), and thus that the code works properly (or doesn't) in a given case. Suites of unit tests can demonstrate with a high degree of confidence that the code works as expected in all cases, and they also provide something that a mathematical proof cannot; a demonstration that the program STILL works the way it was intended. A mathematical proof of the algorithm doesn't prove it was implemented correctly, nor does it prove any changes made were implemented correctly and didn't break it.

The short of it is that, if this team doesn't see any value in what DI would do, then they won't support it, and everybody (at least all the senior guys) has to buy into something in order for real change to happen. They're putting a lot of emphasis on YAGNI. You're putting a lot of emphasis on SOLID and automated testing. Somewhere in the middle lies the truth.

1

Be careful adding extra features (like DI) to a project when not approved. If you have a project plan with time limits you could fall into a trap of not having enough time to finish the approved features.

Even if you don't use DI now, get involved in the testing so you know exactly what the expectations for testing at your company are. There will be another project and you will be able to contrast the issues with testing of the current project vs. DI.

If you don't use DI, you can get creative and make your code DI-"ready". Use a parameterless constructor to call other constructors:

MyClassConstructor()
{
    MyClassConstructor(new Dependency)
    Property = New Dependent Property
}

MyClassConstructor(Dependency)
{
    _Dependency = Dependency
}
0

I think one of their biggest concern is in fact just the opposite: DI is not making the project complex. In most case it is reducing lots of complexity.

Just think without DI, what are you going to get the dependent object/component you need? Normally it is by lookup from a repository, or getting a singleton, or instantiating yourself.

The former 2 involves extra code in locating the dependent component, in DI world, you didn't do anything to perform the lookup. The complexity of your components is in fact reduced.

If you are instantiating yourself in some cases that is not appropriate, it is even creating more complexity. You need to know how to create the object correctly, you need to know what values to initialize the object to a workable state, all these create extra complexity to your code.

So, DI is not making the project complex, it is making it simpler, by making components simpler.

Another big argument is, you are not going to plug different implementation. Yes, it is true that in production code, it is not common to have many different implementation in real production code. However there is one major place that need different implementation: Mock/Stub/Test Doubles in Unit Tests. To make DI works, in most case it relies on interface-driven development model, in turns it makes mocking/stubbing in unit tests possible. Apart from replacing implementation, "interface-oriented design" also makes cleaner and better design. Of course you can still design with interface without DI. However, Unit tests and DI is pushing you to adopt such practices.

Unless your "gods" in company do think that: "simpler code", "unit tests", "modularization", "single responsibility", etc are all something they against, there should be no reason for them to reject using DI.

3
  • That's nice in theory, but in practice adding a DI container IS additional complexity. The second part of your argument shows why it's worth the work, but it's still work.
    – schlingel
    May 23, 2012 at 12:37
  • In fact from my past experiences, DI is REDUCING instead of an addition to complexity. Yes it have some extra added to the system, which contributes to complexity. However with DI, complexity of other components in the system is greatly reduced. At the end, complexity is in fact reduced. For the second part, unless they are not doing any unit tests, it is NOT extra works. Without DI, their unit tests mostly will be difficult to write and maintain. With DI, the effort is greatly reduced. So, it is in fact REDUCE of work (unless they are not and do not trust unit tests) May 24, 2012 at 1:25
  • I should emphasize one thing: DI in my response do not means any existing DI framework. It is just the methodology in designing/developing components in your app. You can benefit a lot if your code is interface-driven, your components expects dependencies to be injected instead of looked-up/created. With such design, even you are writing some application specific "loader" which do the wiring of components (no general purpose DI fw), you are still benefiting from what I mentioned: reduce of complexity, and reduce of work in unit testing May 24, 2012 at 1:29
0

"We want to keep it simple, if possible just stuff everything into one assembly. DI is an uneeded complexity with no benefit".

The benefit is its promotes designing to interfaces and allows easy mocking. Which consequently facilitates unit testing. Unit testing ensures reliable, modular and easily maintainable code. If your boss still maintains its a bad idea, well there is nothing you can do - its accepted industry best practice that they are arguing against.

Get them to try writing a unit test with a DI framework and mocking library, they will soon learn to love it.

3
  • Literally doubling or tripling the number of classes in your system is now what I find useful. This is similar to the debate of trying to get 100% Unit Test coverage by writing a single test per method, or writing meaningful Unit Tests. May 23, 2012 at 11:20
  • I don't understand, you think unit testing is a good idea ? If you do, you also mock objects too ? This is easier with interfaces and DI. But that seems to be what you are arguing against. May 23, 2012 at 11:27
  • It is really the chicken and the egg problem. Interfaces/DI and unit testing is so closely relevant to each other it is tough to do one without the other. But I do believe that unit testing is a better and easier avenue to start with on a code base that exists. Gradually refactoring old kludgy code into cohesive components. After that, you could argue that object creation should be done with an DI framework instead of newing everywhere and writing factory classes since you have all these components in place.
    – Spoike
    May 23, 2012 at 11:54
0

Do you work with these people on any smaller projects, or just one big one? It's easier to convince people to try something new on a secondary/tertiary project than the team/companies bread and butter. The main reasons are that if it fails the cost of removing/replacing it is much smaller, and it's much less work to get it everywhere in a small project. In a large existing one you either have a large upfront cost to make the change everywhere at once, or an extended period in which the code is a mixture of the old/new approach adding friction because you have to remember which way each class is designed to work.

0

Although one of the things that promote DI is unit testing, but I believe that it adds layer of complexity in some cases.

  • Better To have a car hard in Testing, but easy in Repair than car easy in testing and Hard in repair.

  • Also it seems to me that the group pushing for DI are only presenting their ideas through influencing that some existing designs approaches are bad.

  • if we have a method doing 5 different functions

    DI-People Says:

    • no separation of concerns. [ what is the problem of that? they are trying to make it look bad, which in logic and programming it is much better to know what each other is doing closely to better avoid conflicts and wrong-expectations and to see the impact of code change easily, because we cannot make all your objects not related to each other 100% or at least we cannot guarantee this {unless why regression testing is important?} ]
    • Complex [They want to reduce complexity by making additional five more classes each to handle one function and additional class(Container) to create the class for needed function now based on settings (XML or Dictionary) in addition to philosophy "to have a default/or not" discussions, ... ] then they moved the complexity to the container and to the structure , [ to me looks like moving complexity from one place to two places with all complexities of language specifics to handle this. ]

I believe that it is better to create our own design pattern that suits the business needs rather than to be influenced by DI.

in favor of DI, it might help in some situations, for example if you have hundreds of implementations for the same Interface depends on a selector and these implementations are far different in logic, in this case I would go with DI or DI similar techniques. but if we have few implementations for the same interface we don't need DI.

Not the answer you're looking for? Browse other questions tagged or ask your own question.