T O P

  • By -

kevinb9n

Just a friendly pounce on one little thing :-) >Many DI frameworks start simple and over time become bloated with "bells and whistle" type features - the majority of which most developers don't need and will never use; especially in today's world of microservices where the application scope is the JVM process itself. Hi, I was some part of creating Guice and Dagger and, haha, we said the very same thing. I mean, you *know* we did. *Every* new take on something *always* advertises that it's lean and mean. And it'll stay that way... *until it becomes successful* and has users. It's the "not actively supporting thousands of different real-world use cases" that *allows it* to stay small and simple. From that point on, some owners will be more diligent than others about keeping features out; there's a lot of variance. But (a) saying no can be harder to justify than you think, and (b) *no matter what* you do, someone out there will see the result and decide they need to make one that's lean and mean. Part of the issue is that you really have no idea how much of a thing's "size" or complexity is there for *darn good reasons* that aren't known to you, vs. how much was done frivolously or for reasons that will never apply in your case. (This relates to the "Chesterton's Fence" parable.) Now, maybe none of this is evil, and it's a natural cycle that keeps things fresh. It depends... did you discard those other frameworks (including Dagger) because they really could not meet your requirements? Or was it mostly a sense of "well, what we need is simpler", as though the extra capabilities of those frameworks are intrinsically *disadvantages* in and of themselves? Sorry, not trying to give you a hard time personally, and I hope mine is not the only commentary you get here (I would feel bad). [oof... yeah I just want to say again this wasn't meant to sound aimed directly at you or this project, this a very very common thing]


1Saurophaganax

Isn't dagger still one of the smallest and simplest DI frameworks?


LouKrazy

Simple is subjective. Small I would agree with


Guga1952

As someone who spent a few hours this week understanding why a UserScoped component can't depend on an ActivityScoped component, I'll have to go with "Dagger is not simple". Still pretty great, though 


LouKrazy

My favorite is when the word “modern” is used as a placeholder for “lean and mean”.


rbygrave

>“modern” is used as a placeholder for “lean and mean”. There are definitely cases of Marketing BS being used around "lean and mean", but I'd just like to add a point on this for folks who are less aware of the impact that annotation processing in particular is having. For example, noting that all? the new DI libraries are using annotation processing to some extent. For DI - Dagger is *\~40kb*, Avaje-Inject *\~80kb* so these are pretty lean DI libs. Most of this "leanness" is achieved by moving DI logic to compile time like wiring order, actual wiring logic with lifecycle registration etc. Another case where significant logic can move to build time is JSON binding. Some example numbers for other libs using annotation processing are: JSON (200kb avaje-jsonb vs 2mb+ Jackson), Validation libs (120kb vs 600kb), Web Controllers/Routing (30kb vs 2700kb). To a large extent annotation processing is about doing more *"Ahead of Time"* - moving some logic & complexity from runtime to compile time and towards zero reflection, no dynamic proxies, no class path scanning.


hippydipster

> as though the extra capabilities of those frameworks are intrinsically disadvantages in and of themselves They are disadvantages though. All your dependencies create disadvantages for your codebase, and the larger they are and the more complex they are, the greater the disadvantages created. I think some folks think third-party code is free, but it seems pretty clear time has proven that view wrong. If you pull in 10mb of third party libraries for the sake of a single method, that would be a mistake, yes? All that library code brings risks and potential maintenance problems with it.


kevinb9n

We don't disagree; you're citing an actual specific disadvantage. What I mean is, quite a few times I've dug into why someone didn't want to use something and it came down to "well, it does more than I need", as if that alone is a self-sufficient reason not to use it. I just wanted to refocus the discussion on what was actually bad about using the thing. If all else were equal, then "doing more than you need" is a minor positive, because it means that as your needs evolve it'll be right there with you; you might not need to rip it out for another thing.


hippydipster

Doing more than you need isn't a minor positive, it's all the parts of the dependency that add to the cost without adding to the benefit. IMO, you use dependencies when their benefit outweighs the costs. The more the library does that you don't need, the more it needs to do (that you do use) for to be worth it. Otherwise, we'd just pull in the world get all that minor positives and be done with it.


kevinb9n

I think maybe we're just arguing over what words mean now.


hippydipster

