Why I hate badly designed frameworks with unnecessary abstractions.
Why I hate badly designed frameworks with unnecessary abstractions.
I teach a Java web dev course that gradually works up from the servlet API through a bunch of different abstractions. I keep a drawn representation of the current stack at all points of the course, and keep a count of the lines of code required to do any particular thing. At the beginning, we need about 10 lines of code to process a basic GET request, and there are lots of explicit dependencies (inheritance, dependencies on HttpServletRequest etc.) and at the end, there are maybe two or three lines of code, and no explicit coded dependencies (several implicit, provided by the framework).
Once you add in a few pieces of actual work (DAOs, collections, CRUD functionality, database connection pools, query abstraction), the code is an order of magnitude simpler when you use a framework than when you don't.
It boils down to this: Using a smaller technology stack makes for an easier learning curve, but your day job after learning the basics involves ten times as much code ( == ten times as many opportunities for bugs, reinventing the wheel, antipatterns etc.), but investing in how to use a larger stack means your day job becomes much, much easier; you write less code, you focus more on your business logic, and you leave the dependency management and plumbing to the framework.
Sure, you might have to use the word "Factory" a few times, but that means you don't have to write the code to implement your object pools and collection handlers. That's already done for you.
This has been my experience anyway. Even recently, I've had this kind of problem (not to name any names, but socket.io), but only because what I'm working on is somewhat more specialized than in the past.
> Using a smaller technology stack makes for an easier learning curve, but your day job after learning the basics involves ten times as much code ( == ten times as many opportunities for bugs...
I think you're implying here that not having written the framework abstractions yourself or in-house will reduce bug count. First, to be a bit pedantic, the opportunities for bugs is roughly the same over a given feature set between coded-with-framework and written-from-scratch. The the number of discovered and undiscovered outstanding bugs over that same feature set may be lower, but this doesn't necessarily reduce risk.
By using a framework you're making the assumption that most major bugs have been found and fixed by the sheer volume of people testing the framework in the wild. In a general sense, this is often a valid assumption. However this is in no way a guarantee of good testing coverage.
In my day job we decided to make further use of an open source tool to satisfy a particular security requirement. The tool in question has a community of nearly ten thousand users. However, it turns out that the advertised feature we wished to use was never really adopted by anyone in the community. Because of this we found a major "barrier to entry" type bug that had been outstanding for close to four years, and we lost nearly a month before deciding to scrap the effort and use something else.
Beyond bug count, it's been my experience that when framework bugs do crop up they're more likely to be very costly or time consuming. I say this for a few reasons. First, let's make two fairly reasonable (I hope) assumptions:
Assumption #1: Most people use frameworks to avoid having to worry about the details for which the framework offers a solution. This usually is the main value proposition of a framework (and the quote above alludes to this). This means that when you plan and design your software, you typically assume that the framework is correct. Most people don't budget time/expense for learning the framework's internals.
Assumption #2: If the framework is battle-hardened, most of the bugs you'll probably find are in oddball edge cases and/or are very difficult to track down.
In the best scenario your framework code is open and you can fix the bug yourself if need be. However you pay for this this with the original perceived value of the framework (no need to be an expert in its inner workings), and a lot of unbudgeted time. And let's face it; when we're in a time crunch, we don't do our best work. The probability of introducing new bugs when this occurs is quite high.
Aside from the specific application details that you're now worrying about, you now have new unplanned-for operational overhead. How do you maintain the change to correct the bug? You'll either need to figure out the correct procedures/coding style/etc to submit a patch/pull-request, or you'll need to maintain your own fork of the framework.
Or in the other case where you're working with a closed-source product you're just up a creek. Oh you paid for support? Isn't that cute...
I don't say any of this to attack frameworks. I use them myself quite often. However I do say this to attack the idea that frameworks make it safe for developers to remain ignorant to the inner workings of their system, regardless of which parts were designed/built in-house, as the fact that something is made and supported by a third party, commercial or community, has no bearing on its quality alone.
The issue I have with frameworks is that the Factories tend to be too tightly coupled to the problems that the original dev or dev team saw, and they aren't flexible enough for me to re-imagine their purpose. They are a perfectly abstracted a solution to a concrete problem, and implementing their [collection|object pool|routing] "API" will only let you change the steps in the process that they thought needed changing.
Factories are a fixed-arity solution to an infinite arity problem. I can't recompose factories like I can functions (in a language with first class functions). Not to mention that the APIs created to marshal data between the objects created by these factories tend to be poorly thought out because, let's face it, we're all monkeys banging out code and none of us can think at the proper level of abstraction to consider everything. But instead of using simple data types and "assume-nothing" interfaces, they try to create an abstract interface with a sample size of one problem. That's never going to work.
Enterprise Frameworks typically say factory when they mean "configuration". And when they say pluggable there should be a footnote that says *with any of our handful of implementations, don't try to write one yourself. They're very good training wheels, but they aren't the panacea to everyone's problems. Use them when they make things easier, ditch them when they get in the way. Fix them when you have the time ;-)