All Frameworks Sucks, But…

— The original post was written on Oct 24, 2015. —

Morpheus: Frameworks are everywhere, they are all around us. Even now in this very room. You can see them when you look out your window or when you login to Reddit. You can feel them when you go to work, when you go to church, when you pay your taxes. They are the world that has been pulled over our eyes to blind us from the truth.

You: What truth?

Morpheus: That you are a slave, Newbee. Like everyone else you were born into bondage. Into a prison that you cannot taste or see or touch. A prison for your mind.

A while back I watched this video of Rasmus Lerdorf Q&A session. In it, he was asked: what do you think about frameworks? watch his answer here and read on once you’ve finished watching:

We are all familiar with the framework wars. The PHP community is no exception to this cluster-fuck of a phenomenon. On daily basis you see arguments all over the web regarding what frameworks people should use and why X is better than Y.

Laravel devs call themselves artisans, because you know… the Laravel developer community are descendants of Da-Vinci himself. Zend devs are apparently working with a framework so stable that you could use it as a Mars rover. And let us not forget the hipsters who create their own “MVC” frameworks without even knowing what MVC stands for. The landscape is filled with bajillions of junk frameworks for you to choose from. So let us be honest about this for a second. If any given framework was good enough to solve for the code standardization issue, we would not need thousands upon thousands of them.

ALL FRAMEWORKS SUCK and here is why:

1. General Purpose Frameworks:

To add to Rasmus’ point from the video above: The vast majority of the framework landscape, especially within the PHP community, is centered around the notion of general purposeness in order to attract the largest possible audience to the project. We have seen the same sort of framework rebuilt over and over again and again by one vendor after the other. Almost all modern frameworks out there, especially the popular ones such as Laravel and Zend, are built with the following assumptions:

  1. The developer will want their code structured in a Triad or Quad model.
  2. It is better to embed tools that allows for bootstrapping (the internal application workflow) and object generation into the framework itself [see IOC containers as an example of this].
  3. The size of the framework’s code-base is completely irrelevant.
  4. Dependencies are a wonderful thing. As awesome as Composer is, it has brought along a culture of “include everything because it is easy to do so”. If one in 10 000 developers use this feature, then it is worth including it as a dependency.
  5. If the developer wants to do things differently than what is assumed by these core assumptions, then he/she should be able to deviate and change things as he/she sees fit.

In other words, most popular framework makers are trying to create a set of tools that can reach the largest possible audience. They do not care about vertical problem solving, they care about horizontal expansion.

What we need right now are more domain specific frameworks. We could call them vertical frameworks or whatever. The name doesn’t really matter. What matters is that we need to start thinking about designing frameworks to solve specific problems rather than just providing a bulk of code that attempts to solve every generic problem that any given developer might have.

Check this out:

2. Migration:

This right here. Oh god…

Have you ever tried migrating a code base from say Laravel to Zend (or frameworkless for that matter) after using all the “facades” and internally built-in framework functionalities? how was that process? I know that every time I try to embark on such a journey, suicidal thoughts find their way into my head. Time after time I find myself consulting for companies that have performance and resource related problems where the vast majority of these issues are created by lazy development due to the over-reliance on internal framework functionality. For being a “modular”, “component based” and “ease to use framework”, it is surprisingly hard to migrate from any given framework in search for better performance.

IMO, a framework should by default empower you to migrate from it at any point. The framework should not implicitly or explicitly tightly couple your code to the framework itself. You could couple your code to an external package/component and use composer to migrate that over. But having the code tightly coupled with the framework itself is just flat out bad design. Offering the tools to encourage people to do just that is even worse. It might be “powerful” (whatever that now means) but it doesn’t change the fact that it is bad practice. Making such a huge commitment in an early stage of the project’s life cycle without the ability to migrate over is one of the worst things a software company can possibly do.

If re-writing the application is easier than migrating out, then something is seriously wrong. Especially when it comes to ORMs. Wait until you have to deal with migrating from one ORM to another. Yea… I am never doing that again!

3. Inter-dependencies and Documentation:
One of the most annoying things about frameworks in general is inter-dependencies. Some frameworks rely on external components/libraries in order to function. Encountering an error or a bug in one of those components will lead you down a road of wild goose chase. First you have to figure out why the framework isn’t returning the expected result, then you need to figure out why the component it relies on isn’t behaving as expected. And once you have figure all that out you need to create a local fix that you later on push into the component as well as the framework’s repository (provided that the managers of those repos actually give a damn).

This is just flat out annoying. But… “That might happen to some indie-developed framework. The major ones have no such issues” I hear you say. Oh sure thing

Please note that I am not saying that we should not use external dependencies. What I am saying is using the bare minimum should be the policy here. Including things just for the sake of “coolness” is not good practice.

Conclusion:

Rasmus was 100% right on the money. What we currently need more of is lightweight, simple and domain specific frameworks/tools that allow us to tackle certain problems with ease. We should stop focusing on creating these huge frameworks that embed unnecessary tools into their core offering. Inter-dependencies are absolutely fine. However, as a rule of thumb, they should be kept to a minimum. Framework size matters and so does performance and the size of the callstack for that matter. Most importantly it is important to enable and enhance the developer’s ability to debug and fix potentially unexpected problems as well as migrating FROM your framework to something else if need be.

