Browse Source

Rewrote becoming big and foreword

pull/112/head
Armin Ronacher 15 years ago
parent
commit
bca1acf1f3
  1. 116
      docs/becomingbig.rst
  2. 32
      docs/foreword.rst

116
docs/becomingbig.rst

@ -3,54 +3,74 @@
Becoming Big
============
Your application is becoming more and more complex? Flask is really not
designed for large scale applications and does not attempt to do so, but
that does not mean you picked the wrong tool in the first place.
Your application is becoming more and more complex? If you suddenly
realize that Flask does things in a way that does not work out for your
application there are ways to deal with that.
Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
a number of large websites out there and all Flask does is bring those
two together. Being a microframework, Flask is literally a single file.
What that means for large applications is that it's probably a good idea
to take the code from Flask and put it into a new module within the
applications and expand on that.
What Could Be Improved?
-----------------------
For instance it makes a lot of sense to change the way endpoints (the
names of the functions / URL rules) are handled to also take the module
name into account. Right now the function name is the URL name, but
imagine you have a large application consisting of multiple components.
In that case, it makes a lot of sense to use dotted names for the URL
endpoints.
Here are some suggestions for how Flask can be modified to better
accommodate large-scale applications:
- get rid of the decorator function registering which causes a lot
of troubles for applications that have circular dependencies. It
also requires that the whole application is imported when the system
initializes or certain URLs will not be available right away. A
better solution would be to have one module with all URLs in there and
specifying the target functions explicitly or by name and importing
them when needed.
- switch to explicit request object passing. This requires more typing
(because you now have something to pass around) but it makes it a
whole lot easier to debug hairy situations and to test the code.
- integrate the `Babel`_ i18n package or `SQLAlchemy`_ directly into the
core framework.
.. _Babel: http://babel.edgewall.org/
.. _SQLAlchemy: http://www.sqlalchemy.org/
Why does Flask not do all that by Default?
------------------------------------------
There is a huge difference between a small application that only has to
handle a couple of requests per second and with an overall code complexity
of less than 4000 lines of code and something of larger scale. At some
point it becomes important to integrate external systems, different
storage backends and more.
If Flask was designed with all these contingencies in mind, it would be a
much more complex framework and harder to get started with.
two together. Being a microframework Flask does not do much more than
combinding existing libraries - there is not a lot of code involved.
What that means for large applications is that it's very easy to take the
code from Flask and put it into a new module within the applications and
expand on that.
Flask is designed to be extended and modified in a couple of different
ways:
- Subclassing. The majority of functionality can be changed by creating
a new subclass of the :class:`~flask.Flask` class and overriding
some methods.
- Flask extensions. For a lot of reusable functionality you can create
extensions.
- Forking. If nothing else works out you can just take the Flask
codebase at a given point and copy/paste it into your application
and change it. Flask is designed with that in mind and makes this
incredible easy. You just have to take the package and copy it
into your application's code and rename it (for example to
`framework`). Then you can start modifying the code in there.
Why consider Forking?
---------------------
The majority of code of Flask is within Werkzeug and Jinja2. These
libraries do the majority of the work. Flask is just the paste that glues
those together. For every project there is the point where the underlying
framework gets in the way (due to assumptions the original developers
had). This is natural because if this would not be the case, the
framework would be a very complex system to begin with which causes a
steep learning curve and a lot of user frustration.
This is not unique to Flask. Many people use patched and modified
versions of their framework to counter shortcomings. This idea is also
reflected in the license of Flask. You don't have to contribute any
changes back if you decide to modify the framework.
The downside of forking is of course that Flask extensions will most
likely break because the new framework has a different import name and
because of that forking should be the last resort.
Scaling like a Pro
------------------
For many web applications the complexity of the code is less an issue than
the scaling for the number of users or data entries expected. Flask by
itself is only limited in terms of scaling by your application code, the
data store you want to use and the Python implementation and webserver you
are running on.
Scaling well means for example that if you double the amount of servers
you get about twice the performance. Scaling bad means that if you add a
new server the application won't perform any better or would not even
support a second server.
There is only one limiting factor regarding scaling in Flask which are
the context local proxies. They depend on context which in Flask is
defined as being either a thread or a greenlet. Separate processes are
fine as well. If your server uses some kind of concurrency that is not
based on threads or greenlets, Flask will no longer be able to support
these global proxies. However the majority of servers are using either
threads, greenlets or separate processes to achieve concurrency which are
all methods well supported by the underlying Werkzeug library.

32
docs/foreword.rst

@ -17,24 +17,20 @@ may be necessary in larger or more complex applications.
For example, Flask uses thread-local objects internally so that you don't
have to pass objects around from function to function within a request in
order to stay threadsafe. While this is a really easy approach and saves
you a lot of time, it also does not scale well to large applications.
It's especially painful for more complex unittests, and when you suddenly
have to deal with code being executed outside of the context of a request,
such as in cron jobs.
Flask provides some tools to deal with the downsides of this approach, but
the core problem remains. Flask is also based on convention over
configuration, which means that many things are preconfigured and will
work well for smaller applications but not so well for larger ones. For
example, by convention, templates and static files are in subdirectories
within the Python source tree of the application.
But don't worry if your application suddenly grows larger
and you're afraid Flask might not grow with it. Even with
larger frameworks, you'll eventually discover that you need
something the framework just cannot do for you without modification.
If you are ever in that situation, check out the :ref:`becomingbig`
chapter.
you a lot of time, it might also cause some troubles for very large
applications because changes on these thread-local objects can happen
anywhere in the same thread.
Flask provides some tools to deal with the downsides of this approach but
it might be an issue for larger applications. Flask is also based on
convention over configuration, which means that many things are
preconfigured and will work well for smaller applications but not so well
for larger ones. For example, by convention, templates and static files
are in subdirectories within the Python source tree of the application.
However Flask is not much code and built in a very solid foundation and
with that very easy to adapt for large applications. If you are
interested in that, check out the :ref:`becomingbig` chapter.
A Framework and an Example
--------------------------

Loading…
Cancel
Save