Flask is a cancer
January 19, 2019
Over the course my professional career I have seen many production implementations of Flask and all of them had suffered from the same problems. Today I want to point out these problems and convince you to not choose Flask as the framework for your next application.
The diagnosis of Flask as a Python framework
Flask is the 2nd most popular Python framework just after Django. It's popularity comes from excellent documentation and ease of use.
A simple working application can be written in just about 5 lines of code:
Which is, of course, a great thing. Unfortunately this seeming easiness is the only thing that Flask has to offer. When your app starts facing real business requirements, the Flask has only simple (and primitive) ways of solving them.The consequence is that you have to act as if you had chosen badly designed low-level framework for experienced developers.
This is the reason Flask is a trap for developers - they choose it because it promises swift and easy development but after the app gets bigger, they end up with framework architecture that is not suitable for large apps.
So Flask is a trap, but who is the most likely to get caught in it?It could be anyone, of course, but I think there are 2 types of people that are particularly vulnerable:
- Junior developers - because they think it is easy - just a couple lines of code and it works - "oh, just one file and in the view I can put my code". It also stands to reason that due to them being on the very beginning of their journey with programming they did not yet have time to familiarize themselves with excellent Django documentation.
- Senior nonPython developers - because they think they understand the code - "oh, so the app is just an object that you run, no magic framework thing that runs your code" plus they are looking for "micro" framework because Django is big and big is bad.
So they choose Flask over other frameworks like Django or Pyramid.
Design flaws in Flask architecture
Now let me enumerate the biggest flaws:
Oh yeah, this is the worst thing. In fact, it is so terrible that it could be listed as the sole reason to avoid Flask. It starts quite harmlessly, you have g object so that you can attach any global objects to it and it is there to use in any parts of your application. This concept is OK when you write a toy app but it becomes the source of all evil for an app that is maintained by a team of developers.
The nightmare begins when you start realizing that your code is invoked not only by incoming requests but also:
- by unit tests
These split into ones that execute given class or function and the ones that test the whole app by calling given urls.
- by asynchronous tasks (for example - Celery)
- by arbitrary scripts (like calculate reports or create some initial db objects)
For each of these, you need to prepare a separate code that bootstraps your global variables.
For unit tests, if you won't unify them, then you could end up with dozen initialization techniques introduced by you and your fellow developers like described in Flask docs (if you need user object, just insert it).
After all, what you really need is to unify ALL entry points to single initialization function, which means you need to fake web request for celery tasks and scripts and pretend that you are always in web request context.
Using SQLAlchemy as ORM
No bootstrapping process
What does it mean? You have to bootstrap the app yourself.
The biggest problem is that the Flask mixes imperative and declarative way of configuration and encourages you to do so.
To glue all parts together you end up with ugly Python module that is responsible for bootstrapping the app. Mysterious import order (just try to change it!), ifs everywhere, decorators, ugh.
Example (real project, 5 non-junior developers):
How can this be done better?
In Django, the framework bootstraps the app for you: it reads settings file(s) and starts importing your application's modules automatically gluing them together.
Pyramid on the other hand, use dot-notation for specification where the things are, this makes the configuration code much cleaner. Plus it allows you to decorate views like Flask but without using the app object (thanks to venusian library).
Let me give you an example:
A middleware is a great concept - easy to understand and very powerful. This is the reason most of the frameworks implement them.
Example in Django:
- it is hard to discover the execution order
- it is hard to run logic that spans both before and after the request
I have seen a dozen custom implementations of permissions logic in Flask. This proves my point that Flask is chosen as a framework for beginners but then it forces the developer to write quite difficult code around it. As a sidenote permissions have solid implementation both in Django and Pyramid out-of-the-box.
Hard to write proper unit tests
This is caused by Flask using Global Context. If you have request context, script context and, celery context then you have to think in which context to run given test.
If you don't prepare your testing framework at the very beginning of app develpment then it is really hard to do so later.
3 false reasons (+ alternatives to Flask)
There are 3 reasons you can think that Flask is the right framework for you, let me pick them apart one by one and present alternatives.
- choosing Flask because you are a beginnerThis is a simple case, just ignore Flask, use Django. Django is an excellent, mature and easy framework.
- choosing Flask because you are not able to use SQLOk, fair enough. Triple check that you really can't use SQL database, then choose Pyramid.
Wrapping Things Up
I was not able to find a single reason why to use Flask, if you have one, please let me know in the comments down below. I’d be happy to prove you wrong :)