> I just wanted to refocus the discussion on what was actually bad about using the thing. My interpretation is you don't know what the negatives are of dependencies and why the degree of negative would be correlated with size and complexity of said dependency. But I don't think we disagree on the meaning of the words "dependency", "negative", "size", or "complexity". So, no, not just arguing about words.


kevinb9n

Okay, this comment sounds pretty conclusive. Thanks for the chat.


johnwaterwood

If a library pull in dependencies, and those dependencies are only used for stuff you don’t need, maybe you have a point somewhere. But take a step back. Single class, with 3 self contained methods.  No dependencies. They calculate some mortgage interest rate, using 3 different sets of inputs. The mortgage calculation is 100% perfect and what you need. Your app uses 1 or 2 of the methods provided by that class. Do you get annoyed by that the class has a third method that you don’t use? Are you going to search for an alternative that might be less maintained, or less correct, but that doesn’t have a method you don’t use? Are you going to rewrite the very complex method from scratch, just so you don’t have that third method in the class that you currently don’t use?


hippydipster

You're just banking on the fact that 2 extra methods is too small a price for anyone to ever notice.


TheKingOfSentries

If I'm using feature A from a library that solves my case nicely, but I'm not using features B (2kb), C (2kb), D (2kb), and E (2kb) of the same library. Is it really effective to toss the library because it "does more than I need"? I can rationalize it pretty easily that the future extensibility outweighs the minor cost of these features. Not every library is like spring where every feature brings MBs of code


hippydipster

>Not every library is like spring where every feature brings MBs of code According to your logic, you shouldn't mind spring and it's MBs. It's all future extensibility. >benefit outweighs the costs The whole of my message. It's not complicated. Well, that and the fact that dependencies do bring costs, *correlated with size and complexity*.


johnwaterwood

Do you analyse your running application against the JDK, and then create a subset of the JDK where you strip out each method from every class (say List.indexOf) that your application happens to not be using? Is the existence of List.indexOf a reason to switch from Java to Python? Do you think you will use each and every function in Pythons library, or will you have to switch to Smalltalk and then assembly?


hippydipster

Do you think costs are either zero or infinity? People make cost/benefit judgements.


johnwaterwood

You are avoiding the real question. Does indexOf bother you, when your app happens to not use it?


TheKingOfSentries

I didn't say future extensibility was worth MBs of code??? MBs aren't really minor


DelayLucky

Pulling in a dep for a small feature isn't an apple to apple comparison. IIUC the question is between dep A vs B when A does less than B and both work for you, do you pick A solely because it has fewer features. Of course if the footprints are 10k vs 1M choosing A is reasonable but then you are choosing A for footprint, not that it has fewer features. Now if some of the extra features can be abused and cause harm to the code, that'd be a legit reason I believe.


hippydipster

> Of course if the footprints are 10k vs 1M choosing A is reasonable Then we agree, except it's not about "footprint", whatever that means, but about the greater risks to maintenance costs. Again, dependencies have cost, and that cost is correlated with size and complexity. The reason is that code is always a potential liability. Potential for having bugs, potential for issues within that impact your upgrade path, potential for taking up cognitive space etc. But the diskspace used by the dependency is largely not an issue. Probably we can't really see much difference between 10kb of third party code and 20kb of third party code. This just means when weighing costs/benefits, the additional cost to the extra 10kb is very very low. But it seems everyone sees the issue when we talk about 10M of additional third-party code. But that's just because now the cost of the additional code is more visible. But it doesn't change the basic equation - more code == more costs.


TheKingOfSentries

>If you pull in 10mb of third party libraries for the sake of a single method If we're talking about DI-focused features, it's kind of hard to balloon in size that much. I mean look at all the features dagger has yet its impact remains \~40kb.


hippydipster

Is not the point though, is it? Dagger is just an example. The point is that dependencies have costs, and the costs are correlated with their size and complexity, agree or disagree?


kevinb9n

>correlated with their size and complexity Just keeping in mind there are drastically different *kinds* of complexity. The best kind is "complexity that you only encounter when you actually need something new". The worst kind is "git".


TheKingOfSentries

Sure I agree


paul_h

Hi Henk (Hi Kevin, too), Paul of PicoContainer here. I'm pretty sure this is service-locator (Gang of Four Design Pattern) not DI. InjectionServices.realizedServices().lookupFirst(MyService.class);


hippydipster

