Dependency InjectionApplicability, Benefits and Mocking
Jacob Proffitt continues the DI discussion, and he opens with this:
Ayende Rahien responded individually to each of my posts himself, which is more than a little bit intimidating all on its own
Despite rumors to the contrary, I want to mention that I abhor violence in all its forms. It always leads to having to fill forms in triplicate, and I got sick at that when they pulled form 34-C12 from the archives, just for me. Someone should remind me (on a non durable medium only) to demonstrate how you can get whatever you want, explicitly without violence in any form. It is both an interesting story, and it works.
Now, to Jacob's post, I will usually respond to things that I find interesting and rational, even if (or especially) if I do not agree.
All the heavyweight advocates of DI rely on powerful, relatively invasive tools to realize the benefits of Dependency Injection. Until you get those tools, the benefits of DI seem mainly theoretical.
Actually.... *drums roll* no.
Using DI with a container will certainly give you quite a few benefits, but you can get quite a bit from using DI directly. I met a guy in DevTeach which had done just that, with great success, using anonymous delegates and functional style. Nothing beyond a coding convention and what the language offered.
As an aside, I would accept the powerful, but I reject the invasive tools part. An good IoC can and should be invisible to anything but the Main() method. (There are shortcuts that are easier to take if you are explicitly aware of the container, but it is most often used on naked CLR objects).
The problem with DI, which is what the container want to solve, is that the moment you start injecting dependencies, you want some way to manage that, you can do that with a factory or a provider, but you get more benefits from using a container, since this allows you to centrally leverage some interesting concepts across the entire application.
If my client or company needs web pages that validate users against Active Directory and then allows them access to reports based on their group memberships, then telling me that I need some robust loose coupling framework in order to test my user maintenance library is flat out wrong.
Hm, let us see...
public interface IAuthorozationService { bool Authenticate(string username, string password); bool AllowedTo(string action); }
Simple, isn't it? That is what we used on our last project, and that is what enabled us to switch the implementation from hard coded (used on the staging site) to database back-end (staging site, when doing it hard coded started to take too long) and to active directory (production).
If it takes you more than a minute to write that, there is an issue here. If you consider direct coupling to the Active Directory classes, then there is an issue. If you tell me that you can get he same thing from:
public static class AuthorizationProvider { public static bool Authenticate(string username, string password) { //... } public static bool AllowedTo(string action) { //... } }
Then I argue that there really isn't any difference between the two, except that the interface based approach makes your collaborators easier to understand and work with. The second approach is a static Service Locator, which hides the dependency, but can do much the same as an IoC, but is more limited in terms of handling such things as life cycle.
Outside of major corporate infrastructure projects, stringent loose coupling just isn’t as much benefit as more naturally encapsulated OO techniques.
I strongly disagree, I find loose coupling to be an extremely useful idiom, important even in very small projects. The ability to look at a piece of code in isolation is a key factor for maintainability.
So here we have a lot of architecture big wigs selling DI as best practices and not bothering to contextualize those statements at all. Again, Ayende is a good example of this. In Dependency Injection: More than a testing seam, he wraps up with "Dependency Injection isn’t (just) for testing, it is to Enable Change." Nice. So if I don’t use DI, my projects can’t change at all?
And here I was hoping to avoid a wig for the next two years at least.
I am very bad in debate tactics (see first paragraph for why), but I do believe that reversing a sentence and then attacking the reverse conclusion is not a fair tactic.
The ability to change the code is strictly a matter of the quality of the code, the amount of tight coupling, the amount of separation of concerns that you have there. DI or lack of DI doesn't really into the equation on its own. You can certainly build DI-based projects that are impossible to change.
My argument is that DI is one of the tools that I use to make change possible. It is not the only one, naturally, and I am not saying it is the sole factor, but it is an important tool.
In addition to that, I don't think that I have ever said that DI is good for everything and you should use it the next time you want to build a hello world program. Most of the advice that I tend to give (or that given by the people I respect) usually has the "when applicable" part.
It all comes down to cost vs. benefit and that’s what I’m not seeing enough discussion of, here. What are the costs of using DI?
I think that the issues here are twofold, first, you see to significantly exaggerate the cost of using DI, out of proportion, I would say. The second is that you are trying to evaluate a part of the whole while refusing to consider the whole. DI is useful on its own, yes. It is much more useful when you have a container. There are different patterns for DI when you use a container and when you don't, and there are different usage scenarios.
Loose coupling is so far down my project priorities that standard OO practices are more than sufficient
Ookaaay.... so, according to you, this is a good approach?
public int GetCustomerCount() { using(SqlConnection connection = new SqlConnection(Configuration.ConnectionString)) { // ... } }
To me such code suffers from a numerous major issues, ranging from flexibility of implementation to performance concerns to transaction semantics.
Only, everywhere I go for mock objects, I’m being told that all the best projects are loosely coupled so what kind of incompetent fool am I for not implementing DI even before I ever thought of needing a mock object framework?
Jacob, in this case, consider this an official invitation to the Rhino Mocks mailing list, and a promise to ban anyone calling anyone else a fool or an incompetent.
Yes, mocks objects goes hand in hand with DI, because you need to get the mock behavior there. Yes, you can use TypeMock, but as far as I am concerned, the moment that I am doing that, I am in the same boat as I am when I am trying to test private methods. There is no need to do that.
If I ever get into a project where loose coupling is a big enough concern that I have to consider seriously intrusive techniques in order to reduce the risks of dependency change,
Just to clarify, I find it hardly intrusive, and I am sadden to hear that you don't consider loose coupling (which also means SoC) is important to the code base.
Again, Ayende is an example of this attitude when he says, "The main weakness of Type Mock is its power, it allow me to take shortcuts that I don’t want to take, I want to get a system with low coupling and high cohesion." As a personal statement, I guess this is fine. If Ayende doesn’t trust himself to write code appropriate to his requirements and resources, then by all means, he’s free to handcuff himself with less powerful tools.
I appreciate your permission, but I think that you missed the point. My requirements are high cohesion and low coupling. In the end, I want code that I can read, understand and work with.
That’s not at all what he means, though. Too often this kind of statement isn’t intended to actually target the speaker. The speaker is perfectly confident in their own abilities - in this case to create low coupling and high cohesion. It’s the abilities of other developers they want to constrain. It’s another manifestation of the "programmers not doing things my way are idiots" meme used against Visual Basic for so long.
I take offence at this statement. Rhino Mocks is a project that I developed because I wanted to have a mocking framework that I could deal with. It is an OSS project because I feel that other people can also use this functionality. It has never been my intention to use Rhino Mocks to preach My Way to the masses.
Nevertheless, Rhino Mocks reflect the way that I use a mocking framework, and the best practices that I and the community that has grown around it have discovered. I am using the same Rhino Mocks build as anyone else, for the same reason that I quoted above. This is not the case where I am handing down architecture to the stupid programmers down the hall: "Thou Shall Have Loose Coupling, And That Will Be Enforced With Rhino Mocks".
Frankly, loose coupling isn’t an absolute value anyway, so arguments that every decrease in coupling is automatically worth the cost are absurd on their face
Loose coupling comes with the cost of needing to understand the interactions within the system as a whole from the individual parts. I find that the ability to look at a piece of code in isolation, and then look at its interactions much easier than trying to grok a system that has tight coupling, since I can't usually understand a piece of code is isolation. And, as always, it is entirely possible to have too much loose coupling.
To summarize this decidedly long post,
More posts in "Dependency Injection" series:
- (23 Aug 2007) Separating the Container
- (21 Aug 2007) Applicability, Benefits and Mocking
- (18 Aug 2007) IAmDonQuixote ?
- (18 Aug 2007) More than a testing seam
Comments
There seems to be so much anger in Jacobs comments - I never notice any passion - the difference is VERY apparent. Ayende - your time is best spent out of this discussion - you have no need to defend practices that you know work. So far Jacob hasn't produced the following.
How to unit test components using just OO techniques
How to test components in isolation without database, logging, AD, ASMX, other hardware?
In essence, an equvalent to DI and Mocking that requires less typing!
Ayende, I'm with you on this one.
DI is an OO technique - it's as simple as that. Replacing DI with static method calling is Procedural Programming.
DI is a form of polymorphism and encapsulation and is used heavily in the GoF Structural Design Patterns (Adapter, Decorator, Proxy, Flyweight etc...) - all of which are pillars of OO programming.
I'm not a big fan of DI containers because I like configuring my objects in code (and using DI within that code :-).
Also, decoupling your code from external dependencies always leads to cleaner code which can outlast major technological shifts. Ease of testing is an important benefit as well.
As always, an excellent post. I'm formulating my own response to Jacob as well. I still think he's overestimating the "cost" of dependency injection in a codebase, and underestimating the benefit. There are certainly other ways to follow SRP but I've found that DI makes it second nature.
Also, shameless plug @Yoni: If you want DI and still want to wire your objects with code, check out Ninject: http://ninject.org. :)
Guys,
You are doing exactly what Jacob is talking about.
Shouting Acronyms without distinction.
Shun, there IS a need to defend this practice as more and more developers are actually re-thinking if DI is really the silver bullet.
Jacob has answered all you questions.
Yoni, creating an object in your code (eg. new Yoni()) is not considered Procedural Programming
Not really related to this discussion, but
"An good IoC can and should be invisible to anything but the Main() method."
...I never could achieve this and I'm wondering, what you mean by this statement. There a are always a lot of references to the DI/IoC container throughout the application. E.g. each IView that needs to be created to show a WinForm requires to ask the Container to give me the instances:
ObjectFactor.GetInstance<IMyView>()
It might be an IController as well and IView is automatically resolved by the container, but as long as I need to instantiate new objects dynamically, I need to reference the IoC container at serveral places outside Main(). Maybe in very simple cases, the whole object tree required during the lifecycle of the application can be set up in Main().
Tobias,
Castle Windsor will create the IMyView for you as needed. So if you consider an object graph in your application, you only need to ask Windsor for the root object. Windsor will create instances as as needed as long as a child in the tree doesn't create the instance using new. Instead let the container inject that implementation.
See the Windsor tutorial for more.
@Nate
I think I'll stick with the plain old:
new ClassWithExternalDependency(new DependencyImplementationX())
Something about the hocus pokus of DI containers disturbs me. Too loose coupling is sometimes worse than too tight...
@Roni
Yoni.DoSomething()
DoSomething(ICanHelp helper) {}
DoSomething(new Yoni()); // Yoni implements ICanHelp
new Yoni().DoSomething()
Jay,
I havn't worked with Windsor yet, but wouldn't I still have to call container.Resolve<IMyView>() somewhere in the code? Most of the times it's simply not possible to create all instances upfront. A WinForm is a perfect example. I don't wan't all my forms to be created when instantiating the root of the object tree in main(). A form is normally created as a response to an user action. This means that I must have some container.Resolve<>() calls in my code in response to the users activity. So it's not just the Main() method that needs do be aware of the IoC container.
Or am I missing some "black magic" in Windsor here?
Given what Bellware has said about DI:
http://codebetter.com/blogs/scott.bellware/archive/2007/08/18/166944.aspx
"So, both sides of this debate are questionable: dependency injection in application code does indeed suck badly, and type mocking over a static API is a cop out that ultimately leads to design atrophy. The folks with the most at stake in this debate seem to be those who have invested a significant amount of time either creating DI and mocking tools, or learning how to use them effectively, which starts to sound like an argument over who gets to be first in line to be attached to the anchor before it's thrown overboard."
why should I use DI? Now that one Alt.NET guy has questioned it, does that mean the 'echo chamber' will resound accordingly?
To Roni: "Isolate with TypeMock" may help people address testing needs, but DI goes far beyond testability.
To Tobias: You should only need to talk directly to the container from code that has specific need to select between alternate service implementations based on application state rather than container configuration. Otherwise, your injection configuration should be configurable to take care of your dependencies for you.
To Yoni: Your "new ClassWithExternalDependency(new DependencyImplementationX())" starts you down a path where your application will leak dependency knowledge up through the dependency chain. I certainly take an approach a bit like yours when introducing containerless DI into a codebase solely to facilitate unit testing, but I wouldn't recommend using it beyond that, and would recommend that it only be used temporarily: the app is probably best served by gaining a container over time.
I do agree, though, that "too loose" can be dangerous, having worked with a few too many everything's-an-interface designs over the years. There is a happy ground in the middle, though.
I recall awhile back reading something on the bitter coder's blog about good places to use dependency injection.
I've found some uses for DI in some scenarios, in other scenarios it makes for too many vestiges where direct access is more practical.
Either way I'm so tired of hearing developers argue about things they will never agree upon. I once worked with a developer who decided physical intimidation was appropriate because their solutions were Wrong and his was Right and ends justify the means.
This kind of argument turns personal, and when you are building a system, there are no winners to a personal fight only losers.
To Tobias again: the "black magic" isn't so much in the container. Yes, if you do need to switch components on the fly based on application state, at least one point in the code will need a container.Resolve<>() call. Emphasis on "one point in the code".
This has nothing to do with DI at this point, but rather something more akin to Service Locator, and if in the past you would have factored your application properly to minimize dependencies on any specific service locator implementation, there's nothing to stop you from isolating all but one point in your codebase from your dependency on your DI container's service locator functionality.
To jdn: I think that everyone is more set in their ways than they might think, to be frank. Our experiences on particular sets of project work expose us to different ways of achieving things, different limitations in technology, in schedule, in resources, etc. We choose tools that work in our scenarios and then get very used to using them. Other options end up "not making sense" once we're too comfortable in our space.
Most of the argument seems to be around TypeMock versus DI. The problem with this argument is that they are apples and oranges.
TypeMock is a piece of technology specifically created to enable unit testing of code that has explicit, unchangable dependencies. DI is an approach, supported by a variety of different technology each with a different "take" on things, that promotes loose coupling. Period. DI doesn't directly promote testability, it doesn't directly promote SoC, it doesn't directly promote high cohesion. It promotes loose coupling. Loose coupling of this form is IN TURN very supportive of testability, SoC, and efforts at high cohesion.
Too many DI haters seem to think it is only for testability. Too many DI lovers make it sound like DI is required for things it only indirectly promotes. What both need to recognize (the haters need to more than the lovers, admittedly) is that DI is an approach that can be used to assist with many kinds of efforts, supports many aims, and creates a "network effect" of sorts when one is looking to achieve multiple goals on the list of things that DI can assist with. DI assists a set of mutually-supporting efforts that create a feedback loop of improvement, which is why its "lovers" love it so much.
As for "dependency injection in application code does indeed suck badly", all I can say is that if the concrete dependencies are still seen in the application code, no dependency injection has been done. The explicit dependency just _moved_. No no dependency injection having been done, people like Bellware aren't really be in a place to make any claims about DI.
PS - I don't know if I'm either a hater or a lover. I'm certainly not a hater, but I wouldn't lump myself in with the lovers, so I suppose I'm somewhere in the middle. For the purposes of this post, I am a hater of "apples and oranges" comparisons. :)
@Jeremy
Not sure what you mean by "starts you down a path where your application will leak dependency knowledge up through the dependency chain". As long as the code that accepts an external dependency accepts an abstract (and well designed) type it makes no difference whether that dependency was injected by code or by a container. As for the code which creates the object in question - using 'new' is usually a lot simpler and robust than configuring a container to do the same...
Yes, Ayende, I am saying that sometimes
using(SqlConnection connection = new SqlConnection(Configuration.ConnectionString))
is an appropriate approach. Specifically, it is an appropriate approach in cases when flexibility of implementation, performance concerns, and transaction semantics aren't as important as getting a product out the door with a minimum of fuss. I know that's anathema to you. In general, I agree with you that it is worth the time for better design. I'm just not as interested in purity as I am in serving the true needs of the client (whoever that is and whatever their needs are).
And before you go there, I'm perfectly aware of maintainability concerns and that you can come to regret this decision. It's just that I'm also aware of how often something like this is perfectly acceptable, even over the long haul, such that you don't regret it. That's why I say that design is always about context and involves important trade-offs that you have to navigate. No practice is so good that it fits every situation. Any practice that claims to do so is fooling itself.
Not that every practice is acceptable. I personally consider MS DAAB to be the spawn of all that is evil in DB access. I just acknowledge that this is a personal quirk of mine and might not be shared universally.
As for taking offense, none was meant. I'm merely trying to point out how you come across to the unbelievers when you say things like "Type Mock is too powerful". Too powerful? How can any tool be too powerful? You see, the only way that something can be too powerful is if you don't trust people to use it wisely. No tool uses itself. It's all used by, well, users. In this case other programmers. If you don't mean that "programmers who use TypeMock are idiots", then you can't really claim that TypeMock is too powerful.
Frankly, I think your best point is when you say "[Y]ou can use TypeMock, but as far as I am concerned, the moment that I am doing that, I am in the same boat as I am when I am trying to test private methods." This is the best argument against TypeMock I've seen so far because as with testing private methods, using TypeMock requires a view into the internal workings of the object being tested. It's a form of tight coupling of your tests to your objects being tested. Of course, most mocking involves some degree of this. The whole "Expect" assertion paradigm falls into this category. I view it as a continuum where TypeMock lets you traverl further along the axis than other mocking tools do because you aren't limited to mocking through interfaces.
@Jacob:
I've heard the argument that interface-based programming is too "purist" of an approach and doesn't adequately serve the customer's needs. It's complete bunk. Sure, you can go overboard with your loose coupling, but if you're thinking about things in a service-oriented fashion, declaring interfaces to work as contracts can actually speed your development. In my experience, I've found that declaring interfaces for each of my services helps me to keep the interaction points between the types as simple as possible.
In my opinion, saying that designing your app around interfaces results in development overhead is like saying that writing a routine in C results in performance overhead over writing it in assembly language. It's technically true, but not meaningful. :)
@Jeremy:
you said: "there's nothing to stop you from isolating all but one point in your codebase from your dependency on your DI container's service locator functionality"
How should this be possible without just replacing the dependency on the DI container with something else, which then depends on the container itself?
you also said: "You should only need to talk directly to the container from code that has specific need to select between alternate service implementations"
I wouldn't argue ague against this, if "no service" is an "alternate service implementations" as well.
Think of a simple WinForm scenario. If a button is pressed on Form1, Form2 should pop up. Having some kind of MVC pattern we may have:
Controller1(IView1 view) {...}
Controller2(IView2 view) {...}
So Controller1 needs to react on the button press event in IView1 by creating an instance of Controller2 and IView2. Using a DI container this can be as simple as:
container.Resolve<Controller2>();
IView2 will automatically be resolved by the container. But I still have Controller1 talking to the DI container. I think it was Scott Belware who once described this as "transitive dependency". At least right now, I can't see a reasonable way to avoid these transitive dependencies.
An IoC container "invisible to anything but the Main() method", as Ayende said, simply seems to be impossible for me in many applications.
@Nate: Call it complete bunk if you want to. Doing so is hardly useful to me, however, nor is it a compelling argument. Indeed, I'm less likely to consider anything following that statement than if you'd simply given your experience. Here's the thing, "complete bunk" is an absolute statement and can thus be disproven with a single case that contradicts it. Do you really need me to create one?
Declaring interfaces for each service is perfectly reasonable design. It's hardly the same thing as "designing your app around interfaces", however, unless you interpret every object as a service provider. That's the kind of purist-think I find too theoretical for practical use. I'm much too pragmatic to buy into that route.
Also, don't try to paint my position into a corner with an inapplicable analogy. It may play well to those who already believe your position, but it shuts down discussion as you create an artificial barrier to any response I might choose to make. You wouldn't like it if I compared the overhead of creating interfaces for all your objects to Pascal's Wager (http://en.wikipedia.org/wiki/Pascal%27s_wager).
Jacob,
I don't understand why you are trying to cast me in the role of the theoritical guy. I build software for a living, and I am facing the usual "we need a demo yesterday, and the product should ship by the end of the week" conversations that a lot of us have to face.
Getting a product out the door with a minimun of fuss is hardly an anathema for me, it is one of the things that I spend a lot of time trying to improve.
The problem is that you seem to assume that I am working with an empty toolset. It is faster & easier to me and my team to work correctly than to try this type of hacks.
Because we have the toolset in place that turn that new SqlConnection to:
public long GetCustomerCount()
{
}
I don't think that I would argue with that. I argue with the way that you seem to be trying to limit loose coupling and separation of concerns into "big, complex frameworks".
Well, you can use unsafe block in C# to do stuff. That is powerful, the general approach is that this is a good thing to have, but you should avoid doing it, because it is too powerful.
And by that reckoning, good C++ code never has memory leaks, right?
Since practical evidence points to the contrary, I am very happy to have a GC to clean up after me, rather than do it by hand.
I most certainly can say that TypeMock is too powerful without saying that TypeMock users are idiots. Again, don't try to reverese a statement and get conclustions out of it.
I think that you can agree that programmers who use C++ are not idiots, and that C++ memory management is very powerful. But that doesn't explain memory leaks.
There are two types of interaction based tests. Those where you supply stubs, which you just need to fill some values and then test the results and those where you supply mocks. In the first case, you are simply making sure that the test can run as a Unit Test, in the second, you are testing the interactions with the collaborators.
Nevertheless, one of the advices that I often give about mocking in general is no to get into paralisys by mocks, where you over specified the test, and any change at all will cause issues.
@Ayende: I'm not trying to paint you as the big theoretical guy. I'm trying to paint you as the guy who throws around general statements of "this is correct" and "this isn't" without including context or qualifiers. This latest response is much better because you qualify "It is faster & easier to me and my team to work _correctly_" with the context of "Because we have the toolset in place". I find that much more interesting than earlier, simpler statements about DI being universally correct without any attempt at constraining your statement.
There's a difference between "powerful" and "too powerful". One is a recognition that you can do weird and potentially stupid things with something, the other is a judgement that people shouldn't be allowed to use it. This applies to both your C# unsafe block and C++ examples. Both are powerful. Neither are "too powerful." People who claim that something is "too powerful" are implying that all people who could use it are such idiots that nobody should have access to that kind of power. It may not be their intent to call people idiots but it is, nevertheless, a clear implication.
I think that it's good advice to avoid overspecifying tests so that any change at all will cause issues. TypeMock can certainly lend itself to tying your tests tightly into your classes such that changes can break tests even when the method is correct. This is possible with just about any mock framework but is a particular weakness of TypeMock because it encourages pin-point specific overrides. That'd be an example of something I'd consider "powerful" that people should be careful in using.
You have a different definition of too powerful than mine.
In my book, too powerful means that it easily lends itself to problelm
You have a different definition of too powerful than mine.
In my book, too powerful means that it easily lends itself to problelm
Not a hater or a lover.
It goes without saying that I generally believe in loose coupling and programming to interfaces but they always need to be considered in context.
For example having my business classes directly coupled to the database or any infrastructure service seems to me to be a bad idea and I avoid it all costs. I don't want my business classes involved in that sort of work. However in some cases it is unaviodable and I then turn to a dependency injection based approach involving interfaces (though not IoC). The key point for me is that I'm decoupling and using a DI approach because that is what I think is the best option for the design, not just because I need to avoid it to make the classes testable.
However I don't particularly want every business class to be supporting interfaces so that they can be injected and mocked. The problem isn't just that the extra interfaces aren't necessarily much use in real situations (because they aren't designed for real clients), or that in real use I don't want to replace one Order class with another, but also that they hide the meanginful abstractions. I think the image problem that interface programming has is caused by the overuse of interfaces, where as if you use the approach carefully where it is most appropriate you can end up with a really good design that everyone can buy into.
That tends to be the way I work anyway, I'll read someone like Robert Martin and think eureka and then gradually introduce the techniques to places I think they are most appropriate.
On the complexity issue, I should say that to me DI isn't that complex an idea and some of the frameworks are ridiculously simple. However redesigning to allow the IoC framework to get in there (interfaces aplenty) does have an additional cost in terms of complexity. If I need the decoupling, or think it improves the design, or if I think it makes the software more understandable then great but if not I'm buying very little.
On another topic TypeMock being too powerful seems an over statement to me. Its like other powerful techniques such as reflection, use them carefully where appropriate and you are fine but overuse them and you can end up in a mess.
In reading more recent replies, as well as re-reading the earlier ones, I can't help but thing the following:
A lot of people want to selectively blame technology for exposing problems with their work.
On the one hand, we've seen examples in this thread where projects are sprinkled with direct dependencies on things like specific service locator implementations. Instead of refactoring to at least centralize that dependency, if not entirely abstract it, they blame DI and/or IoC containers, as if they had anything to do with the problem in the first place. IoC containers just happen to often provide service locator behavior. It's up to you to factor your application well, whether you use such a container or not.
On the other hand, the DI/IoC container haters are more than happy to knock it for all sorts of reasons, many of them based on errant claims and/or misunderstandings of what DI/IoC are about (recall my "apples and oranges" comparison concerns), all the while ignoring that their testing technology of choice is actually pointing out all sorts of smells in their designs, specifically explicit dependencies that should at least be centralized if not entirely abstracted.
Both of the above kinds of "selectiveness" happen naturally, as people are often unwilling to look at their own work objectively, but that doesn't make for a good excuse. Glass houses, and all that.
To Jacob: Of course Oren is only speaking in terms of his experiences on his projects! And given that this is his blog, where he does just that each and every day, he need not coddle anyone along by couching everything he says in "In my particular experience, on my particular projects, YMMV". Every blog reflects the specific experiences of specific people (or groups of people) in specific situations, and will be more or less applicable to other people in other situations. Given that this is a universal truth about the online medium (though not just the online medium, the same should certainly be said for books, but don't get me started on "Evans haters", "Fowler haters", etc. :) ), it is your responsibility to remember that YMMV, not Oren's to state it.
@Ayende I'm trying to point out how you come across to unbelievers when you use terms like "too powerful". In this case, it doesn't really matter what your definition of "too powerful" is. It doesn't matter how many people will rise to the defense of your use of the term "too powerful". What matters is that this is how a certain number of people are going to interpret your use of the term "too powerful". You can decide that I'm an aberrant example in how I read it, but I think that would be a mistake because I'm not stretching to get the meaning I'm reading. Anybody who disagrees with you about whatever you are calling "too poweful" is going to feel that you are being censorious and deprecating. Worse, many people who are on the fence are going to read it the same way. If that is what you intend, then that's fine. If it isn't, you might want to find a better way to describe what you mean.
@Jeremy: It's not the implied YMMV that I have a problem with. I wouldn't be in the discussion if I didn't believe that my milage could vary. What I'm trying to get more of in certain technical conversations isn't the caveat that someone can disagree, it's the broader context of the statements themselves. If I worked on his projects or team, then I'd be familiar with his context and thus aware of the background that leads Ayende to make the statements he does. Since most of the people reading this aren't, it'd be helpful if Ayende would include more context for his broad general statements about programming rectitude. That'd be helpful all around because people in similar situations would be more certain that their situation conforms to his. Plus people who want to be closer aligned with his environment could more easily do so and people with demonstrably different environments would be aware of those differences and more able to translate his statements in ways that are useful to them.
In the DDD forum Udi put the following quote (http://tech.groups.yahoo.com/group/domaindrivendesign/message/4774):
"I think that there is a good chance that if you were to employ this kind of design, in the kind of architecture described, on the kinds of projects that I do, in the kind of companies that I work with, that your success would be entirely dependent on yourself :)"
I think that Jeremy is bang on, and it is a problem with blogs in general in my view. Its difficult to take whats on a blog and then see how to best apply it and what things you need to consider...but thats life and I'm still glad people take the time to write blogs.
I don't take part in religious arguments, as a rule.
Allow me to disagree, on my blog, that is he definitive definition.
I do believe that I do, except that I don't do that on a post by post basis. Go over and read the history, you can see what my context usually is.
Comment preview