Use bigint, never UUID. UUIDs are massive (2x a bigint) and now your DBMS has to copy that enormous value to every side of a relation.
It will bloat your table and indexes 2x for no good reason whatsoever.
Never use UUIDs as your primary keys.
Or you could preload a table of autoincremented bigints and then atomically grab the next value from there where you need a surrogate key like in a distributed system with no natural pk.
Otherwise you can still use the pregenerated autoincrements. You just need to check out blocks of values for each node in your distributed system from the central source before you would need them:
N1 requests 100k values, N2 requests 100k values, etc. Then when you've allocated some amount, say 66%, request another chunk. That eay you have time to recover from a central manager going offline before it's critical.
I have no problem with using uuids but there are ways around it if you want to stick with integers.
"enormous value" = 128 bits (compared to 64 bits)
In the worst case this causes your m2m table to double, but I doubt this has a significant impact on the overall size of the DB.
Even Microsoft default sample database schema uses INT ids.
This seems like terrible advice.
For the vast, vast, vast majority of people, if you don't have an obvious primary key, choosing UUIDv7 is going to be an absolute no-brainer choice that causes the least amount of grief.
Which of these is an amateur most likely to hit: crash caused by having too small a primary key and hitting the limit, slowdowns caused by having a primary key that is effectively unsortable (totally random), contention slowdowns caused by having a primary key that needs a lock (incrementing key), or slowdowns caused by having a key that is 16 bytes instead of 8?
Of all those issues, the slowdown from a 16 byte key is by far the least likely to be an issue. If you reach the point where that is an issue in your business, you've moved off of being a startup and you need to cough up real money and do real engineering on your database schemas.
You can monitor and predict the growth rate of a table; if you don’t know you’re going to hit the limit of an INT well in advance, you have no one to blame but yourself.
Re: auto-incrementing locks, I have never once observed that to be a source of contention. Most DBs are around 98/2% read/write. If you happen to have an extremely INSERT-heavy workload, then by all means, consider alternatives, like interleaved batches or whatever. It does not matter for most places.
I agree that UUIDv7 is miles better than v4, but you’re still storing far more data than is probably necessary. And re: 16 bytes, MySQL annoyingly doesn’t natively have a UUID type, and most people don’t seem to know about casting it to binary and storing it as BINARY(16), so instead you get a 36-byte PK. The worst.
This kind of problem only exists in unsophisticated databases like SQLite. Postgres reserves whole ranges of IDs at once so there is never any contention for the next ID in a serial sequence.
MySQL does need a special kind of table-level lock for its auto-incrementing values, but it has fairly sophisticated logic as of 8.0 as to when and how that lock is taken. IME, you’ll probably hit some other bottleneck before you experience auto-inc lock contention.
So your advice that you DEFINITELY won't need a BIGINT, well, that decision can come back to bite you if you're successful enough.
(You're probably thinking there's no way we have over 2 billion users and that's true, but it's also a bad assumption that one user row perfectly corresponds to one registered user. Assumptions like that can and do change.)
Also, protip for anyone using MySQL, you should take advantage of its UNSIGNED INT types. 2^32-1 is quite a bit; it’s also very handy for smaller lookup tables where you need a bit more than 2^7 (TINYINT).
> but it's also a bad assumption that one user row perfectly corresponds to one registered user. Assumptions like that can and do change.
There can be dupes and the like, yes, but if at some point the Customer table morphed into a Customer+CustomerAttribute table, for example, I’d argue you have a data modeling problem.
I’ll be 110 years old telling my great-grandchildren about how we used integers for primary keys, until reason arrived and we started using uuids.
And they’ll be like, “you weren’t one of those anti-vaxxers were you?”
Preferably delete the row once it's been permanently stored.
Keeping an actual transaction open for that long is asking for contention, and the idea of having data hanging around ephemerally in memory also seems like a terrible idea – what happens if the server fails, the pod dies, etc.?
With UUIDs there is absolutely no need for that.
> Keeping an actual transaction open for that long is asking for contention
Yes, which is why this is not an actual db transaction, it's a business transaction as mentioned.
> and the idea of having data hanging around ephemerally
The data is not ephemeral of course. But also the mapping between business transaction and model is not 1:1, so while there is some use for the transaction ID, it can't be used to identify a particular model.
Except now (assuming it’s the PK) you’ve added the additional overhead of a UUID PK, which depending on the version and your RDBMS vendor, can be massive. If it’s a non-prime column, I have far fewer issues with them.
> The data is not ephemeral of course.
I may be misunderstanding, but if it isn’t persisted to disk (which generally means a DB), then it should not be seen as durable.
Yes the overhead of UUIDs was something I mentioned already. For us it absolutely makes sense to use them, we don't anticipate to have hundreds of millions of records in our tables.
Twice now I was called to fix UUIDs making systems crawl to stop.
People underestimate how important efficient indexes are on relational databases because replacing autoincrement INTs with UUIDs works well enough for small databases, until it doesn't.
My gripe against UUIDs is not even performance. It's debugging.
Much easier to memorize and type user_id = 234111 than user_id = '019686ea-a139-76a5-9074-28de2c8d486d'
I recently upgraded two ~10 year old aging legacy applications at work. One was in Flask, and one in Django. This made me appreciate the "batteries included" philosophy of Django a lot more.
Even though the django legacy application was much larger, it had barely any extensions to "vanilla django". Comparably, the flask application had a dozen third-party flask-* dependencies that provided functionality like auth, permissions, and other features that Django has built-in. Many of these dependencies were archived/abandonware and hadn't been maintained in a decade.
When it came to upgrading the Django app, I had one giant release notes page to read. I didn't need to switch any packages, just make some pretty simple code changes for clearly documented deprecations. For the Flask app I had to read dozens of release notes pages, migrate to new maintained packages, and rework several untested features (see: legacy application).
In my mind, "batteries included" is an underrated philosophy of Djangoo. Also, it is now such a mature ecosystem it is unlikely there will be any radical breaking changes.
Perhaps there are some parallels to draw with newer trendy (but minimalistic) python frameworks like FastAPI.
If I were building a web application I wanted to last a decade or more, Django would be up there in tech choices - boring and sensible, but effective.
> I talked to this speaker afterward, and asked him how they did nested modals + updating widgets in a form after creating a new object in a nested modal. He showed me how he did it, I've been trying to figure this out for 8 months!
Do share!
I find with HTMX, it can introduce a lot of edge cases to do with error handling, showing loading progress, and making sure the data on the current page is consistent when you're partially updating chunks of it. With the traditional clunky full-page-refresh Django way, you avoid a lot of this.
fidotron•2mo ago
pabe•2mo ago
zerr•2mo ago
ecshafer•2mo ago
zerr•2mo ago
btreecat•2mo ago
fhd2•2mo ago
sgt•2mo ago
One can then later consider spinning certain logic off into a separate service (e.g. in Golang), if speed is a concern with Python.
leoh•2mo ago
sgt•2mo ago
gymbeaux•2mo ago
rob•2mo ago
JodieBenitez•2mo ago
bnchrch•2mo ago
ashwinsundar•2mo ago
blitzar•2mo ago
the__alchemist•2mo ago
- Imports are a mess - No control of mutation in function signatures, and in general it's still a surprise when things mutate - Slow - Types and enums have room for improvement
sgt•2mo ago
seabrookmx•2mo ago
We have a few FastAPI services, but are mostly moving away from Python for projects > 1kloc.
nine_k•2mo ago
seabrookmx•2mo ago
haneul•2mo ago
seabrookmx•2mo ago
cjauvin•2mo ago
wahnfrieden•2mo ago
macNchz•2mo ago
I still love Django for greenfield projects because it eliminates many decision points that take time and consideration but don't really add value to a pre-launch product.
ashwinsundar•2mo ago
I prefer Python and it's web frameworks over Typescript/React because there is a lot more stability and lot less "framework-of-the-week"-itis to contend with. It's much easier to reason about Django code than any React project I've worked on professionally. IMO when you don't have a firehose of money aimed at you, then Python is the way to go
sgt•2mo ago
Yet the approach also scales up to enterprise grade project, leveraging DRF, Django-cotton and so on (and htmx).
tcdent•2mo ago
Too much other stuff going on in this app to incorporate Django, but it's still way ahead of the curve compared to bringing together independent micro frameworks.
thenaturalist•2mo ago
tcdent•2mo ago
In terms of views, route configuration and Django's class-based views are sorely missed when using FastAPI. The dependency pattern is janky and if you follow the recommended pattern of defining your routes in decorators it's not obvious where your URL structure is even coming from.
haneul•2mo ago
tcdent•2mo ago
`options(selectinload(...))`
to name a couple goofy ones
haneul•2mo ago
globular-toast•2mo ago
BiteCode_dev•2mo ago
SQLA is cleaner and more powerful, but when you just need CRUD, django wins.
globular-toast•2mo ago
Dealing with the session object seems a small price to pay for the flexibility in architecture that it gets you.
sgt•2mo ago
This is why most folks just needing a plain Python API without anything else, they usually go for Flask, which is vastly simpler. For a more complete web app or site, I would recommend Django.
BiteCode_dev•2mo ago
the__alchemist•2mo ago
ipaddr•2mo ago
the__alchemist•2mo ago
hellojesus•2mo ago
It makes it easy to compartmentalize the business logic in terms of module imports.
globular-toast•2mo ago
Modular monolith is a good idea and if you want to do it in Django then make small apps that just expose services to each other (ie. high-level business functions). Then have a completely separate app just for the UI that uses those services.
hellojesus•2mo ago
For a true modular design I'd probably step away from django to a less comprehensive framework or just write in golang.
alganet•2mo ago
hellojesus•2mo ago
alganet•2mo ago
A composite is a structure of many pieces that work together.
Thus, a composite monolith is this arrangement of components in a way that they work together as a monolith. Separate modules, but working together as a single thing.
globular-toast•2mo ago
alganet•2mo ago
Replaceable parts can have inheritance, but often what is good about them is the composability. Each part can connect to each other in different ways. We call these boundaries "connectors".
It's quite a fascinating tech.
imjonse•2mo ago
rowanseymour•2mo ago
haneul•2mo ago
vFunct•2mo ago
I don't know why anyone would use any other framework.
fhd2•2mo ago
1. Very easy to find developers for. Python developers are everywhere, and even if they haven't worked with Django, it's incredibly easy to learn.
2. Simple stuff is ridiculously fast, thanks to the excellent ORM and (to my knowledge fairly unique) admin.
3. It changes surprisingly little over time, pretty easy to maintain.
ranger_danger•2mo ago
It's extremely well-designed and extensible, there is no reason to reinvent the wheel when so much time and effort has been put into it.
They will complain of things like "eventually you will have to start over with a custom solution anyway"... but whatever gripes they have, could just be put into improving the admin to make it better at whatever they're worried about.
Personally I've not run into something I couldn't make work in the admin without having to start over. My own usecases have been CRUD for backoffice users/management and I've had great success with that at several different companies over the last ~15 years.
People will say "it's only for admins you trust" yet it has very extensive permissions and form/model validation systems heavily used in the admin and elsewhere, and they are easily extensible.
fhd2•2mo ago
andybak•2mo ago
I've been saying the same thing for decades (checks calendar - almost literally!)
jdboyd•2mo ago
ropable•2mo ago
atoav•2mo ago
jgalt212•2mo ago
tiffanyh•2mo ago
(Since Threads was based on the IG tech stack, and IG is a modified Django stack)