HN Debrief

Some more things about Django I've been enjoying

  • Programming
  • Developer Tools
  • Open Source
  • Infrastructure

The post is a lightweight appreciation piece about Django, a long-running Python web framework, focusing on the parts that feel pleasant in day to day use rather than making a grand argument for the stack. The comments landed on a sharper version of that same idea. Django is still widely respected for the stuff around the request handler, especially its ORM, migrations, and built-in admin, and several people argued those pieces are good enough that they use Django just for schema management while serving production traffic from Go, Rust, or Java services.

If you run a CRUD-heavy product, Django still looks like a strong default when speed of delivery and database ergonomics matter more than raw throughput. Do not use anecdotal benchmark numbers to judge the stack without checking deployment, static file handling, caching, and the test method first.

Discussion mood

Mostly positive about Django as a mature, productive framework, with skepticism directed at the post’s benchmark numbers rather than the framework itself. The strongest praise centered on migrations, ORM, admin, and operational simplicity for conventional apps, while complaints focused on weak raw performance, awkward template syntax, and the pain of multi-app migration sprawl in large monoliths.

Key insights

  1. 01

    Sync Django is a deliberate operational choice

    For ordinary CRUD apps, synchronous Django was defended as the safer Python stack because async Python has too many production-only failure modes. Unbounded `asyncio.Queue`, careless `asyncio.gather`, blocking calls like `requests.get`, weak-reference task handling, and cancellation bugs all create outages that tutorials do not prepare teams for. The useful framing here is not nostalgia for sync code. It is that Python’s async model demands a lot more discipline than many teams realize, so plain Django behind workers and a reverse proxy can be the more robust system.

    If your app mostly serves short request-response traffic, do not assume async is the modern default you must adopt. Price in debugging complexity and load-path failure modes before choosing it over synchronous workers.

      Attribution:
    • CraigJPerry #1 #2
    • selfmodruntime #1
    • marcosdumay #1
  2. 02

    Django as a database control plane

    Several people treat Django less as a full web stack and more as a schema, admin, and migration layer for systems whose hot path runs elsewhere. The attraction is not just familiarity. Django’s model definitions, migration workflow, and admin UI are strong enough that teams use them to drive Go, Rust, Java, or PHP applications, sometimes even generating DAO or Hibernate code from Django models. That turns Django into a control plane for data and back-office tooling while other services handle performance-sensitive APIs.

    If your team likes Django’s data tooling but not Python for serving requests, split those concerns. Keeping Django for models, migrations, and admin can save a lot of custom internal tooling work.

      Attribution:
    • ranedk #1
    • leetrout #1
    • Klonoar #1
    • 0xpgm #1
  3. 03

    Multiple Django apps age badly in monoliths

    The sharpest large-codebase warning was that slicing one product into many Django apps creates migration debt that compounds for years. Once foreign keys and many-to-many relationships cross those boundaries, the app structure stops being a clean modular design and starts becoming a historical trap. Teams end up with cyclic dependencies, underspecified constraints, unsquashable migration chains, and growing CI cost. The better pattern for many internal products is one core app for models and migrations, with normal Python modules used to organize code underneath it. For shared multi-service databases, some teams go further and put models and routing in a standalone library so the database is managed as its own unit.

    Before you `startapp` repeatedly, ask whether you are creating real reusable boundaries or just future migration pain. For a single product, default to fewer Django apps and use Python package structure for separation.

      Attribution:
    • leetrout #1
    • pdhborges #1 #2 #3
    • dotandgtfo #1
    • 3eb7988a1663 #1 #2
    • Izkata #1
  4. 04

    Template limits are intentional, but Jinja is viable

    The complaints about Django templates were concrete: weak expression support, awkward arithmetic, no obvious slot-like capture pattern, and friction passing values around. The best defense was pragmatic. Those restrictions often stop teams from building sprawling logic in templates and push work into Python, where testing, caching, and performance tuning are easier. When that tradeoff feels too constraining, Jinja2 is a realistic option in Django. The catch is that it is not truly drop-in. Tags and helper names differ, tests behave differently, and third-party app support can lag.

    If your templates keep fighting you, first move logic into Python and see if the code gets clearer. If you still need a richer templating language, budget for a real migration instead of assuming Jinja2 is a flip-the-switch replacement.

      Attribution:
    • seanwilson #1
    • acdha #1
    • leetrout #1
    • selfhoster1312 #1
    • mcrk #1
  5. 05

    The double-underscore query syntax buys portability

    People complained about Django’s `field__operator=value` filters as ugly compared with operator-overloaded query DSLs like SQLAlchemy or Peewee. The useful counterpoint was that Django’s syntax is easy to serialize directly into URLs, admin filters, and API query parameters. That means the same query vocabulary used in code can drive ad hoc admin searches and shareable report links like `admin/event/?end__gt=...`. It is less elegant than expression objects, but it is unusually portable across the framework.

    Do not judge the query syntax only on aesthetics. If your product leans on admin tooling, support workflows, or filterable URLs, Django’s plain string-based lookups can be a feature, not just legacy syntax.

      Attribution:
    • mmclar #1
    • mixmastamyk #1
    • echoangle #1
    • Izkata #1

Against the grain

  1. 01

    Go and Rust erase Django's efficiency case

    The strongest pushback said Django no longer wins on either cost or delivery speed once a team is fluent in Go or Rust. The claim was not just about raw requests per second. It was that lower memory use, simpler concurrency, and cheap scaling outweigh framework convenience, and that experienced teams do not actually save time by accepting Python’s runtime overhead and framework abstractions. That view cuts directly against the dominant assumption that Django is the faster way to ship a stable product.

    If your team already has strong Go or Rust patterns, test the productivity claim instead of taking it on faith. Framework convenience is not a universal advantage once your own tooling and templates are mature.

      Attribution:
    • faangguyindia #1 #2
  2. 02

    Django's niceties are not unique anymore

    A smaller but credible objection was that much of what feels charming about Django is simply standard framework ergonomics in older ecosystems like .NET. The point was not that Django is bad. It was that developers can over-romanticize its coherence because they have not recently looked at ecosystems that have offered batteries-included web tooling, mature libraries, and stable conventions for years. That takes some of the specialness out of the original post’s enthusiasm.

    If you are revisiting backend stack choices, compare current framework experience across ecosystems rather than relying on old impressions. Django may still be right for you, but its strengths are no longer rare.

      Attribution:
    • hecreto #1
    • miki123211 #1
    • rileymat2 #1
    • Izkata #1

In plain english

ab
ApacheBench, a command-line tool for sending repeated HTTP requests to benchmark a web server.
async
Short for asynchronous programming, where a program can handle many waiting operations without blocking a thread for each one.
asyncio
Python’s standard library framework for asynchronous programming and event loops.
CRUD
Create, Read, Update, Delete, the four basic operations used in most data-entry software.
DAO
Data Access Object, code that wraps database operations behind a programmatic interface.
Django
A high-level Python web framework that includes tools for routing, database access, templating, admin pages, and other common web app needs.
Hibernate
A widely used ORM framework for Java.
Jinja2
A Python templating engine with richer expression support than Django’s default template language.
ORM
Object-Relational Mapper, a tool that lets code work with database rows as language objects instead of writing raw SQL for everything.
reverse proxy
A server placed in front of an application server that forwards requests and can handle caching, TLS termination, and static assets.
VM
Virtual Machine, a rented virtual server instance used to run software.

Reference links

Load testing and deployment references

Schema and migration tools

Template and query language alternatives

Python operator overloading example