10 ms·
Dependency injection frameworks add confusion
- sureglymop 1y agoI highly agree. I especially believe that manual DI should always be the starting point. Eventually one can evaluate if there really is a need for a framework. It's already dangerous if I have to change the code significantly just to satisfy the framework.
- epolanski 1y agoIsn't that true for every framework/library out there to some extent?
- srvaroa 1y agoLooking forward to someone writing the Spring equivalent this on the JVM
- brabel 1y agoWhy? It would be nearly identical, just changing the names of the frameworks.
- delusional 1y ago[flagged]
- TeMPOraL 1y agoOr maybe they just don't have oversized egos. The key to using a framework effectively, whether it's Spring in Java or SAP for your business, is to accept that the framework knows better than you - especially when it objectively does not- and when there's a difference between how you or your business think of things, vs. how the framework frames them, it's your thoughts and your business that must change. Otherwise, you're fighting the framework, and that's worse than just not using it.
- delusional 1y agoDo you not think I've heard that line before? The framework knows nothing. It's made by a bunch of children that make CRUD apps wrapping an SQL query behind an HTTP server over and over again. They don't make any applications that do anything commercially or technically interesting, resorting themselves to infinitely copying data structures with increasingly complex annotations, a practice they call "business logic", to trick themselves into feeling like they're doing something. I've been there. I've seen it. It doesn't lead anywhere. The abstractions that spring (and other heavyweight JavaEE-style frameworks) provide is razor thin, and usually implemented in the most trivial possible way. The frameworks, like the applications often built on them, do nothing interesting. EDIT: I realize this is a pretty unkind way to put it. I hope readers can understand the argument along with the indignation I express. I do believe very strongly in these points, but wish I could express them without quite as much anger. I can't tough. Parse out what useful stuff you can glean, and leave the rest along with the knowledge that you don't have to impress me.
- deleted 1y ago[deleted]
- TeMPOraL 1y agoI'm sorry too, I realize I didn't make my point clear. Yes, frameworks are stupid. Their designs are likely suboptimal from the start, and only get worse over time, accumulating hacks to address biggest issue while they double down on going the wrong direction. A competent engineer will easily come up with better ways of doing any individual thing a framework offers, and they have a good shot at designing a framework much better for the needs of their team and their business. Which is why I brought up SAP. It's well-known that adopting SAP usually ends up burning untold millions of dollars on getting expensive consultants to customize it for specific needs of the company, until it either gets written off and abandoned, or results in a "successful" adoption that everyone hates. It's less well-known that the key to adopting SAP effectively and getting the promised value out of it is to change your business process to fit SAP. Yes, whatever processes a business had originally were likely much smarter and better for their specific situation and domain, but if they really want the benefits that come with SAP, abandoning wisdom of your old ways is the price of admission. I say the same is true of software frameworks. Most businesses don't do anything interesting or deep either; you don't integrate with SAP to handle novel challenges, you do it to scale and streamline your business, which actually involves making most work more mindless and boring, so it can be reliably and consistently done by average employees. Software frameworks, too, aren't there to help you with novel technical challenges; they're there to allow businesses to be more efficient at doing the same boring shit every other business is doing. I personally hate those frameworks, but that's because they're not meant for people like me; it doesn't mean they don't work. They're just another form of bureaucracy - they suck all life and fun and individuality from work, but by doing that, they enable it to scale.
- superdisk 1y agoIt always blew my mind that "dependency injection" is this big brouhaha and warrants making frameworks, when dynamic vars in Lisp basically accomplish the same task without any fanfare or glory.
- silvestrov 1y agoBecause "big brouhaha" is what people really want. They don't want simple and easy to read code, then want to seem smart.
- throwaway21371 1y ago[flagged]
- mattmanser 1y agoBecause in statically typed languages they require a bit more scaffolding to get working. And it is a bit magic, and then when you need something a bit odd, it suddenly becomes fiddly to get working. An example is when you need a delayed job server to have the user context of different users depending who triggered the job They're pretty good in 95% of cases when you understand them, but a bit confusing magic when you don't.
- TeMPOraL 1y ago> when you need a delayed job server to have the user context of different users depending who triggered the job I feel this is just a facet of the same confusion that leads to creating beautiful declarative systems, which end up being used purely imperatively because it's the only way to use them to do something useful in the real world; or, the "config file format lifecycle" phenomenon, where config files naturally tend to become ugly, half-assed Turing-complete programming languages. People design systems too simple and constrained for the job, then notice too late and have to hack around it, and then you get stuff like this.
- mattmanser 1y ago
- danielovichdk 1y agoMark Seemann has written extensively about the subject. He a tremendous source of knowledge in that regard. https://blog.ploeh.dk/2017/01/27/from-dependency-injection-to-dependency-rejection/ https://blog.ploeh.dk/2017/01/27/from-dependency-injection-t...
- edvardas 1y agoHis AutoFixture C# NuGet takes away so much pain from unit test maintenance. It does have a learning curve. https://github.com/AutoFixture/AutoFixture https://github.com/AutoFixture/AutoFixture
- wewewedxfgdf 1y agoStrangely I seem to have built all of my software without dependency injection. I must be a terrible programmer.
- whattheheckheck 1y agoCan you expand on that?
- wewewedxfgdf 1y agoYeah I once got a job and after I got the job when they found out I'd never done dependency injection they said "we'd never have hired you if we knew that." Mind you that same manager also believed no code should ever be written if it doesn't have a test written first - real code is only ever an outcome of writing something to match what a test expects - poof - all the fun and creativity went out of programming there in an instant. My philosophy of programming is "there's no right way to do it, do what works for you and makes you happy and if someone tell you you're doing it wrong, pay no attention - they're a bully and a fool".
- nssnsjsjsjs 1y agoWhich is rediculous as a taxi driver not getting the job if they have never taken a passenger with a trombone.
- sfn42 1y agoMore like a carpenter not getting the job because he doesn't know how to frame a house.
- nssnsjsjsjs 1y agoNot really. Frame or not frame a house would be a core requirement if the jobs needs that. Having used DI is not a core requirement. It is something you can learn in 2 hours if you are experienced. It might be like a carpenter not having used a specific tool but instead used another tool and there is a 4hr training at the local college on how to use the new tool. Or like a pilot doesn't get a job because they flew a slightly older Airbus model and need to do some sim time.
- nssnsjsjsjs 1y agoI agree. I had to do what the article says in Node for a project for $reasons but secretly I loved not using a framework, and having the construction explicit. I've also seen bugs because tests may set up DI different to prod.
- rr808 1y agoLove this article, Spring is a cancer in Java, its one of the reasons the language isn't fashionable.
- devrandoom 1y agoCancer? It's poisoned blood and slayer of puppies, hopes and dreams. It's the lord of hell.
- Squarex 1y agoIt's still miles better than what was there before in Java ecosystem.
- elric 1y agoI think you might be living in a bubble of some sort? Spring, specifically Spring Boot, is extremely popular. Calling it unfashionable is simply wrong. I agree that it is a cancer. Monocultures are rarely a good idea. And I strongly prefer explicit dependencies and/or compile time magic over runtime magic. But it is "convenient" and very much en vogue.
- rr808 1y agoI think the problem I'm trying to describe is yes, Spring is popular within the people that do work on Java appications, however the Java/Spring platform is the reason most developers do not want to use Java. Java/Quarkus, Java/Micronaut or even Java/VertX would be more popular if they became the default Java framework in stead of Java/Spring.
- lemagedurage 1y agoAnother downside of DI is how it breaks code navigation in IDEs. Without DI, I can easily navigate from an instance to where it's constructed, but with DI this becomes detached. This variable implements Foo, but which implementation is it?
- rightbyte 1y agoYe debugability and grepability is terrible. DI seems like some sort of job security by obscurity.
- wordofx 1y agoNot an issue in C#
- rootsofallevil 1y agoI haven't really done any c# for 5+ years. What has changed? I remember trying to effectively reverse-engineer a codebase (code available but nobody knew how it worked) with a lot of DI and it was fairly painful. Maybe it was possible back then and I just didn't know how ¯\_(ツ)_/¯
- sverhagen 1y agoIf the rules of the dependency injection framework are well understood, the IDE can build a model in the background and make it navigable. I can't speak for C#, but Spring is navigable in IntelliJ. It will tell you which implementation is used, or if one is missing. In a Spring application there are a lot of (effective) singletons, the "which implementation of the variable that implements Foo is it" becomes also less of a question. In any case, we use Spring on a daily basis, and what you describe is not a real issue for us.
- DanielHB 1y agoIs it ctrl+click takes you to the main implementation directly? If not it is reaaaaaallly annoying
- SillyUsername 1y agoEvery so often a developer challenges the status quo. Why should we do it like this, why is the D in SOLID so important when it causes pain? This is lack of experience showing. DI is absolutely not needed for small projects, but once you start building out larger projects the reason quickly becomes apparent. Containers... - Create proxies wrapping the objects, if you don't centralise construction management it becomes difficult. - Cross cutting concerns will be missed and need to be wired everywhere manually. - Manage objects life cycles, not just construction It also ensures you code to the interface. Concrete classes are bad, just watch what happens when a team mate decides they want to change your implementation to suit their own use cases, rather than a new implementation of the interface. Multiply that by 10x when in a stack. Once you realise the DI pain is for managing this (and not just allowing you to swap implementation, as is often the the poster boy), automating areas prone to manual bugs, and enforcing good practices, the reasons for using it should hopefully be obvious. :)
- timclark 1y agoThe D in SOLID is for dependency INVERSION not injection. Most dependency injection that I see in the wild completely misses this distinction. Inversion can promote good engineering practices, injection can be used to help with the inversion, but you don’t need to use it.
- SillyUsername 1y agoAgreed, and I conflated the two since I've been describing SOLID in ways other devs in my team would understand for years. Liskov substitution for example is an overkill way of saying don't create an implementation that throws an UnsupportedOperationException, instead break the interfaces up (Interface Segregation "I" in SOLID) and use the interface you need. Quoting the theory to junior devs instead just makes their eyes roll :D
- layer8 1y agoLSP is about much more than not throwing UnsupportedOperationException, that’s a complete mischaracterization. ISP isn’t about avoiding UnsupportedOperationException as well, it’s about reducing dependencies.
- ssijak 1y agoLanguage and/or library issue. DI helps code be easier to follow, more decoupled and read with less boilerplate AND helps testing much easier. If you are on node/ts look at effect-ts.
- ramon156 1y agoCan you show a project that effectively uses effect-ts? The docs is a tsunami of information that just looks to try to make a whole new language out of TS. If someone else had to review my code I doubt they knew what was going on
- Kinrany 1y agoOne thing that can motivate a dependency container is a complex chain of constructors.
- throwaway21371 1y ago> But that reflection-driven magic is also where the pain starts. As your graph grows, it gets harder to tell which constructor feeds which one. Some constructor take one parameter, some take three. There’s no single place you can glance at to understand the wiring. It’s all figured out inside the container at runtime. That's the whole point. Depdendency Inversions allows you to write part of the code in separation, without worrying about all the dependencies of each component you create and what creates what where. If your code is small enough that you can keep all the dependencies in your head at the same time and it doesn't slow you down much to pass them all around all the time - DI isn't worth it. If it becomes an issue - DI starts to shine. There are other solutions as well, obviously (mostly in the form of Object-Orientified global variables - for example you keep everything in GameWorld object and pass it everywhere).
- JyB 1y ago> doesn't slow you down much to pass them all around all the time - DI isn't worth it. You are confusing DI principles and using a "DI framework". Re-read the article.
- surgical_fire 1y agoDependency Injection has a fancy name that makes some developers uncomfortable, but it's really just all about making the code easier to test. Basically everything that the class depends upon has to be informed during construction. This can be done manually, but becomes a chore super fast - and will be a very annoying thing to maintain as soon as you change the constructor of something widely used in your project to accept a new parameter. Frameworks typically just streamline this process, and offers some flexibility at times - for example, when you happen to have different implementations of the same thing. I find it funny that people rally against those Frameworks so often. To make things more concrete, let's say you have a method that gets the current date, and has some logic there (for example, it checks if today is EOM to do something). In Java, you could do `Instance.now()` to do this. This will be a pain in the ass to test, you might need to test, for example a date when there's a DST change, or February 28th in a leap year, etc. With DI you can instead inject an `InstantSource` to your code, and on testing you can just mock the dependency to have a predictable date on each test.
- ffsm8 1y agoYou're talking from the perspective of Java, which has been designed from the ground up with dependency injection in mind. Dependency injection is the inversion of control pattern at the heart, which is something like oxygen to a Java dev. In other languages, these issues are solved differently. From my perspective as someone whoes day job has been roughly 60+% Java for over 10 yrs now... I think I agree with the central message of the article. Unless you're currently in Java world, you're probably better off without it. These patterns work and will on paper reduce complexity - but if comes at the cost of a massively increased mental overhead if you actually need to address a bug that touches more then a miniscule amount of code. /Edit: and I'd like to mention that the article actually only dislikes the frameworks, not the pattern itself
- mattmanser 1y agoDI wasn't around when Java (or .Net) came out. DI is a fairly new thing too, relatively speaking, like after ORMs and anonymous methods. Like after Java 7 I think. Or maybe 8? Not a Java person myself. I know in .net, it was only really the switch to .net core where it became an integral part of the frameworks. In MVC 5 you had to add a third party DI container. So how can it have been designed for it from the ground up? In fact, if you're saying 10 years, that's roughly when DI became popular. You're wrong about other languages not needing it, yes statically typed languagess need it for unit testing, but you don't seem to realize that from a practical perspective DI solves a lot of the problems around request lifetimes too. And from an architectural context it solves a lot of the problem of how to stop bad developers overly coupling their services. Before DI people often used static methods, so you'd have a real mess of heavily interdependent services. It can still happen now but.its nowhere near as bad as the mess of programming in the 2000s. DI helped reduce coupling and spaghetti code. DI also forces you to 'declare' your dependencies, so it's easy to see when a class has got out of control. Edit: I could keep on adding, but one final thing. Java and .Net are actually quite cumbersome to use DI, and Go is actually easier. Because Go has implicit interfaces, but older languages don't and it would really help reduce boiler plate DI code. A lot of interfaces in Java/C# only exist to allow DI tow work, and are otherwise a pointless waste of time/code.
- DanielHB 1y agoDI is a very religious concept, people hate it or love it. I myself am on the dislike camp, I have found that mocking modules (like you can with NodeJS testing frameworks) for tests gives most of the benefits with way less development hell. However you do need to be careful with the module boundaries (basically structure them as you would with DI) otherwise you can end up with a very messy testing system. The value of DI is also directly proportional to the size of the service being tested, DI went into decline as things became more micro-servicy with network-enforced module boundaries. People are just mocking external services in these kind of codebases instead of internal modules, which makes the boundaries easier. I can see strict DI still being useful in large monolith codebases worked by a lot of hands, if only to force people to structure their modules properly.
- grxar 1y agoDI frameworks add confusion and require more unnecessary memory in advance
- Traubenfuchs 1y agoDon't hate a paradigm because you only experienced one bad implementation of it. In IntelliJ, with the Spring Framework, you can have thorough tooling: You can inspect beans, their dependencies, you even get a visual bean graph, you can write mocks and test dependencies and don't even need interfaces anymore and if a dependency is missing, you will receive an IDE warning before runtime. I do not understand why people are so excited about a language and its frameworks where the wheel is still actively being reinvented in a worse way.
- KronisLV 1y agoIoC is nice (or DI as a concept in particular), but DI frameworks/libraries sometimes are a mess. I've had my fair share of Java and Spring Boot projects and it breaks in all sorts of stupid ways there, even things like the same exact code and runtime environment working in a container that's built locally, but not working when the "same" container is built on a CI server: https://blog.kronis.dev/blog/it-works-on-my-docker https://blog.kronis.dev/blog/it-works-on-my-docker Literally a case where Spring Boot DI just throws a hissy fit that you cannot easily track down, where I had to mess around with the @Lazy annotation (despite the configuration to permit that being explicitly turned on too) in over 100 places to resolve the issue, plus then when you try to inject a list of all classes that implement an interface with @Lazy it doesn't seem like their order is guaranteed either so your DefaultValidator needs to be tacked on to that list manually at the end. Sorry about the Java/Spring rant. It very much feels like the proper place for most DI is at compile time (like Dagger does for Java, seems closer to wire) not at runtime, or just keep IoC without a DI framework/library and having your code look a bit more like this: @Override public void run(final BackendConfiguration configuration, final Environment environment) throws IOException, TimeoutException { // Initialize our data stores mariaDBManager = new MariaDBManager(configuration, environment); redisManager = new RedisManager(configuration); rabbitMQManager = new RabbitMQManager(configuration); // Initialize our generic services keyValueService = new KeyValueService(redisManager); sessionService = new SessionService(keyValueService, configuration); queueService = new QueueService(rabbitMQManager); // Initialize services needed by resources accountService = new AccountService(mariaDBManager); accountBalanceService = new AccountBalanceService(mariaDBManager); auctionService = new AuctionService(mariaDBManager); auctionLotService = new AuctionLotService(mariaDBManager); auctionLotBidService = new AuctionLotBidService(mariaDBManager); // Initialize background processes based on feature configuration if (configuration.getApplicationConfiguration().getFeaturesConfiguration().isProcessBids()) { bidListener = new BidListener(queueService, auctionLotBidService, auctionLotService, accountBalanceService); try { bidListener.start(); logger.info("BidListener started"); } catch (IOException e) { logger.error("Error starting BidListener: {}", e.getMessage(), e); } } // Register resources based on feature configuration if (configuration.getApplicationConfiguration().getFeaturesConfiguration().isAccounts()) { environment.jersey().register(new AccountResource(accountService, accountBalanceService, sessionService, configuration)); } if (configuration.getApplicationConfiguration().getFeaturesConfiguration().isBids()) { environment.jersey().register(new AuctionResource( auctionService, auctionLotService, auctionLotBidService, sessionService, queueService)); } ... } Just a snippet of code from a Java Dropwizard example project, not all of its contents either, but should show that it's nothing impossibly difficult. Same principles apply to other languages and tech stacks, plus the above is unequivocally easier to put a breakpoint in and debug, vs some dynamic annotation or convention based mess. Overall, I agree with the article, even across multiple languages.
- chvid 1y agoYou probably don't need functional programming. Here is how to do it with a for-loop. You don't see many articles written like that because it kinda would be obvious that the author hasn't bothered to understand the approach that he is critizing. Yet when it comes to OO concepts people from "superior" platforms like Go or the FP crowd just cannot let go of airing their ignorance. Just leave OO alone unless you are genuinely interested in the approach.
- deleted 1y ago[deleted]
- ac130kz 1y agoDI is fine, if it is fully typed, and objects are explicitly initiated by the user, and the DI only does thread-safe dependency resolution.
- deleted 1y ago[deleted]
- hyperbolablabla 1y agoCommon sense is in short supply these days. It's a shame we need blog posts like these to outline how much you lose when you go with the "magic" approach. Devs just seem to be allergic to simple but verbose code.
- jaza 1y agoAs a (mainly) Python dev, I'm aware that there are DI frameworks out there, but personally I haven't to date used any of them. My favourite little hack for simple framework-less DI in Python these days looks something like this: # The code that we want to call def do_foo(sleep_func = None): _sleep_func = sleep_func if sleep_func is not None else time.sleep for _ in range(10): _sleep_func(1) # Calling it in non-test code # (we want it to actually take 10 seconds to run) def main(): do_foo() # Calling it in test code # (we want it to take mere milliseconds to run, but nevertheless we # want to test that it sleeps 10 times!) def test_do_foo(): mock_sleep_func = MagicMock() do_foo(sleep_func=mock_sleep_func) assert mock_sleep_func.call_count == 10
- deleted 1y ago[deleted]
- benhoyt 1y agoYeah, I use this sometimes too (even though Python makes "monkey patching" easy). However, note that it's simpler and clearer to use a default value for the argument: def do_foo(sleep=time.sleep): for _ in range(10): sleep(1)
- rybosome 1y agoI’ve used this exact pattern. However, in Python I prefer to use true DI. I mostly like Injector[0] because it’s lightweight and more like a set of primitives than an actual framework. Very easy to build functionality on top of and reuse - I have one set of modules that can be loaded for an API server, CLI, integration tests, offline workers, etc. That said, I have a few problems with it - 2 features which I feel are bare minimum required, and one that isn’t there but could be powerful. You can’t provide async dependencies natively, which is not usable in 2025 - and it’s keyed purely on type, so if you want to return different string instances with some additional key you need a wrapper type. Between these problems and missing features (Pydantic-like eval time validation of wiring graphs) I really want to write my own library. However, as a testament to the flexibility of Injector, I could implement all 3 of these things as a layer on top without modifying its code directly. [0]: https://pypi.org/project/injector/ https://pypi.org/project/injector/
- deleted 1y ago[deleted]
- deleted 1y ago[deleted]
- kazinator 1y agoThe system is composed of classes which are nicely encapsulated, independent and obey Liskov substitution and all that. You can connect them in different arrangements and they play along nicely. But then some classes which use other classes hard code those classes in their constructor. They then work with those specific hard-coded classes. It's like if someone crazy-glued some of our Lego blocks together. We recognize this problem and allow the sister objects to be configurable. Then some opinionated nubmnut comes along and says, "hey, we should call this simple correction 'dependency injection'". And somehow, everyone listens.
- latchkey 1y agoI've written a couple large apps using Uber's FX and it was great. The reason why it worked so well was that it forced me to organize my code in such a way as to make it super easy to test. It also had a few features around startup/shutdown and the concept of "services" and "logging" that are extremely convenient in an app that runs from systemd. All of the complexity boils down to the fact that you have to remember to register your services before you can use them. If you forget, the stack trace is pretty hard to debug. Given that you're already deep into FX, it becomes pretty natural to remember this. That said, I'd say that if you don't care about unit tests or you are good enough about always writing code that already takes things in constructors, you probably don't need this.
- wiseowise 1y agoThe article can be summarized by: “I’m using language that is stuck in 80s”. Java and Dagger 2 have solved the DI years ago. Fast, compile time safe and easy to use.
- JyB 1y ago[flagged]
- jillesvangurp 1y agoSeparating your glue code from your business logic is a good idea for several reasons. That's all dependency injection, or inversion of control is. It's more of a design pattern than a framework thing. And structuring your code right means that things are a bit easier to test and understand as well (those two things go hand in hand). Works in C, Rust, Kotlin, Javascript, Java, Ruby, Python, Scala, Php, etc. The language doesn't really matter. Glue code needs to be separate from whatever the code does. Some languages seem to naturally invite people to do the wrong thing. Javascript is a great example of this that seems to bring out the worst in people. Many of the people wielding that aren't very experienced and when they routinely initialize random crap in the middle of their business logic executed asynchronously via some event as a side effect of a butterfly stirring its wings on the other side of the planet, you end up with the typical flaky untestable, and unholy mess that is the typical Javascript code base. Exaggerating a bit here of course but I've seen some epically bad code and much of that was junior Javascript developers being more than a little bit clueless on this front. Doing DI isn't that hard. Just don't initialize stuff in places that do useful things. No exceptions. Unfortunately, it's hard to fix in a code base that violates that rule. Because you first have to untangle the whole spaghetti ball before you can begin beating some sense into it. The bigger the code base, the more likely it is that it's just easier to just burn it down to the ground and starting from scratch. Do it right and your code might still be actively maintained a decade or more in the future. Do it wrong and your code will probably be unceremoniously deleted by the next person that inherits your mess.
- rco8786 1y agoWhat you’re describing is generally good coding practice. But not, I don’t think, what people associate with DI.
- jillesvangurp 1y agoI think people's associations might be wrong then. In general, people seem to have a lot of misconceptions about DI. Like needing frameworks. Basically by inverting control of what initializes code, you create a hard separation between glue code and logic. Any logic that initializes code would violate that principle. You inject your dependencies because you are not allowed to create them yourself. And yes, that is good coding practice. That kind of was my point.
- shadowgovt 1y agoThis will vary from firm to firm depending on what you're writing, but I generally find DI to be more complexity than needed. Granted, - I'm willing to rewrite some code if we decide that a core library needs to get swapped out - I'm always using languages that allow monkey-patching, so I'm not struggling to test my code because, for example, it's hard to substitute a mock implementation for `Date.now()`. DI makes more sense if you're not in that position. But in that position, DI adds "you need these three files in your brain at the same time to really understand what's going on" complexity that I seek to avoid. (Also, DI overlaps with generics, and to the extent that you can make things generic and it makes sense to do so, you should).
- jongjong 1y agoI don't like DI as a concept because it typically obscures the path/source of the file where relevant code is located. DI trades off long term readability for short term implementation convenience. Maintainability requires strict adherence and awareness of conventions which it something that many developers are terrible with.
- therealdrag0 1y agoAs someone who was raised in the religion of Java and Spring and SpringBoot (many years over many companies). It was a revelation to work on micro-services that didn’t use a DI framework. I’m now thoroughly against them.
- frollogaston 1y agoRules are different with microservices, like globals can be ok. But even in large projects with big services, idk what problem these frameworks are trying to solve. I've never felt the slightest need to introduce some metaprogramming to get dependencies where they need to be.
- frollogaston 1y agoI'm ok with "dependency injection" being confused with "dependency injection framework," cause it's silly to have a name for the first thing. Might as well call it "parameter injection" when I call a function, and "memory carburation" when I instantiate a variable.
- exabrial 1y agoOf the countless pro's for Dependency Injection, it allows for near perfect test isolation, among many other benefits. Test the code under test, mock the rest. Not not mention limitless composition instead of inheritance. The real con of Dependency Injection = {Developer Egos + ( Boredom | Lax Deadlines ) Lack of senior oversight} which inevitably yields needless overengineering.
- afsdef3f3wafew 1y ago[dead]
- danpalmer 1y ago"Dependency injection is too complicated, look at this one straight-line implementation" is not exactly a fair argument. The whole point of DI is that when you can't just write that straight-line implementation it becomes easier, not harder. What if I've got 20 different handlers, each of which need 5-10 dependencies out of 30+, and which run in 4 different environments, using 2 different implementations. Now I've got hundreds of conditionals all of which need to line up perfectly across my codebase (and which also don't get compile time checks for branch coverage). I work with DI at work and it's pretty much a necessity. I work without it on a medium sized hobby project at home and it's probably one of the things I'd most like to add there.
- JyB 1y agoYou are missing the point the post is about Golang specifically.
- jjice 1y agoBut can’t you do your DI by hand? The frameworks can really become absurd in their complexity for a lot of tasks (I’m sure they make sense in many situations). DI is a concept that can be orchestrated with normal code at the top of the execution just fine, eliminating a lot of cruft. To be fair, the numbers you h the row out sound like a framework becomes valuable, but most places I’ve seen DI frameworks, they could be replaced with manual DI and it would be much simpler.
- danpalmer 1y agoWhen you say "do your DI by hand", do you mean wiring up each case, or do you mean writing a DI system that treats the cases generically? The former is what I'm suggesting becomes untenable at some point. The latter is just writing your own DI framework for, which I think would be fine. DI frameworks are complicated, but they're a constant level of complicated, they don't get more complicated as the codebase grows. Not using a DI framework is simple at the beginning, but it grows, possibly exponentially, and at some point crosses the line of constant complexity from a DI framework. Finding where those lines intersect is just good engineering. Ignoring the fact that they do intersect is not.
- dboreham 1y agoDI is a confusing fancy name for "global variables".
- boxed 1y agoOr "function arguments". Either way it's a stupid name.
- Fire-Dragon-DoL 1y agoI noticed usually DI is not necessary at runtime, but rather at compile (or boot) time. In practice I noticed I'm ok with direct dependency as long as I can change the implementation with a compile time variable. For the tests, I use an alternative implementation, for development another. I don't swap an implementation for another within the same code. It is an option, but it happens so rarely that it seems absurd optimizing for it. So, I like dependency injection as a concept, but I avoid it to reduce complexity. The advantage is that you can get by with a lot more "global" code. In Go this is particularly nice since DI is really nasty (struct methods have various limitations)
- pinoy420 1y ago[dead]
- fifticon 1y agoThough my daily work involves plenty of DI, and I see the need for it, I see some unfortunate side-effects, in the behaviours it 'causes'. - the 'autopilot GPS' problem: Colleagues who basically have no idea how things fit together, because DI connects the dots for them. So they end up with either a vague or no mental model of that happens below the surface. - the same, but related to scope and costs: Costs: Because they don't touch what is 'built behind the scenes', they get no sense of how costly it is ('every time you do use that thing, it instantiates and throws away a million things'). Scope: Often business logic dictates that the construction hierarchy consists of finely tuned parts: You don't just need an instance of 'Foo', you need a Foo instance that originates from a specific / certain request. And if you then use two Bar's together, where Bar 1 is tied to Foo 1 but Bar 2 is tied to Foo 2, you will get strange spurious errors (think, for example, ORMS and database transactions - the two Foos or Bars may relate to different connections, transactions or cursors.) One antipattern I have seen (which may actually be an argument FOR DI..), is the 'query everything' service, which incorporates 117 other sub-services. And some of the junior developers just love that service, because "then I can query everything, from a single place!" (yes.. but you just connected to 4 databases with 7 connections, and you are only trying to select a single row from one of them. And again, code with the everything-service becomes quite untestable). The best part of the article is its advice for triggering the broken dependencies at compile-time, I really hate when I have to go through complicated flow #127 to learn that a dependency is broken.
- fallingfrog 1y agoThe basic pattern of not having one object construct some other object that has external references to it is.. kind of obvious. I didn't know there was a name for it but sure, I agree, fine. But the way DI is usually implemented is with this bag of global variables which you just reach in and grab the first object of the desired type. I call this the little jack horner pattern. Stick in your thumb and pull out a plum. That, is stupid. You've reinvented global variables, but actually worse. Congratulations.
- deleted 1y ago[deleted]
- danzero 1y ago[dead]