- There's the potential for quite a bit of choice paralysis along the way as you assemble your stack
- It's harder to seek help: as you've likely assembled a unique collection of tools (and therefore encountered unique challenges)
- It's harder to onboard other developers
- There's often more bespoke glue code
- Smaller libraries often have a smaller community and a shorter lifespan
All of that said, I'd likely choose the library approach for certain unique applications and the framework approach for "common / solved problems" like publishing content.- There's the possibility you will do something very wrong if you roll your own from libraries
For example, HTTP serving frameworks are popular because the HTTP protocol is complicated, not to mention best practices around security that have evolved over decades and are still evolving, and the (ideal framework) will handle a lot of high-level protocol logic (do you really want to parse mimetype ranges and do Accept matching? How about CORS? Cookie signing?) on your behalf, calling into your code only for the business logic.
Even then, a library or framework can choose to split up functionality and allow you to include it or not based on your needs; even if it doesn't, there's a high likelihood that problems that aren't unique to you already have a default solution (i.e., HTTP serving frameworks/libraries that make CORS, CSRF protections, and other browser only considerations a piece of middleware that is trivial to include if you want it. An example being Spring, where I believe the only place CSRF tokens are supported is in Spring Security).
It's kinda cool in a way too but it has those specific downsides you mentioned.
So ideally, I would have a framework which allows me to swap out components as the application evolves and what is being solved becomes more well defined.
> - There's the potential for quite a bit of choice paralysis along the way as you assemble your stack
Yes. Which is why it is crucial to know software patterns, be able to research architectures and have (someone close, with) experience in making those choices - and seeing them fail. Frameworks are not a real answer to this, as they don't take away the need to make such fundamental choices: they just pull them forwards: it's the first thing you'll have to choose. At a moment when you lack all information and insight to make that choice.
> - It's harder to seek help: as you've likely assembled a unique collection of tools (and therefore encountered unique challenges)
If you stick to well-known patterns, mostly, there's enough help. Apply patterns to encapsulate, decouple and isolate all pieces. This way you avoid having to go to SO and asking "my Wordpress in a Docker with both FooFormsUltra(plus edition), EcommerceBarEasy, the MegaThemePlus with BootBulma subtheme, gives an error when I click on a red button". Instead your problems are either about what patterns to apply (which is high-level, well-documented) or with code in isolation (which means you can ask the library devs).
> - It's harder to onboard other developers
Again, well-known patterns can help. Counter is that even a senior hardened Rails dev will have a tough time on that legacy Rails project. In fact, to keep your Rails (ReactBoilerplate, Django) app onboardable over time, strict application of design patterns are the best solution. You'll need to spend time on this regardless.
> - There's often more bespoke glue code
This one I agree. In fact, if you Do It Right, your app will be mostly glue code. Especially the "boring parts" (like database setup, connecting, http handling etc). This is stuff that matters not for your domain and is best kept out of the way anyway. You really don't want the CLI-opt-parsing to get in the way of what your CLI app is doing anyway.
> - Smaller libraries often have a smaller community and a shorter lifespan
Maybe I misunderstand. But why does "no framework" imply "smaller libraries"?
You’re essentially creating a private framework without public scrutiny which catches a lot of mistakes. That’s one of the main values of using an open source framework.
Are there times when a new framework is better than a preexisting one? Sure, when there are no frameworks that can meet your specialized project, but if it’s a typically CRUD application? Imo you’re better off with an open source framework.
Exactly. I always phrase it this way: if you don't choose a framework, you will be writing one.
(for a certain class of problems, obviously)
So, just know that going in. If you're not prepared for that reality, maybe your best choice is in fact to choose a framework that's already written, tested, and well supported.
True, but I feel (gut feel, no facts) that this is a matter of not fully understanding either the problem or the pros and cons of the options. If it's a choice you'll be stuck with for an extended period of time, it's worth writing up an Architecture Decision Record, or some other form of semi-formal design document; writing it proves to yourself and / or your team that you and/or your team understand the problem, have reviewed the options, and know the consequences of picking that particular solution.
And it gives you, your team, and future developers a handhold why library X was picked over library Y, and a basis to challenge the decision on. If they challenge it, they need to show they understand the cost of changing as well (working hours, retraining time, chance of new issues, etc).
> It's harder to seek help: as you've likely assembled a unique collection of tools (and therefore encountered unique challenges)
Each library on their own - thinking of mainly JS libraries here - will have their own website and community; I haven't found many instances where an issue was specifically about the combination of libraries X and Y, but your mileage may vary.
> It's harder to onboard other developers
I acknowledge this is a potential issue. You can probably find Django developers more easily than "developers with experience in x, y and z". It's a trade-off. That said, I do feel like every nontrivial application and development team will need to come to terms with the fact that training and education is an important aspect of onboarding new team members, and even if they may be familiar with e.g. a framework, they still need to learn the quirks of your particular application and domain.
> Smaller libraries often have a smaller community and a shorter lifespan
A library's community size and longevity is one of those factors to keep in mind in this architecture decision record I mentioned above. Sometimes it'll be better to pick the library with more known future proof than the sexiest one. Compromises all around. I picked React because I'm confident we'll be able to find developers in 5 years time, but I also went for a cleanly separated client-server application in case we need to rebuild the front-end (which for some reason ends up happening anyway every 5-10 years).
No you aren't. This is a thought-terminating cliche. I think what people mean by this is you are assembling your own architecture. Frameworks often come bundled with an architecture, and that is a core value proposition for a lot of folks.
But you're not building a reusable tool to scaffold other apps with, you're building a bespoke architecture for a single app.
Mobile apps, for instance, have no "framework." iOS and Android require you to implement certain classes to participate in the app lifecycle, but it is not at all the same idea of "framework" as Rails, which really pushes you to use DHH's Favorite Things over everything else.
And I'll add that a lot of frameworks (I'm thinking of Spring here) devote most of their effort to creating "value add" wrappers around technologies that work more than well enough without, and that create even more problems for a dev who now has to understand both original and wrapper's attempt at enhancement. Enough wrapping! Create something real, just once.
I definitely agree that the attitude of never upgrading dependencies doesn't work! But I've seen plenty of teams using dependencies successfully that took the opposite approach of updating them regularly (say, spending an hour updating versions every couple of weeks - perhaps a bit longer if there is a major release of an important dependency). This might not work if you require mission-critical stability, but it can work out to a huge time saving if you don't.
... that don't usually add any real value.
It's a convoluted mess. A while ago I made the move to migrate to Yarn 2 / 3, because it uses less disk resources (dependencies are in .zip files). But because most tools still expect a node_modules folder, it required a lot of workarounds. And now I'm in some kind of dependency hell where one library needs version X of another lib while the other needs version Y, and they're not compatible or don't work well in my editor. It just keeps giving. Sigh.
But if you add dependencies, and don't make time to keep them up to date... you're going to have a bad time.
I dislike articles like this. The vast majority of people would consider React a framework, and if you need to redefine such a basic word in order to make your point valid, there's probably an issue with your argument, not the definition.
Having worked with both, React isn't just an "opinionated way of writing JS", it uses Javascript under the hood but there's a ton it does uniquely, that is there is no JS equivalent, this is a thing that only React has (hooks, context, etc).
I agree with you that there's something slightly wrong about that framing on the part of the devs.
But this is just the old descriptivism/prescriptivism divide again.
"It looks like you're 80% done with the web-page already, and we've just started - great!" — Management
Over time I’ve learned that the correct response is, approximately, “Sorry, I’m in the middle of [important work], please book 30 minutes for us and [stakeholders] to discuss it.” Gives me time to prepare so that I’m not caught off guard. And to bring backup if I think I need it.
New teammates will have to learn how to do things anyway. But with frameworks there's a chance they already know.
This would let people get going quickly and not have to make a thousand decisions or figure out all the integration points, but retains some of the benefits of the library-mindset (configurability, swappability, domain expertise, etc.)
I still think that relationship still creates friction. The framework itself has swapped out libraries in the past, and you are basically dragged along if you upgrade.
Which also means you'll choose libraries that are decoupled by design because the others are really hard to write such wrappers for.
Even if you do find yourself needing to update your dependencies, you can do them 1 at a time. This way you aren't stuck with a not building, completely broken codebase for days while you refactor to adapt to new changes.
Personally, I only like frameworks that don’t fight me tooth and nail to encapsulate them into adapter layers (hexagonal architecture) when the time comes. There aren’t many of them. Some of them feel almost narcissistic in how much they want to assert themselves within your code.
The domain model is the most important thing. The framework is a detail that is not important.
Edit: forgot to mention, there’s a whole other argument here about how frameworks are not architectures. I suspect this is a big draw for many teams. But a commoditized architecture won’t serve you well over the long term.
This alone implies that what matters is your domain. Not the framework. This is also an important point that many seniors in the industry keep repeating. Eric Evans wrote a giant book about it (DDD), Robert Martin named his company after it (and keeps bringing it up on any conference). Dave Farley keeps repeating it in his youtube channel and his books. And so on.
My take is that development speed does not come from quick scaffolding like `rails generate CreditCard` but from having a clean, well-maintainable, loosely coupled, well-tested CreditCard Service or Aggregate, or DomainBoundary or whatever. Rapid time to market is not achieved with `curl fancyframe.work/in.sh | sudo bash` or `pip install django`. Rapid time to market is achieved with an architecture that allows easy rewrites, fast pivots and rapid swapping of dependencies.
Frameworks often hamper such loosely coupling. They get in the way, in the long run. Sure, chances are >50% that what you are building now fill be replaced or have failed within years. But for the off-chance of having something that you'll be maintaining over years, decades even, be sure you have something that does not get in the way. Edit: and on that timescale, the time-wins that the framework initially gave you, are often neglecatable anyway.
> So my strategy is to keep frameworks like Spring, Hibernate, and Rails at arm’s length; behind architectural boundaries. I get most of the benefit from them that way; and I can take ruthless advantage of them.
>
> But I don’t let those frameworks get too close. I surrender none of my autonomy to them. I don’t allow the tendrils of their code to intermingle with the high level policy of my systems. They can touch my peripheral subsystems; but I keep them away from the core business logic. The high level policies of my systems shall never be touched by frameworks. - Robert C Martin https://blog.cleancoder.com/uncle-bob/2014/05/11/FrameworkBo...
I suspect some people value frameworks more highly because they perceive them as coming bundled with a community (e.g. support). This consideration outweighs the architectural damage done in their minds. I'm not disparaging this. But as devs mature and age, they gain confidence, and eventually realize that every project doesn't need to automatically start out with a framework.
One edge case: Situations where the language itself has a major, breaking change between versions. IE, .Net Framework 4 -> .Net 5 (based on .Net Core,) or Python 2 -> Python 3.
A simple codebase might easily move from Python 2 to 3; or .Net Framework 4 to 5; but once there's a lot of libraries, there's a good chance that you'll need a newer library in order to move to the newer language... And that newer library might have breaking changes.
Not as bad as it used to be but also just moving between platforms and staying on the same framework and things would act different. Because something in the OS layer was different. I ran into this quite a bit in python 2.x. Where windows and linux methods would be slightly different just because of underlying assumptions about how things like files were opened. That was when I learned your code is only as good as the std lib your platform provides. Take for example c++. The language itself is not terribly big. But that stdlib that goes with it (whew). Then you wonder would c++ be worth anything if that lib was not there?
If you're using something like Rails, a lot of the issues are minimized to a huge degree:
- Work required from updates to Rails itself, while sometimes painful, usually has a lot in the way of community support, and it hasn't been really bad since Rails 2/3/4.
- Libraries are highly, highly incentivized to keep up to date with the latest version of Rails, since so much of the ecosystem revolves around it. Not supporting the latest version of Rails basically signals you've abandoned the project.
Regarding switching libraries and frameworks, this depends on the scale I think. With React (an example he gave of a library, although I personally think it's in a bit of a grey zone) switching to something else basically means re-writing your front end - even your state management is likely written in something fairly coupled to React. If you're replacing a single-purpose library, sure the impact is smaller, but those sorts of libraries are ones you'd probably be using in a framework world anyway.
For me, the biggest value of frameworks is eliminating low-value decisions. With an opinionated framework, there doesn't need to be arguments about how to structure your project, how logging, or authentication, or database interaction, or one of a million other things with a lot of OK answers is going to work. This eliminates arguments when initially developing things, and massively reduces onboarding time. I can get a git repo of a Rails project and be more or less productive in a few days - with bigger Node apps I sometimes feel like it takes weeks or months to really get how things are structured sometimes.
I spent about a month reviewing frameworks on "ToDoMVC.com". My goal was to evaluate those and choose one for an app I wanted to upgrade. When I'd finished the review process I struggled for about a month trying to decide on which of those I'd invest my time in because that's a huge investment.
I finally decided to no use one. I did exactly what the author did. I selected libraries of code. I used jQuery, Mustache.js, Accounting.js, PouchDB.js, Chart.js, and Bootstrap.
With the exception of the latest Bootstrap I've been able to update all of those without any issues at all and I've gotten pretty good and using those tools.
I don't regret this at all. In fact I think this choice would make it much easier for anyone to modify my code, and that's something I want to offer users.
Also I think the issue is more relevant with Python and django and ruby as they lack abstract classes and interfaces? in the context of making things incrementally changeable they're quite useful, so maybe there is something that is like that?
"Move fast and break things" frameworks are not good ones.
My first job, I was working on a Tapestry 5 web-app. Very nice framework, very opinionated though.
And the creator, Howard L Ship, was very opinionated about breaking things. So we could never upgrade past 5.x (where I think x was 4 or 6), because it just broke everything for us, and we put weeks of dev time into trying to overcome that.
In the end, we just ported the app to Wicket, it was faster to do so.
As an added benefit, you’ll probably find it easier to get help from outsiders.
If your project falls within the scope, great. You may be sporting a bunch of YAGNI functionality, but could grow into it.
Now, should requirements venture outside the scope...
- You may eat some Level Of Effort to meet those requirements, assuming s decent plugin system, or
- As The Famius Article notes, you could eat the framework equivalent of DLL Hell trying to appease the Version Gods.
It's very simple to setup, easy to extend and letting you to setup the structure whatever you want.
If only it's updated to use async await, better error handling and have modules to perform ssr for react (and vue), it'll be much better.
Good luck removing Lodash from your code, unless you're replacing it with an equivalent Underscore.
Might be a good or bad thing depending on the project.
Plan for your code's death. Keep an eye on the next versions, and keep a CI job pinned to them. Read the changelogs. Keep the import EOL dates on your calendar. EOL your own code. We don't get to choose whether our code will die, but we can choose how it goes out.