FrankWiles.com
Back to all questions
CS Christian Stefanescu
January 20, 2026

What are your do's and don'ts when starting a new Django project? Without going into specifics of the project, can you share apps you'd use and general best practices like "create your own user model" etc? I understand that your business is based around offering this advice for money, but maybe there are already great blog posts or cookiecutter templates you could point to?

Hello Christian!

Thanks for the great question. One of the things I love about this Ask Frank series is you all are asking a lot of the quetions I’ve been meaning to write about for a long time and this helps give me a push to actually do it.

As you can imagine, every project is different and you should avoid prematurely optimizing for things you aren’t sure to need, but here are a few things we always do.

We have a private copier template we use at work that I sadly can’t share with everyone, but all of these tips are in there.

Custom User model

We always start off with users.User as our User model which is a “custom” but basically standard Django User model.

The only thing we really add is a data = models.JSONField(default=dict) to ensure we can add a bit of preferneces to a user without necssarily needing a database migration right away.

To be clear, we MOSTLY define actual fields on the User model, but it can be handy to have this field in large deployment sitautions where adding a field is a bit tricker and this is a premeptive escape hatch of sorts.

One Settings File

We have config/settings.py. I know many people who use multiple settings files, importing from a common.py or base.py and that might be our fault. We pushed that pattern A LOT years ago and it served us fairly well, but over time we found it to be harder to reason about what was set where than anything.

In our Docker based world, environment variables make much more sense and provide more flexibility both locally and in deployments. So everything lives in config/settings.py.

NOTE: We’ve also abandoned the practice of having our “Django project” live in a directory named after the project. So for say the revsys.com website we might have previously had a structure like:

shell
src/
  revsys.com/
     revsys/
       settings.py 
       urls.py 
       wsgi.py
     users/
      models.py 

We’ve moved to having ALL of our “projects” but named config, because that is a better description of what lives in there. And this makes it the same for us across all of our projects. No more wondering, did we name the project revsys or rs or site or what?

We use django-environ configured to load a .env file. This allows for really flexible local customization between a developer’s .env file and Docker Compose overlays to pretty much allow you to customize your local environment as much as without Docker.

Anytime you find yourself itching to use a settings overlay, you just need to add another env var and set the default to be whatever was there previously and your co-workers don’t even need to be enformed of the change.

Justfile

We also have a standarized set of just recipes we always use. Things that standaridize copying .env-dist to .env when setting up a project for the first for example.

We also bake in useful things like just manage which will run a manage.py command inside of the right docker container. just shell to get a bash shell in that same container to poke around, use an IPython REPL, etc.

It’s handy to have things like just makemigrations and just migrate, things you do a lot to remove a little bit of friction.

Useful packages

As a team you come to agree and like certain packages and use them often. So we always start off with most of these:

  • django-db-geventpool to make our ORM calls async
  • django-click and django-typer so we build lots more and nicer to use management commands
  • django-ninja for our APIs
  • django-structlog for good structured logging
  • django-extensions for things like TimeStampedModel and shell_plus
  • django-hijack to make it trivial to switch between users locally
  • pytest and pytest-django for obvious reasons
  • model-bakery to make building pytest fixtures simpler

Consistency is Key

It is going to sound weird, but consistency is a HUGE deal in a code base. No, it’s a bigger deal than you think. Yeah, bigger than that even!

Are you going to have templates in a common templates/... folder structure? Or use <app>/templates/<app>/... style? Or a mixture of both?

PICK ONE and stick with it across all projects.

Are app names plural and models singular? Good. Reverse is also fine as long as it’s 100% of the time always the same. Yeah even when in that one made up special case you just made in your head sounds weird or funny. Even then! Yes even for moose.Moose should you ever find yourself writing US National Park software. Even then.

Do you have a top level static/ folder for static assets that are presented on a /static/ URL and deployed into a deployed_static/ folder? Great, do it that way in EVERY project.

The 10-15 minutes each you spend debugging silly inconsistencies like this add up. It is not even that time as much as the time lost context switching from what you were really working on. You aren’t working on “static assets” you’re adding another bit of JS to the mix and now you’ve dropped entirely out of flow on that task to trace through your static file settings to realize you co-worker called it images/ this time.

Reinvent the wheel in other cooler places in your code base, not in the core of the group’s understanding of the structure of their world.

devdata

You’re going to need local data for manual testing of your app. Don’t make this a second class citzen by doing it haphazardously in your test fixtures. This is an opportunity to reap amazing dividends with a little bit of discipline from the start.

I’ve written about crafting developer data for testing and how much of a development velocity boost it can be.

You can construct a few builder functions that can be used by your ./manage.py devdata <scenario> command AND your pytest fixtures so they can play double duty.

This also has the benefit of having some consistency and story, if you will, around this test data. You see [email protected] in a test and you remember from your local testing that is the head hancho, aka is_superuser=True, of the Park Service because you’ve hijacked him a million times as you’ve built out features. All because you’re reusing that persona in the same way in both.

Developer Experience Bits

I suppose all of these are developer experience, but I needed a header…

You want CI/CD as early as possible. Optiminally, on day one or two. If it’s week two and you don’t have CI? Stop what you’re doing and set it up right now.

You aren’t saving time, you’re actively wasting it.

You aren’t working on something more important. There is nothing more important.

You want something like pre-commit ensuring that your team has their ruff and ty configurations set up correctly and to enforce any other rules you might have to keep everyone honest and providing no surprises.

What about AI and LLMs?

Guess what is great about all of these things I’ve listed above?

LLMs love them!

If you’re following alone we’ve now:

  • List of commonly needed and useful commands documented in a Justfile
  • One place for settings and one way to set them
  • Clear rules around formatting, structure, etc that you can bake into a AGENTS.md file, skills, and prompts.
  • Code/data isolation in Docker
  • Example data from your devdata and pytest fixtures
  • Tests! Lots of tests!
  • CI to allow your LLM of choice to run loose and have confidence it’s not actually letting a moose run roughshod through your code.

Hope you found all of these tips useful! Keep bringing the great questions!

P.S. What’s up with the moose? It was just the first thing I thought of that is both plural and singular and the last time I saw one in person was at a National Park.

View other questions about:
RSS Feed All Content | RSS Feed All Questions
Next Question
The more I develop applications these days, the more I'm inclined to enable meaningful logging and an observability stack in place as modern web apps tend become more and more complex. What are your insights into some of the common mistakes or some of your tips on the same?