Created
May 26, 2025 12:01
-
-
Save callmesangio/304a29439ce4fb1d70d472288385e6b3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Sure, here are some ideas that will hopefully expand on my post: | |
| 1. validation: in Django, the responsibility of validating model data | |
| is shared between forms and models. Plus, validation logic is not | |
| triggered by default on `Model.save()`, you need to explicitly invoke | |
| `Model.full_clean()` before saving, otherwise you may incur in errors | |
| from the database itself. I think this is not a good pattern to go with. | |
| This works great as long as the only source of incoming data is forms, | |
| but in modern web apps data can come in from a very broad variety of | |
| sources, which makes this approach unpractical. | |
| In my opinion, the ultimate (sometimes not exclusive, of course) place | |
| you want your whole validation logic to run at is just the model itself. | |
| The model should be the single, centralized, source of truth about the | |
| shape of the data it holds, the last resort of validation before hitting | |
| database constraints, and the last chance of getting meaningful error | |
| messages whenever validation fails. And I think that having all of this | |
| on `save` is the most sensible approach. | |
| ActiveRecord models behave this way, and, as a bonus, you can have the | |
| same validation primitives on your own classes via ActiveModel. | |
| 2. loose coupling between models and database schema: in Django, models | |
| and migrations are tightly coupled. You change something in the model, | |
| and you get a database migration for free. Of course you need to describe | |
| each and every column in the model, because Django needs to know how to manage | |
| tables on your behalf. And that most of the time is a great feature to | |
| have! But I think that opting out of it is something that the framework | |
| should allow more gracefully (sure, you have `managed=False` on | |
| models' `Meta`, that's a nice escape hatch, but IMHO not a very powerful | |
| one). Rails's models and migrations are loosely coupled, you can have | |
| each one independently of the other. ActiveRecord models don't require | |
| upfront annotation of table columns. Admittedly, coming from Django, | |
| this can be confusing at first, but it's something I got used to pretty | |
| quickly. I find this particularly valuable and convenient when the use | |
| case is building a web frontend against an existing database, where the | |
| web app has no (or partial) ownership of the schema. | |
| Also about migrations: Django applies migrations all the way through | |
| when testing (except when `--keepdb` is used) or when recreating the database | |
| from scratch, while Rails relies on a snapshot of the current schema | |
| (`schema.rb`), which speeds things up during tests or when the need of | |
| starting from a clean slate comes up. | |
| 3. background jobs/scheduled tasks: currently Django does not support | |
| background jobs out of the box, and that's very unfortunate. In my | |
| experience you end up needing them most of the time, and pretty soon | |
| into the development lifecycle of an app. So not having them in core | |
| Django usually means installing Celery or something similar, just to | |
| satisfy a dependency that's basically hardly avoidable. Maybe this | |
| will change with Django 6.0, as it appears tasks will be added to core, | |
| but I think it's undoubtedly long overdue. Rails supports tasks out of | |
| the box via ActiveJob, which, on top of that, provides a uniform interface | |
| for working with pluggable queue backends. | |
| 4. frontend support: Django is basically frontend agnostic. Just drop | |
| your assets (wherever they come from) into a directory and have them | |
| picked up by `collectstatic` and injected into your templates. | |
| With some work, you can plug just about any frontend library | |
| into a Django project, if you want so. And that's a very nice property to have! | |
| But I would *love* to get an opinionated frontend stack ready to use upon a | |
| default Django installation. Having a fullstack, out of the box solution | |
| would be empowering at least. Rails pursues this goal with the Hotwire suite | |
| of tools, and that helps in reaching a good level of productivity in a | |
| reasonable amount of time. | |
| 5. language integration: Ruby being | |
| Ruby, it allows libraries to tap into the language and augment it at | |
| will. ActiveSupport does that with many core classes, providing multiple | |
| big and small quality of life improvements that add up into a globally better | |
| DX. Being able to write `2.days.ago` instead of juggling with `timedelta` | |
| objects is bliss. That's not an issue with Django of course, it's an | |
| opportunity offered by the underlying platform that Rails got to take | |
| advantage of. I just like it a lot. | |
| 6. autoloading/Zeitwerk: again thanks to Ruby, in Rails you get autoloading | |
| of constants out of the box, that is, you usually don't have to `require` | |
| things before using them. It just happens automagically when you reference them. | |
| That means less boilerplate, and that moving files around while refactoring | |
| is a simple operation, since most of the time you don't have to fix your `require`s | |
| all over the place. Coming from Python, this was at best confusing to me when | |
| picking Rails up ("where the heck does this class come from!?"), but again, | |
| you end up getting used to it, and I find it speeds the dev flow up. | |
| The fact that Zeitwerk relies on module/class nesting being mirrored by the | |
| filesystem layout of files and directories is definitely helpful. | |
| 7. object storage: `django-storages` works great for object storage integration, | |
| but ActiveStorage comes with Rails and thus is maintained by the same team, | |
| no need to have an external dependency and/or to wait for that dependency to | |
| catch up with new framework releases. | |
| 8. websockets: again, `django-channels` is awesome, but I think ActionCable | |
| is more convenient, same reasons of #7. | |
| 9. inbound email: ActionMailbox provides support for inbound email processing. | |
| I honestly don't know if something along the lines of this actually exists for | |
| Django, but I think I would really appreciate having that already implemented | |
| for me should the need arise. | |
| 10. encrypted credentials: Django does not have an opinion, nor does provide | |
| solutions, about secrets management, delegating the user to provide their own | |
| solution. Rails solves the problem of the storage of secrets | |
| in a very effective, elegant, and transparent way. Handling credentials is | |
| basically a universal requirement, so having it solved from the start is great. | |
| 11. deployment: Rails goes the extra mile by providing an out of the box solution | |
| for deploying applications, Kamal. I got to use it recently for the first time | |
| and the experience I had was super smooth. I'm not saying it should be a web | |
| framework responsibility to provide a deployment tool, but hey, it's there, it | |
| works, everything is already configured for me to use it, why not. | |
| 12. Ruby: this may actually be a byproduct of the "new toy" effect, but I really | |
| am having fun writing Ruby. It's a pleasure to work with it. Python has been a long | |
| time companion to me, so maybe I just needed something new, but I'm actually | |
| having a great time, so I'll put it here among the reasons I find the whole RoR | |
| stack more pleasant. Time will tell, I guess. | |
| On top of this, I find the Ruby ecosystem much more oriented | |
| to web development than Python's: there are more libraries, better solutions to | |
| common problems, more tools and in general a wider spectrum of opportunities. | |
| The community is at least on par with Python's with regards to kindness | |
| and inclusiveness. | |
| That's about everything that comes to my mind right now. There may be other things, | |
| we'll see if I notice something new along the way of using RoR. | |
| I for sure have a nitpick about Rails, namely JSON validation. I'd like a more | |
| effective solution for validating complex JSON structures. It seems to me that | |
| Strong Parameters are not the right tool if not for basic use cases, so probably | |
| I will reach for an external validation library. | |
| Hope this helps! |
Comments are disabled for this gist.