Hacker Newsnew | past | comments | ask | show | jobs | submit | j4mie's commentslogin

Hi, author here. I have been building Django and DRF applications commercially for about fifteen years at DabApps, and this guidebook is an attempt to write down the architecture patterns we have arrived at, for projects that have to survive years of ongoing maintenance.

High level summary:

* We don’t hide Django under layers of abstraction, we just slice it up a bit differently than you would by default.

* Organise code by responsibility (readers, actions, interfaces, data) rather than by app.

* Put most business logic in plain functions, not in fat models or deep class hierarchies.

* Keep views thin: treat GET as "read some data" and POST as "do an action".

* Design endpoints around real frontend use cases (backend for frontend) instead of idealised resources.

* Control queries carefully to avoid performance traps.

Happy to answer questions!


Thank you for sharing this — I found it really fascinating and insightful. It's a shame it didn’t get more attention here. I work in an environment similar to yours and have come to similar conclusions, though not quite as "radical," for the most part.

The part about the query/values/projection of the readers layer was the most foreign to me, but I have to say, it really clicked and finally put my finger on something that’s been nagging me for a long time.

Since you mention that this came out of your work projects, I’m curious — how has your experience been onboarding developers with this way of managing Django projects?


Laravel’s ORM was originally inspired by my now-long-dead PHP projects Idiorm and Paris:

https://laravelpodcast.com/episodes/c7807d42/transcript (Search for “Paris” to find the relevant section)

https://github.com/j4mie/idiorm

https://github.com/j4mie/paris

Idiorm was started in early 2010 while I was still writing PHP professionally. I’d heard about Django from a talk @simonw gave at the FlashBrighton meet-up group in 2009 and immediately fell in love. Idiorm and Paris, although not direct attempts to duplicate Django’s ORM, came from frustration that such an elegant ORM and query builder didn’t exist in the PHP world.

So in a roundabout way, Laravel’s ORM was absolutely inspired by Django.

As a side note, I’m still doing Django 16 years later and love it more than ever.


I do love Django too! and my developtment with more users (not most deployed) is running on Django :)


It's worth noting that uv also supports a workflow that directly replaces pyenv, virtualenv and pip without mandating a change to a lockfile/pyproject.toml approach.

uv python pin <version> will create a .python-version file in the current directory.

uv virtualenv will download the version of Python specified in your .python-version file (like pyenv install) and create a virtualenv in the current directory called .venv using that version of Python (like pyenv exec python -m venv .venv)

uv pip install -r requirements.txt will behave the same as .venv/bin/pip install -r requirements.txt.

uv run <command> will run the command in the virtualenv and will also expose any env vars specified in a .env file (although be careful of precedence issues: https://github.com/astral-sh/uv/issues/9465)


uv and its flexibility is an a absolute marvel. Where pip took 10 minutes, uv can handle it in 20-30s.


It’s an absolute godsend. I thought poetry was a nice improvement but it had its flaws as well (constant merge conflicts in the lock file in particular).

Uv works more or less the same as I’m used to with other tooling in Ruby, JS, Rust, etc.


How does uv avoid merge conflicts in lock files? I need a reason to switch.


I never got a chance to see the difference there because I moved on shortly after.

It was just that almost constant conflicts with poetry (and the errors about the project being out of sync) with a team developing in parallel were painful enough for me to suggest we try uv instead.

It seemed uniformly better with a simpler docker setup too (although I liked how pants would created executable bundles and you could just ship those).


Fair enough! I'm a bit surprised that anyone could get regular out of sync errors unless a team member were constantly updating dependencies every commit. But then if they did that with uv I'd imagine they'd have the same issue. Unless uv does something extra smart and creates you a new environment for every git branch.


+1, this is the exact reason I started using uv. Extremely convenient.

For some reason uv pip has been very slow, however. Unsure why, might be my org doing weird network stuff.


Or very difficult package spec


Doesn't it store the python version in the pyproject.toml though, is the python version file needed?


It’s not:

> uv will respect Python requirements defined in requires-python in the pyproject.toml file during project command invocations. The first Python version that is compatible with the requirement will be used, unless a version is otherwise requested, e.g., via a .python-version file or the --python flag.

https://docs.astral.sh/uv/concepts/python-versions/#project-...


cheers


Things that are down or impacted:

* Heroku and most of Salesforce

* Pipedrive: https://news.ycombinator.com/item?id=44234098

* OpenAI: https://status.openai.com/incidents/01JXCAW3K3JAE0EP56AEZ7CB...

* Lobsters: https://news.ycombinator.com/item?id=44234075

What's the common denominator?


I expect Lobste.rs's outage to be totally unrelated to Heroku's, just pure timing coincidence.

From the last archived version of lobste.rs/about: https://web.archive.org/web/20250601002505/https://lobste.rs...

"Lobsters is hosted on three VPSs at DigitalOcean: a s-4vcpu-8gb for the web server, a s-4vcpu-8gb for the mariadb server, and a s-1vcpu-1gb for the IRC bot. DNS is hosted at DNSimple and we use restic for backups to b2. (Setup details are available in our ansible repo.) Lobsters is cheap to run, so we don't take donations."


"Please state the nature of the medical emergency"


For anyone who wants to understand the economics of hydrogen, I can't recommend this lecture [1] by Michael Liebreich highly enough.

[1] https://www.youtube.com/watch?v=w0Q9cuF8zKg


Do you have a source for your statement about copper? The closest I can find is [1] "The expansion of electricity networks means that copper demand for grid lines more than doubles over the same period". That's a doubling of demand, not of total current circulation.

[1] https://www.iea.org/reports/the-role-of-critical-minerals-in...


Sorry not in circulation, my bad. The discrepance between required(for net zero) and planned mining is still worrying. Other metals like cobalt can be substituted by something else but not copper.

https://www.iea.org/reports/copper


From the article: "It adds LLM as a dependency, and takes advantage of LLM’s Python API to abstract over the details of talking to the models. This means sqlite-utils-ask can use any of the models supported by LLM or its plugins"

Ollama is supported in LLM via this plugin: https://github.com/simonw/llm-ollama


Thanks for the info.

I just assumed LLM was a en var manager for openai stuff. But makes sense.


Previous discussion: https://news.ycombinator.com/item?id=31849787

Repeating my comment from that thread:

  from diagrams import Diagram
  from diagrams.aws.compute import EC2

  with Diagram("Simple Diagram"):
      EC2("web")
This has a very odd API. It's using (abusing?) context managers and contextvars to do weird spooky things that you could just as easily do with ordinary objects or functions.


Depends how it's implemented. It seems very similar to Apache Airflow in API, and similar to the many Argo Workflows Python wrappers.

This pattern when implemented with context vars in the context managers is a pretty nice way to reduce the boilerplate, without the downsides of traditional globals. The state can be safely contained to just the with block.

Usually these types of APIs also allow usage without it, using the context vars only to populate arguments not explicitly provided.


it's using context managers to manage context i.e. state that is there but isn't needed to be referenced by code directly. it's the whole point. you can do it all with just functions or objects (doesn't matter, functions are objects in Python anyway) by design, context managers are there for DRY purposes.