2 comments

  1. Ultimately a lot of this article is based on expectations – and the conclusions depend on whether the expectations are relevant.

    If you expect widely used open-source software to be bug free then you will be disappointed – but then again if you expect your own home grown software with only yourself as a user to be bug free you will be even more disappointed.

    If you are expecting e.g. Laravel and Zend to be source code compatible, then you are suggesting that they both conform to some sort of Framework standard. This would be a GREAT idea if it could ever be made to happen, but I doubt that it ever will. But if you are looking to migrate away from Laravel, then in broad-brush terms Laravel is kinda like some other frameworks which have ORMs etc. – though you would have to select the technology and framework for its Laravel compatibility if you wanted to switch away rather than pick a different PHP framework (like Zend) which may not be a close match. But I would hazard a guess that if you picked another closely matching framework written in a different but widely used language (say Python or Rust) then you might well find an AI tool that could do a passable job at migrating the code for you to start fixing up.

    In reality, we all have to make a choice about technologies at the outset of our projects.

    If you are aiming for a site with billions of simultaneous users each doing something simple but using a shed-load of data, then probably no framework is ever going to be right for you – you need to use a very high performance database solution (like NoSQL) and design the solution to be both horizontally scalable and highly performant in its own right – which probably rules out most interpreted languages (like PHP and Python) in favour of compiled languages that run native processor instructions, and almost certainly rules out frameworks are not a good fit to this.

    However, if you want to write complex business logic on complex databases, for relatively low-volume sites, and your interest is in not having easy to use pre-built functionality you can tap into and not having to reinvent the wheel, then perhaps Laravel is an excellent choice for you.

    In other words, there is no single choice for all requirements – you need to choose technology that fits your own mix of functionality vs. performance.

    All that said, I know from (bitter) past experiences the difficulties of bug finding and fixing in open source projects, that some open source projects are more amenable to merging PRs from users than others, and that investing in the wrong open source technology (or commercial technology also) can lock you in to obsolescence that you will regret later.

    Some open source technologies are more mature than others, and have larger user bases and are better tested and are more bug-free than others, and some have better leadership and greater longevity than others.

    When it comes to getting bugs fixed, the Laravel/Symphony example you gave shows a highly obscure problem which had never or only rarely been experienced by others is difficult to diagnose and difficult to either get fixed or find a workaround. It took 2 years before someone else effectively diagnosed it as an Apache .htaccess/mod_rewrite issue. IMO this is more illustrative of how commercial software with a commercial support contract, both of which you pay through the nose for and can therefore demand a fix, can be better in some circumstances than open source software – but if this issue was really that important the user could have found and paid for a Laravel/Symphony/Apache expert to track it down for him. The bottom line is that this is one aspect of open source that you need to through into the mix and weigh up against all the other factors.

    The good news is that with Open Source, because you have access to the source code, if you find a bug then you can fix it yourself. And if on rare occasions you cannot get that fix accepted into the base code, you still have the choice of reapplying that fix yourself to future releases. This only becomes a problem if you have chosen a product that is full of bugs and where the developers will not merge bug fixes. (I had one such project and eventually abandoned my development because maintaining the plethora of fixes which (to this day, over a decade later) were unmerged became too much of a headache.)

    So, as the Knight said in Indiana Jones and the Holy Grail, “Choose wisely.”

    That said, even though making a wise technology choice is essential, this is far from easy. To the uninitiated, the plethora of technology choices (and even worse combinations of technologies) can make choosing the best technological and longevity solution to meet your unique needs **very difficult** indeed.

    Choosing to roll-your-own is certainly one option worth considering – it has its pros and cons – but so do all the other choices. But you need to weight these choices against a list of requirements – and here is my generic list for most (i.e. not extreme) projects:

    1. Rapid development avoiding reinventing the wheel
    2. A code base which adheres reasonably well to development best practices i.e. reasonably comprehensive automated tests, avoiding excessively large classes and files.
    3. Reasonable performance – certainly technologies that don’t tie you in to code that is always poorly performant.
    4. A large existing user base who will have ironed out most of the bugs already.
    5. An ecosystem of add-ons for when the functionality you need doesn’t exist in the core product.
    6. An ecosystem of support – community help, good and responsive product support, a willingness to accept bug reports, and to accept and merge bug-fix PRs – and expert consultants you can hire if things get tough.

    However, in the end it comes down to one thing – you have a choice. Regardless of my views or those of Rasmus Lerdorf or Wesam Mikhail, you have the freedom to choose from more than one mature and well respected technology or to roll-your-own. Just don’t expect there to be a single solution which works for every project however much of an edge case it is.

    Like

  2. I should also add that I think that the PHP Composer approach to autoloading is a performance bottleneck because it literally autoloads everything for every single PHP execution, regardless of whether your specific application code uses all that stuff in Rasmus Lerdorf’s diagram or not, and regardless of whether the specific parts of the application this specific execution is going to run will require all of the stuff that your application might need across all its code.

    (I have not measured the impact of this – and the OpCache in more recent versions of PHP means that the cost of pre-loading everything is significantly reduced, but there is still an overhead nevertheless.)

    However, IMO there is no need to autoload literally everything:

    1. Composer can scan your application code for objects it actually uses, and then autoload only the objects that your code references and iteratively the objects that code references. This would avoid a lot of the performance overhead of having a large tree of packages most of which your code will never use.

    2. It should be possible to find a way for each package to autoload only files containing the API objects, and for the remainder of the package only to be loaded when the API is used. Similarly in e.g. a very large Laravel project, it should be possible to modularise it, and only load the modules required once the (Laravel) router determines the modules that the specific call needs. This would avoid a lot of the performance overhead of autoloading code which your application might use, but which will not be used for this specific execution.

    Like

Leave a Reply