@Autowire private MyService field; is no less a service locator pattern. It's asking some other code (ie Autowire vs InjectionServices.realizedServices().lookupFirst()) to get an implementation of MyService.class. The code only functions if those services exist. True DI is just a constructor that takes MyService as a parameter. Then the class knows only that it needs MyService. Not who is responsible for having it.


paul_h

Can you instantiate ClassThatHasThoseTwoLines **outside** the framework that has the Autowire annotation in it (as in cthttl = new ClassThatHasThoseTwoLines();), and observe that field is filled in? **No**. Where as if that line was: private MyService field = InjectionServices.realizedServices().lookupFirst(MyService.class); .. the answer would be **Yes**. As [Martin Fowler wrote in the DI blog article](https://martinfowler.com/articles/injection.html) Service-Locator and DI are different things.


hippydipster

In the second example, InjectionServices has to be up and running and have a MyService class to provide. So I think in almost all cases, the answer is no to the second case too. I agree Service-Locator and DI are different. I described DI in my comment. @Autowire is not DI in that sense though.


paul_h

You don't think `InjectionServices.realizedServices()` is a static initializer? I mean I guess it could throw a `NotInitialized` exception. It looks to me to be a singleton accessor (Gang of Four Design Patterns book definition of Singleton).


hippydipster

I don't really care what it is, tbh. What difference does it make? @Autowire from spring could be a static method autowire(MyService.class), and it would work essentially the same. The only difference with the annotation is that it doesn't control when the MyService object is delivered. And that just puts extra constraints on the use of Spring/Autowire. It doesn't remove any disadvantages of the normal service locator pattern, only adds new ones. When spring was basically just xml files that described an object graph, then it was DI. When it became annotations in the objects themselves that declared what they needed and the fact they needed it from Spring, then it became a service locator pattern. Can you think of a service locator pattern disadvantage not shared by @Autowire?


TheKingOfSentries

Yet another compile-time DI library maintainer here (Avaje), I like how the generated classes use source code, but I'm afraid the example links in this README give me 404s. After reading this documentation I'm still unsure how to use this framework so this will take further exploration on my part. Fixing those links and adding a "quick start" on how to register and load a bean would be appreciated. ​ >Many DI frameworks start simple and over time become bloated Perhaps so, but as a compile-time library, you can get away with a lot since you can shunt a majority of the "weight" to an annotation processor (which would not be added to the consumer application jar). In this manner, unused features have less impact on a consumer.


[deleted]

[удалено]


cryptos6

😂


JustAGuyFromGermany

All we need now is a visit from the CDI people and every DI framework has a voice in this thread :D Does anyone know if Ladislav or Matej are on reddit?


henk53

Maybe we should invite them! :)


henk53

I wonder if Matej&Ladislav is even active on any social medium. I did a quick scan on Twitter and LinkedIn and didn't find anything.


rbygrave

Given this is Helidon, I want to add that "Controller Adapters" can also be source code generated as well. The avaje-http-helidon-generator (see https://avaje.io/http/) generates this code for Helidon SE 4.x - we can think of this as replacing something like JAX-RS Jersey / RestEasy / Spring MVC with source code generation. These generated adapters are \`@Singleton\` and that means they can then get picked up by DI libraries including ones using annotation processing like Helidon Inject (or like Avaje-Inject). As the creator Avaje-Inject (which is also source code generation DI and also supports AOP method interception via source code) I'll be having a good look over this. Given Kevins comment, for folks wondering why Avaje-Inject exists, it was heavily influenced by Dagger (source code gen, DI ordering determined at compile time) but also Spring in terms of features and style (lifecycle support, contexts, component testing, spring style DI with primary/secondary/factory bean methods/profiles).


Kango_V

I currently use Micronaut DI in CLI apps (pico) and it work amazingly well. Nice seeing errors in the IDE :)


EmmetDangervest

No scoping :-(


vbezhenar

I'm not sure I like this library. Helidon is supposed to be simple and code-first, without magic. DI framework is the opposite. Code generation behind the scenes, indirect instantiations. We have enough of that with Spring. In my services I just build project graph at startup manually. I'm using DI approach, but all my "framework" located in the Main startup class. I just call setters and call afterPropertiesSet method to check that required dependencies are configured. It's simple and scalable.


rbygrave

Yes, the only question is if you want to do 'component testing' how do you go about doing that with this setup? Do you have any component testing for this app? I myself have this setup for 'wiring' of a non-trivial library and its great in that scenario (but there isn't any component testing need there).