What's jarring me with the parent's example is the fact that the context managers must be holding global state.

It would feel a lot better if it was this, for example, where you use each context explicitly:

  from diagrams import Diagram
  from diagrams.aws.compute import EC2

  with Diagram("Simple Diagram") as d:
      d.add(EC2("web"))
As as the parent also suggests, this then doesn't really need the context management at all

  from diagrams import Diagram
  from diagrams.aws.compute import EC2

  d = Diagram("Simple Diagram")
  d.add(EC2("web"))


yeah but then you have to repeat yourself a lot:

   d.add(first)
   d.add(second)
   d.add(...)
you see redundancy and a misused language capability, I see a feature and a nice DSL.


I wrote a library a little while ago which is intended to be the simplest possible expression of this idea:

https://github.com/j4mie/hotmetal/

The core implementation is only 85 lines of Python, and has no imports at all, so works nicely on minimal/alternative Pythons like Micropython.

I'm using it on a medium-sized side project and it's shockingly productive compared to string templates. I don't think anyone is using it except me, though.


Your project looks great and I like the fact you can use it with MicroPython.

I've also been working on a library with a similar idea:

https://github.com/SamDudley/neat-html

It's still a work in progress so an example might be better to look at:

https://github.com/SamDudley/neat-html/blob/main/examples/fo...

I was inspired by some JavaScript libraries that I've used in the past:

- https://github.com/jorgebucaran/hyperapp

- https://github.com/hyperhype/hyperscript

There is also a working integration with Django that enables the use of neat-html as a template backend, however it isn't up on GitHub yet.

I find the space of HTML generation libraries which can leverage the power of Python, really interesting.


This looks really neat. One suggestion is that in your readme, show the webpage output with your examples. It’s hard to image exactly what the page will look like just looking at the hotmetal python code


This is wonderful, I love it. I recently wrote a blog post about generating HTML in Python using LXML (https://ricardoanderegg.com/posts/python-build-html-componen...) and I didn't know about your library. I'll update it to add a mention to it.

Apart from the simplicity, I like the fact you can "install" it by copy-pasting the code at the top of your script, or running:

  curl -L https://raw.githubusercontent.com/j4mie/hotmetal/main/hotmetal/__init__.py >> myscript.py
which is nice when you want a self-contained file.

I think I'll be using this in the future.


We've come full circle on naming HTML tools!

https://en.wikipedia.org/wiki/HoTMetaL


Glad someone got the reference!


I'm glad it was intentional. :-) I don't feel as old. Still old. But less so.


Nice work! There are a few expressions of this idea. There's a reason people keep coming back to it: it's actually really nice to write markup this way.

https://github.com/weavejester/hiccup https://github.com/JuneKelly/sneeze


I use gomponents with go, which is very similar. The productivity increase is immense. https://github.com/maragudk/gomponents


I've been looking for something exactly like this for a while — a small Python library for generating HTML that isn't prescriptive about framework and doesn't introduce any particually weird syntax. Jinja2 macros are... not great when you want to turn your HTML into lots of smaller functions.


Search for "clojure hiccup"


Cool idea. I had a similar one a few weeks ago, with this additional idea of turning this into a "real" templating language (e.g. by adding a special construct for conditionals, looping, variables, macro expansion...).


This looks like a similar model to how Elm handles HTML. Except Elm provides functions for each tag, so it also does type checking to ensure that the passed arguments correspond to the actual attributes of the tag.


Elm [1] is based on a similar idea. Build your app from pure functions that return HTML tags.

[1] https://elm-lang.org/


Reminds me of dominate.


see also: twisted nevow + stan, which I believe predate the term 'AJAX'.

https://github.com/twisted/nevow/blob/master/examples/simple...


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: