4 ms·
The author says that he has soured on Python for “serious, large projects”. While it’s clearly personal opinion, and that’s fair enough , I can’t help but think
by orf 6y ago
The author says that he has soured on Python for “serious, large projects”. While it’s clearly personal opinion, and that’s fair enough , I can’t help but think his choice of framework hasn’t helped him and has likely caused significant slowdown when delivering features.
Looking through some of the code for Sourcehut, there’s an insane amount of boilerplate or otherwise redundant code[1]. The shared code library is a mini-framework, with custom email and validation components[2][3]. In the ‘main’ project we can see the views that power mailing lists and projects[4][5].
I’m totally biased, but I can’t help but think “why Flask, and why not Django” after seeing all of this. Most of the repeated view boilerplate would have gone ([1] could be like 20 lines), the author could have used Django rest framework to get a quality API with not much work (rather than building it yourself[6]) and the pluggable apps at the core of Django seem a perfect fit.
I see this all the time with flasks projects. They start off small and light, and as long as they stay that way then Flask is a great choice. But they often don’t, and as the grow in complexity you end up re-inventing a framework like Django but worse whilst getting fatigued by “Python” being bad.
1. https://git.sr.ht/~sircmpwn/paste.sr.ht/tree/master/pastesrht/blueprints/public.py https://git.sr.ht/~sircmpwn/paste.sr.ht/tree/master/pastesrh...
2. https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/srht/email.py https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/srht/emai...
3. https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/srht/validation.py https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/srht/vali...
4. https://git.sr.ht/~sircmpwn/hub.sr.ht/tree/master/hubsrht/blueprints/mailing_lists.py https://git.sr.ht/~sircmpwn/hub.sr.ht/tree/master/hubsrht/bl...
5. https://git.sr.ht/~sircmpwn/hub.sr.ht/tree/master/hubsrht/blueprints/projects.py https://git.sr.ht/~sircmpwn/hub.sr.ht/tree/master/hubsrht/bl...
6. https://git.sr.ht/~sircmpwn/paste.sr.ht/tree/master/pastesrht/blueprints/api/pastes.py https://git.sr.ht/~sircmpwn/paste.sr.ht/tree/master/pastesrh...
- StavrosK 6y agoExactly agreed. I basically only use Flask for things I want to explicitly be single-file these days. For anything larger, I reach for Django, because I know that if I need at least one thing from it (and I always need the ORM/migrations/admin), it will have been worth it. My current favorite way of building APIs is this Frankenstein's monster of Django/FastAPI, which actually works quite well so far: https://www.stavros.io/posts/fastapi-with-django/ https://www.stavros.io/posts/fastapi-with-django/ FastAPI is a much better way of writing APIs than DRF, I wish it were a Django library, but hopefully compatibility will improve as Django adds async support.
- fastball 6y agoI built the backend for my knowledge-base platform[0] using Flask originally, but performance was definitely a struggle so I rewrote the whole thing with FastAPI. Have definitely seen a serious performance bump from that switch, and currently am quite happy with it. Many of our users are actually impressed with how fast everything is on the platform. I still want to rip out SQLAlchemy ORM and replace it with pure SQL via `asyncpg`, as SQLAlchemy ORM is not async and that causes a bunch of extra switching in the backend that certainly doesn't help eek out more perf, but at the moment it's a bit too much effort and users are happy. Scaling is handled by just throwing more instances of the application at the problem, behind a load-balancer. [0] https://supernotes.app https://supernotes.app
- StavrosK 6y agoThat sounds like a good solution, and is a good data point to know, thank you. Did you try Sync FastAPI? I'm wondering how its performance compares with async
- fastball 6y agoWhen you say sync do you just mean having sync endpoints? If so, then yeah, it's required since we're using SQLAlchemy ORM. Otherwise the calls to SQLA ORM would block the main event loop. As it is, FastAPI (well really Starlette) creates threads for sync endpoints to prevent blocking the main thread/event loop. So yeah, still seeing good speedups in our own benchmarks even though most of our endpoints are sync. What was arguably more important though was how much switching to ASGI helped with handling WebSockets. We're using SocketIO, and trying to get a fundamentally async protocol working within sync (Flask) land was a massive pain. We had repeated reliability and deployment issues that were very hard to debug. Switching to FastAPI made that much easier.
- StavrosK 6y agoOh, I can imagine, ASGI must be immeasurably easier. Where do the async speedup gains come from, though, if your database is still sync? Wouldn't threadpools provide comparable performance before?
- ramraj07 6y agoI maintain a fairly complex flask application and cannot see a better tool for that job. Our code looks similar as well. It's boilerplate repeated often for sure, but there will always be that one endpoint where you need that flexibility to do something a highly opinionated framework just won't let you. In the end it's deciding whether you write some extra code with flexibility or some extra code fighting the framework. Can you show me a comparable codebase in django and how it looks? I'm genuinely curious how people deal with edge cases.
- Scarbutt 6y agoSome devs prefer clear, flexible and performant/unambiguous code to 20 layers of abstraction.
- orf 6y agoThey do indeed. But then they realise that copy-pasting repetitive authentication and validation boilerplate everywhere leads to inconsistencies, bugs and at worst security issues. Then they build their own abstractions. And then, congratulations, they’ve spent longer than they should have to end up with a worse version of Django, that nobody but them finds “clear” or “unambiguous”. If only we could capture these common, repetitive and important patterns and put them in some kind of library. A “framework”, if you will. That way you don’t need to copy-paste this stuff over and over again, and anyone who knows the library will find it clear and unambiguous! In fact this is such a good idea that I’m going to do it myself. I’ll call the library Franz, after a famous pianist.
- carapace 6y agoI haven't used Django in years, so maybe things have changed, but I recall two incidents that stick in my mind and prevent me from taking the whole project seriously. The first was when they removed tracebacks. Singularly useless thing to do IMO. But there's a --show-tracebacks option (or something like that, it was a long time ago) to show tracebacks, but it didn't work. I dug into the code for this one. IIRC, the guy who added the code to suppress tracebacks didn't take into account the CLI option. I patched it to not suppress tracebacks but there turned out to be another place where tracebacks were suppressed, and I eventually gave up. The second incident (although, thinking about it they happened in chonologically reversed order) was when a junior dev came to me with a totally wacky traceback that he couldn't understand. All he was trying to do was subclass the HTML Form widget, like a good OOP programmer, but it turned out that Django cowboys had used metaclasses to implement HTML Forms, and utterly defeated this poor kid. I was so mad: Who uses metaclasses to make HTML forms? Overkill much? (In the event the solution was simple: make a factory function to create the widget then patch it to have the desired behaviour and return it. But you shouldn't have to do that: OOP works as advertised, why fuck with a good thing?) So, yeah, Django seems to me to be run by cowboys. I can't take it seriously. FWIW, I'm learning Erlang/OTP and I feel foolish for thinking Python was good for web apps, etc. Don't get me wrong, I love Python (2) but it's not the right solution for every problem.
- orf 6y agoI appreciate your reply, but Django never “removed tracebacks” and I would love to see a declarative form that didn’t use metaclasses in some way. Here’s the ~20 lines of cowboy code you’re referring to[1] - collecting the declared fields and setting an attribute containing them. Not exactly the kind of thing that should make you mad, and rather than overkill it’s exactly the use case for metaclasses. And to top it off, that metaclass is completely optional, if you want to create a list of fields and pass it into BaseForm then go for it. Most don’t. 1. https://github.com/django/django/blob/5776a1660e54a95159164414829738b665c89916/django/forms/forms.py#L24 https://github.com/django/django/blob/5776a1660e54a951591644...
- carapace 6y ago