Hacker News new | ask | show | jobs
by matsemann 1655 days ago
It's great for getting things up and running. And can last a long time. But now that we're 40 devs or so working in the same 400k LOC codebase, I'd prefer Java/Spring (or really, Kotlin). So hard to maintain django in the long run, need to be really strict, or one ends up with each app spaghettied with other apps. Doing queries where you filter deep on other apps' models, and since it's only done as kwargs with no typing nothing stops that from exploding runtime when someone changes a model somewhere. Too easy to send fat objects around everywhere, accidentally doing heavy db stuff when using a property. Also makes it harder to test, because everything leaks.
6 comments

Strongly agreed on these pain points. One tool I have been using recently to help is django-seal, which locks the QuerySet so you can’t run extra queries. This should really be part of the ORM, fail-unsafe is a bad option for performance critical code.

Combined with the Repository pattern from DDD, you can have all your model fetches go through a separate class that does the necessary select-/prefetch-related calls, then seals. (Or just override your model's Manager to do this).

And we have a coding standard guideline to discourage using queryset operators in business logic, as you note it breaks encapsulation and makes your code really hard to refactor later. This is hard to enforce though…

I think Django’s ORM is if anything too convenient - it’s great for the first 100kloc but then as you say, you need to overlay some discipline to prevent things from blowing up, and the framework is all about removing friction which makes this hard.

I can see that happening on a large codebase with a lot of devs. Being a Java guy, I organized all the business logic and db access into a service layer, added type info and wrote a decent amount of unit tests. I can't deny however that I am 2x faster cranking out crud screens in Django than in Java and the combination of ORM, migrations, templates, and access to the 3rd party app ecosystem is a real productivity booster.
Yeah, for a simple app, it's so quick to get some auth, rest endpoints, a fine admin panel etc.

Problem is when you start abusing stuff. Just hook into some signal to do something, nbd, but suddenly it's an interconnected mess. Just add some custom stuff to the admin page, and suddenly what should have been a custom made page is now a weird mess it's hard to extend. Need to do stuff async in the background, and what could have been adding a task on some queue and having a thread poll it is instead this behemoth of complexity.

A large django app can also be good, I think, but then one at some part have to realize when to stop tweaking built in django features and write custom stuff, to avoid adding hacks on top of hacks. And also early stop doing the easy communication between apps, instead take the annoying detour around services and hard boundaries.

Yes, but nothing is stopping you from typing and organizing everything, right? I agree that it's not the default, which isn't ideal, but you can do it fairly easily (and keep it enforced).
Problem is that the typing with mypy is in general not a very strong guarantee in Python, and combined with Django most of it's almost useless. Sooo many kwargs doing magic stuff, or types being lost after being through some django functions.

And hard to do the organization propably in django, doing it breaks much of the benefits of the orm for instance. People will do what's convenient, and in Django that's writing unmaintainable code.

This seems like an architecture issue, rather than a framework issue. At some point you need to split these 400K and 40 devs out into different applications. Different application should not be able to access each other's database, but rather communicate through an API.

I'm not saying that you need micro services, but you should find a way to split this up into a few applications with well defined interfaces between the applications.

I agree. What we've been doing is - based on advice from somewhere I can't remember - is to always make things easy to remove. So whenever a new app/module/service is added - the "author" has to think about making said thing easy to remove. The result is that new apps/modules/services must have very few connections with the other parts of the code. It does result in a fair amount of "get_<app>_settings" on the base classes, but the result is a lot less spaghetti. The thing is, we rarely remove stuff later on - but when we do, it's often only a few methods that needs to be altered in order to disconnect a specific part of the system.
> one ends up with each app spaghettied with other apps

This is what I always found in Django projects with the notable exception of the one that had only one giant app.

I started to believe that one project doesn't get along with multiple apps (kind of microservices) in the mind of the average developer. Only one app Rails style is probably easier to grasp and manage.