For any non-trivial program you need to locate bits of data and code that aren’t . In a “normal” program it’s up to you to work out how to do this. Short programs generally use module level globals to find class definitions (i.e. import statements) and something homegrown for locating data – it might be a handle to a database, or some config file that specifies a directory containing some files or whatever. So far so obvious.
In a framework, however, it all gets far more complex. Your execution environment is expected to be much larger, and there may be many contributors of components. You may wish to switch components without editing code, ideally even at runtime.
To do this you need a mechanism for locating “things”. This is “context”, or at least the part of it generally addressed outside the normal language features. Context is probably the real distinction between a framework and a library (something that has been controversial). When you use libraries you provide your own context – when you use a framework, the framework provides the context.
As an example, imagine you want to provide a form to create a new user for your application. This seemingly simple operation is the kind of thing that gives frameworks a bad name — but this is one of those areas where there is real complexity in the problem space and all abstractions just serve to make the problem less distinct and the solution more baroque.
Just to register a user there are a huge number of knobs any self-respecting framework wants to provide independently of code that uses it:
- What does the form look like?
- What fields does it contain?
- What are the validation rules for the fields?
- Where are the users stored?
- What properties does the user have?
- What happens once you submit the form?
Those who complain about frameworks, I suspect, are people who see all of this and just groan at the unnecessary amount of code that is seemingly unrelated to anything to do with adding a user. Almost all the code involved in solving this generically is logistical stuff – just locating all the things and orchestrating them together is a significant undertaking. Superficially, good solutions to these points are more complicated than the overall problem itself.
Each of the questions above is solved within frameworks using some sort of context. How well components developed separately are capable of orchestration is a key part of a framework’s success or failure.
The benefit in providing generic solutions is a “Total Cost of Ownership” one — over the lifetime of an application, you really do want to vary these things and to be able to do it independently of the codebase, and therefore with less testing, is a massive advantage. We have a number of real-life applications that have been running for many years where we have, for example, switched from ZODB to OpenLDAP to MySQL as a user source without (in theory) needing to change any code.
It’s a bit like the old saw “Any sufficiently complicated C or Fortran program contains an ad-hoc, informally-specified bug-ridden slow implementation of half of Common Lisp.” – any large program that aspires to long-term maintainability will eventually contain an ad-hoc, informally specified bug-ridden slow implementation of a framework.