<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Houssem Reggai</title>
    <description>The latest articles on DEV Community by Houssem Reggai (@h_coder).</description>
    <link>https://dev.to/h_coder</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3856866%2F1dec3925-53a6-481b-a116-8260d57fe70d.jpeg</url>
      <title>DEV Community: Houssem Reggai</title>
      <link>https://dev.to/h_coder</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/h_coder"/>
    <language>en</language>
    <item>
      <title>Why django-admin startproject Is a Trap</title>
      <dc:creator>Houssem Reggai</dc:creator>
      <pubDate>Wed, 08 Apr 2026 20:57:36 +0000</pubDate>
      <link>https://dev.to/h_coder/why-django-admin-startproject-is-a-trap-1ba4</link>
      <guid>https://dev.to/h_coder/why-django-admin-startproject-is-a-trap-1ba4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The default layout Django hands you is a starting point. Most teams treat it as a destination.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every Django project begins the same way. You type &lt;code&gt;django-admin startproject myproject&lt;/code&gt; and in three seconds you have a tidy directory: &lt;code&gt;settings.py&lt;/code&gt;, &lt;code&gt;urls.py&lt;/code&gt;, &lt;code&gt;wsgi.py&lt;/code&gt;. It is clean. It is simple. And for a project that will never grow beyond a prototype, it is perfectly fine.&lt;/p&gt;

&lt;p&gt;The problem is that most projects do grow. And when they do, the default layout starts to work against you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Project structure is not a style preference. It is a load-bearing architectural decision that determines how easily your codebase can be understood, tested, and extended by people who were not there when it was written.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Three Ways the Default Layout Breaks Down
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The God Settings File&lt;/strong&gt;&lt;br&gt;
The default settings.py is a single file. By the time you have added database configuration, static files, installed apps, logging, cache backends, email settings, third-party integrations, and a few environment-specific overrides, that file is six hundred lines long.&lt;br&gt;
More dangerous than the length is the assumption baked in: that your local development environment and your production environment want the same configuration. They do not. The usual solution is to litter settings with conditionals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BAD: conditional spaghetti in settings.py
&lt;/span&gt;&lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;DEBUG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ENGINE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.db.backends.postgresql&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...}}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;DATABASES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ENGINE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;django.db.backends.sqlite3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...}}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works. Until a developer forgets to set the environment variable and deploys debug mode to production. Until you need a staging environment. Until the nesting is three levels deep and nobody is sure which branch is actually active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Flat App Structure&lt;/strong&gt;&lt;br&gt;
startapp creates apps in the root directory alongside &lt;code&gt;manage.py&lt;/code&gt;. For one app this is fine. For ten, it is a flat list that communicates nothing about your architecture. The deeper problem is apps that are either too large (one giant core app with every model in the project) or too small (one app per database table, with a web of circular imports connecting them).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The Missing Business Logic Layer&lt;/strong&gt;&lt;br&gt;
The default structure gives you models and views. It gives you no guidance on where business logic lives. The result in most codebases: it lives everywhere. Some in models, some in views, some in serializers, some in a file called helpers.py that grows to contain everything that did not fit anywhere else.&lt;/p&gt;
&lt;h2&gt;
  
  
  What a Professional Layout Looks Like
&lt;/h2&gt;

&lt;p&gt;Here is the structure that fixes all three problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myproject/
    .env                      # Environment variables — never commit
    .env.example              # Template — always commit
    requirements/
        base.txt              # Shared dependencies
        local.txt             # Development only
        production.txt        # Production only
    Makefile                  # Common dev commands
    manage.py
    config/                   # Project configuration (renamed from myproject/)
        settings/
            base.py           # Shared settings
            local.py          # Development overrides
            production.py     # Production overrides
            test.py           # Test-specific settings
        urls.py
        wsgi.py
        asgi.py
    apps/                     # All Django applications
        users/
            services.py       # Business logic
            models.py
            views.py
            tests/
        orders/
        ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Three Changes That Matter Most
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Rename the inner directory to config/&lt;/strong&gt;&lt;br&gt;
The inner directory named after your project (myproject/myproject/) tells a new developer nothing. Renaming it config/ communicates its purpose immediately. To do this at project creation time: django-admin startproject config . — note the dot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Group all apps under apps/&lt;/strong&gt;&lt;br&gt;
Add apps/ to your Python path in settings and your apps can be referenced as 'users' rather than 'apps.users'. Your project root stays clean. New developers can orient themselves in seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Split requirements by environment&lt;/strong&gt;&lt;br&gt;
Three files, not one. local.txt starts with -r base.txt and adds django-debug-toolbar, factory-boy, pytest. production.txt adds gunicorn and sentry-sdk. Your production environment never installs your development tools.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;✓  The one rule worth memorizing&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;The config/ directory contains project-level configuration only. The apps/ directory contains all domain code. Nothing else belongs at the project root.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Payoff
&lt;/h2&gt;

&lt;p&gt;These are not cosmetic changes. They are the decisions that determine whether, six months from now, a new developer can navigate your project in an afternoon or spend a week getting oriented. Structure is the first thing everyone inherits and the last thing anyone wants to refactor.&lt;/p&gt;

&lt;p&gt;If you are starting a new project this week, spend the extra ten minutes getting this right. If you are inheriting an existing project, understanding why it is structured the way it is will tell you most of what you need to know about the decisions made before you arrived.&lt;/p&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
