The definitions of "framework" are always a sticky issue. If you take "framework" at face value, it's minimally a set of idioms. "an essential supporting structure which other things are built on top of" - which necessarily includes a mental model of execution. This relates closely to why naming, in software development, is considered so difficult.
All software has an inherent framework. First is the mental model and then the implementation. Implementation middleware to simplify utilizing these idioms or enforce them, are incidental. If you're lucky, you can derive some of the underlying design from documentation.
Ironically, those who claim that they never use a framework are largely making implicit claims about what they consider a framework to be or have.
Not saying that I could write code directly in octal... but I was pretty darn close. Of course, it helps when your assembly language consists of just eight instructions.
Hence why the article defines what it means by framework, and by the definition in the article, software rarely has any implied frameworks within as one of the definition points is that there is code in the framework the user is not free to change.
There certainly can be frameworks emerging within large projects, but by the definition in the article they're not problematic because they don't lock you in: You can change those pieces. You in particular ultimately remain in charge of the flow of control in the code. Those "frameworks" don't take away control and put you in a straightjacket.
The article could perhaps have been framed better as "how to write a framework or framework-like thing which doesn't become a problem". Some thoughts might be:
* Structure it as decoupled components where you can opt in and opt of different elements, including of control flow. E.g. Padrino - a Ruby web framework fits well here. You can pick and choose which ORM to use; you can opt in to a router, controller, admin interface, mailers, logging, caching, and view libraries, or not use them at all. You can start with plain Sinatra and add in bits of Padrino as and when you choose, or supply your own.
* In particular, if possible, externalise the control of flow into replaceable components. Doing this alone can often be enough to get you out of the straight-jacket of "frameworks as defined by the article"
* Allow users to override/replace all components rather by defining clear interfaces and not assuming you can call hard-code calls between the different components. Padrino again fits well there. So did e.g. Qmail treated as a "framework for building a mail server" ("nobody" used pure Qmail, but Qmail defined the interfaces between each individual program which made up the full system, so you could replace every single bit step by step). In particular there should be no "magic" glue which there isn't a well defined way of tearing out.
I might have agreed with this at one time. A dedicated "framework" has documentation and additional software to shortcut assumptions, but a coherent design isn't different enough from the rules that guide any given framework to be a distinction. The biggest differences are how mature the frameworks are in breadth of documentation, guidance in how to achieve constrained goals (there is no "everything" framework), and supporting software to shortcut boilerplate.
That's not what people commonly understand as a "framework". Nor is it a helpful definition because then where does "abstracting things" and "framework" start and end?
No, the main difference between frameworks and what you describe is the IOC. In the framework world, the framework calls you and then something happens behind the scenes until the framework decides to call some code that you provided via the frameworks API.
With libraries it's the other way around: you call the library and then you do something with the result and then use it to call the library again and so on until you have the desired outcome.
The difference is, for example, that you can call the library and when it returns something to you, you can inspect it and choose to ignore it. You can do so in an arbitrary way. In the framework case, if the framework does not provide any way for you to interact with it in the way you want, then you are screwed.
Heck, it's not even true of some of those specifically called out in the article above. For example, I've written programs that use Rails as a library - or rather, suite of libraries - of functions and classes to be called, but anyone suggesting that Rails is somehow "not a framework", on the flimsy grounds that it did not dictate the flow of control, is gonna get some funny looks at morning standup.
But the greater problem is that narrow, mechanical definitions omit purpose, and talking about software without purpose being front-and-center becomes rapidly theological. If you're wondering what I mean by that, look up the wikipedia article for "Software framework", scroll through its desultory talk page, and recognize the inevitability of the boilerplate it got stuck with: "This section is written like a personal reflection, personal essay, or argumentative essay that states a Wikipedia editor's personal feelings".
Some parts (ActiveSupport, the part that simplifies e.g. date calculations) are clearly libraries. They can be used outside of Rails as well, because they totally ignore the shape of your other code.
Other parts (ActionPack, the component that calls your code depending on the incoming web request) are working like a framework. Your code must adhere to their requirements, otherwise it won‘t work.
When you extend this pure/impure idea all the way throughout the computing eco-system, it quickly becomes apparent that something went wrong a long time ago, and every-time, instead of untangling the mess that is pure and impure into their own buckets, we find a way to run a framework in a framework.
What is an Operating System? What is a container? What is k8s? What is a Programming Language? What is a browser? What is a website? What is next?
Also, it is certainly possible to have a mix of a library and a framework where parts use IOC and others don't. Maybe that's try for Rails (never used it) and that's where your confusion comes from?
One of the most useful definitions I found was from Luciano Ramalho, the author of Fluent Python (and so many other cool things). According to his insight, a framework is something that calls your code. A library is something your code calls. Frameworks are more rigid things that force structure on your functionality. If the structure it imposes is good, then your code will be good.
Their scope is also tailored to the languages they support. As you pointed out, different languages will have different needs.
But there's a large gray zone between these two, and eventually the distinction is also often quite pointless in the real world.
> A better definition might be that a framework imposes an entire 'application model' on you, and trying to step outside that predefined model is at your own risk
This is the kind of definition were 50% will call something a framework and the other half will call it a library then. Not sure if that is really helpful.
Callback hell however, is not something I'd associate with a framwork
It doesn’t. If you abstract things enough there is your framework.
The only things I’ve seen that were truly without a framework were raw php files that did everything contained within that single file. And even these had headers that combined some common functionality.
The C standard library is exactly that though, a framework for writing UNIX command line tools :)
(for anything else it's much less useful)
A framework is either about inversion of control (it calls your code, e.g. you write web request controller logic, and it sets up the infrastructure for calling your controllers), and/or a set of ways to structure your code, like classes to extend, folder structure, a set of patterns you must follow to work with it, like a specific MVC-like organization of code, and so on.
Ok, I'm throwing together the CRT and standard lib here, but in the end both are parts of the "UNIX command line tools framework" which is supposed to provide a programming environment to easily extend the UNIX shell with your own commands.
Similarly, "libc is the same as a framework" is not an abstraction/assumption at any meaningful level, or really pertinent to the discussion.
I don't think you're even being reductive; it's just irrelevant. The saying is to do with the software you're writing right now - do you include a framework (e.g. Django, Ruby on Rails, Spring) or do you include libraries that you could swap out (e.g. Flask - which terms itself a "micro framework", but is a library for this definition, ReactJS, SQLAlchemy) and your code is in control. You're right that the code will run on one or more operating systems, but abstracting out that far elides the useful conversation application developers can have about eggs and baskets.
> the code you write is actually a pretty small percentage of the total code that makes up your system/application
This is almost always true, but seems again irrelevant. How I write my application is worthy of discussion in and of itself, if for no other reason than, with a few exceptions, I'll spend a lot more time and money on making my application than I will thinking about how the Linux kernel works.
I'd say by this definition (your code calls a library, a framework calls your code), Flask is every bit as much a framework as Django; I write functions that are called by the router just like I do for Django views. Sure, I can swap out the ORM and the templating library; I might even be able to swap out the routing library, but here be dragons. But a standard Flask application and a standard Django application are very similar. And if I wrote an application using Flask, it would be more or less a rewrite to swap in something else for Flask, because I'd be depending on Flask's request and response objects, at minimum.
This is partly why I don't think this definition is very useful, too. Clearly Django and Flask are different, but this isn't why.
The arguments here are:
"With libraries you call the code; with frameworks they call your code"
"This isn't a good distinction because there are lots of things we wouldn't consider frameworks that call your code"
That seems relevant to me.
> I'll spend a lot more time and money on making my application than I will thinking about how the Linux kernel works.
That means the kernel is doing its job, and if you swap out "Linux kernel" with "Django", that would also mean Django is doing its job. Without the kernel, you'd have to do a lot more work talking to hardware and scheduling other processes to run. Without Django, you'd have to do a lot more work wrangling HTTP and SQL.
It feels like peoples' issue with frameworks are that they force a way of thinking on you. But everything in software does that, the semantics of processes, pages, threads and so on are entirely made up, just like model view controller is.
Which is neither here, nor there.
We're talking about a specific kind of inversion of control and forced structure within the codebase.
Else we could just go down an irrelevant rabbit hole, and it would like being a lawyer in a criminal case and arguing that your client is innocent, because "ultimately everything, including the murder, is just deterministic after-effects caused by the Big Bang".
Like, the way you use Django is by wiring up URLs to functions [0]. This is pretty similar to setting up a vector table for interrupt handlers. Is that a framework? What makes Django a framework and vector tables not a framework?
I'm not trying to be pedantic here, I just really couldn't tell you the difference, and as a result, I'm not sure the distinction is useful. It sounds more like a post-hoc rationalization of why you like or don't like something.
[0]: https://docs.djangoproject.com/en/4.1/intro/tutorial01/#writ...
It doesn't fit the term as used in the article at all, though, in that it doesn't dictate the overall program's flow of control.
shell => CRT => main(argc, argv) => exit code => shell
(to me, the CRT and C standard lib are not strictly separate, although that's debatable of course)EDIT: To expand on this: You can call the code from stuff other than a shell; I'm assuming you mean "C runtime" by CRT, and some libc's may have issues without them, though usually only access to argc/argv and atexit(), and you can certainly write C without linking with the initialisation code; and while you'll of course usually enter via main() you don't have to. You decide the control flow.
Nothing stops you from e.g. providing your own startup code which then calls the appropriate initialisation code either.
The point being that there's no enforced inversion of control in the sense used by the article, and so calling it a framework in the sense described by the article is meaningless.
Even if we postulated the inversion of control was there, the fact you can replace all parts of it still means it does not fit the article definition.
This is also massively shifting goal posts. You first wrote "At which point it is no longer C". By your argument here, e.g. the Linux kernel is not written predominantly in C. But nobody uses the term that way. If you were to speak specifically about ISO C, maybe, but the person I replied to initially did not limit it to ISO C.
As such this is also entirely irrelevant to the original argument.
If you're willing to extend a little, there are also signal handlers, or setting up callbacks in pthreads.
Again this isn't to be reductive, but to say this is a super common method of encapsulating irrelevant stuff. It doesn't seem to be a significant differentiation between "I'm a library" and "I'm a framework".
As TFA says, one of the main problems with frameworks is that the maintainers of the framework don't share your organisation's goals, and therefore will have different problems to solve.
For example, in an overall solution for some snowflake business challenge you may see components such as an order taking system, a customer support center, a CRM, an ERP—and on another level patterns like a statically rendered site, an SPA, a “classic” content-driven site, and so on. In my opinion, being blind to these opportunities and intent on putting the entire solution together without any delegation of control flow smells of lazy architectural thinking and/or job security. The exact decomposition would depend on business specifics, foreseeable future evolution of the business, team capabilities and other factors, but as a result you’d be able to radically lighten the implementation and indeed maintenance burden with a strategic use of frameworks or even lightly customized CMS.
SAP is the classic example of this: SAP implements vanilla business processes (with lots of flexibility built in). Yet every single business implementing SAP needs extensive customisation and modification to make it fit their business processes. Some have spent millions or tens of millions trying to make SAP fit their business and failed.
In your terms; every business is a snowflake. You cannot create (for example) an order taking system that will fit all businesses. Or even "all businesses for whom order taking is not part of their core business domain". Only "those businesses who are willing and able to make their order-taking process fit your system".
It’s a spectrum: a CMS may be more fiddly as to customization but more reliable if your needs and extension points fit, framework offers more freedom, and a bespoke combination of libraries generally forces you to implement and integration test more of control flow and imposes higher costs of maintaining proper documentation as to how everything fits together (to avoid the next engineer accidentally killing performance due to a misunderstanding of how it’s supposed to work).
On the other hand, CMS and frameworks can actually inform your decisions, as their engineers had faced tasks similar to yours time and time again.
It is sometimes possible to persuade the business that if they create the process in a way that matches the framework it will make life easier for everyone. But often that isn't possible, and often there are good reasons for that.
I think it’s often possible to identify the truly inflexible/snowflake aspects the business and still make use of frameworks/CMS. For a tech-focused company there are possibly more cases in which building your own is a viable decision, but with other companies that don’t have strong technical brains so to speak “this will cost you more to develop and more difficult to hire for later” is something they would understand…
Any business that tries to make SAP fit their needs will fail to some degree (usually to a large degree, and expensively).
SAP will tell you in a heavy German accent that you bought the perfect business practices from them, and if you want to be successful, you VILL make your business fit SAP, not the other way around. In reality, they're right - All the really successful SAP installations I've ever seen had the company tossing all of their existing processes and replacing them wholesale with the ones built into SAP. Trying to make SAP fit foreign practices is a losing deal.
:s/framework/SAP/g
The interesting thing to me is that this is how Django, one of the canonical big backend frameworks, came into being. It was based on what a newspaper's website's staff were actually using in practice.
I actually have written software without a framework, so by this standard I feel qualified to comment.
Not all software has an implied framework within it. Most Unix command-line utilities do not, for example, with only a few exceptions, and discounting the C standard library as something worthy of the label “framework”. Nevertheless even they exist within an overall architecture that starts to look like one, if you squint, and realise the enormous power of piping stdout around.
However, many line-of-business applications do develop sufficient complexity, as the owner/operator adds new features to support their expanding operations, that the subsequent edifice either accretes random code into a horrifying fatberg of unmaintainability, or some wiser developer refactors substantial parts of the interface, business logic, persistence, and runtime configuration, effectively creating a framework within that application on which the logic and interface and so forth subsequently hang, and with a pinch of skill and luck, in a fashion that is more readily reasoned about and amended.
Whether that informally-specified framework might be considered worthy of extraction into a unit of software for itself is another matter, and largely a case of programmer hubris.