$ \def\Vec#1{\mathbf{#1}} \def\vt#1{\Vec{v}_{#1}(t)} \def\v#1{\Vec{v}_{#1}} \def\vx#1{\Vec{x}_{#1}} \def\av{\bar{\Vec{v}}} \def\vdel{\Vec{\Delta}} $

Harald Kirsch

about this blog
2022-04-08

Why I hate Software Frameworks

Everyone seems to be using software frameworks these days and each has its own hype-culture, uniqness-claim, and provides an in-group for its followers. Being somewhat distrustful by nature I developed an aversion against software frameworks over the years. To be a bit more specific, I had to work with Spring, Spring Boot, Angular, Gradle and Reactor. So what I say below is inspired by those and may or may not apply to others.

I make a difference between a software framework and software library. Though the border is a bit blurry, a library is roughly a software framework without all the ugly stuff I mention below.

What is wrong with frameworks?

  1. They prescibe a way to set up your software, it is not up to you.
  2. They are full of magic behaviour supposed to make your live easier and to give you a fast start. Yet the downside of magic is that it is magic to you: you don't understand why and how it works. For a fast start, this looks great, things just happen, but beware, the first quirk is waiting for you. To fix it you need to read the documentation and the code and lots of misguided stackoverflow answers of people in the same situation as you without the stamina to actually understand what is going on.
  3. Doing things their way is easy only at the start. With every deviation from their way your work gets exponentially harder.
  4. The magic may stop working from one version to the next. Either because of a new bug, or a new feature, or a dependency incompatibility or your previous need to deviate from their way which now no longer works.

I contrast this all with a software library providing a well designed API. There is no magic, behaviour is invoked explicitly by calling into the API. Getting started is slightly harder, because some boilerplate code needs to be written to invoke the needed API functionality in the right order. Yet often the boilerplate is not as boilerplate as framework designers assume. There are always those quirky deviations from the assumed and enforced "standard" which force you to fight against framework magic, while the not-quite-boilerplate code you have to write when using a library is all yours to adapt to your needs.

In addition, when leaving a project alone for half a year and coming back, the boilerplate makes explicit what is going on. It can by understood easily again. With a framework, things, hopefully, just happen and you might have forgotten some of the magic details you had researched — until they stop to work or you need to change something.

Using a framework, you literally sell your soul and bet the future of your software on that one framework, since switching would require a complete rewrite. Call it vendor lock-in if you want. But not only with regards to your software. Also with regards to your knowledge and experience. The experience you gather comprises a lot of how to fight or subvert the framework magic to get it your way, not their assumed default way.

In contrast, with a well designed library API, chances are that the next best library covering a similar topic has a similar API. You learn how to use the API, not how to fight against off-by-epsilon magic behaviour. Switching your software to a similar library may actually be possible, while switching away from or to another framework is typically a 100% rewrite.