I restructured our backend's folders exactly once. Three years and 20 engineers later, I still haven't touched it again.

Organize by domain/feature first and technical layer second. A folder should map to an owner, not a technical concern.

architecture

Problem

New engineers couldn’t find where code belonged. Every PR added a file to whatever folder felt closest, and within a year “utils” and “services” had become dumping grounds nobody trusted.

Why it happens

  • Folders get organized by technical layer (controllers, services, models) instead of by business domain, so a single feature scatters across five folders
  • There’s no owner of the structure, so every engineer makes a locally reasonable choice that’s globally inconsistent
  • Nobody revisits the structure until it’s already painful, and by then a rewrite feels too risky
  • “Shared” or “common” folders get created for anything that doesn’t obviously belong somewhere else, and become the default answer within weeks

Better approach

  • Organize by domain/feature first (billing/, auth/, notifications/), technical layer second
  • Each domain folder owns its own controllers, services, and models: no cross-domain reaching in
  • Put a one-line README in each top-level folder describing what belongs there and what doesn’t
  • Review new top-level folders in PRs the same way you’d review a new dependency

Example

A billing feature touching invoices, refunds, and webhooks lives entirely inside billing/, with billing/invoices.py, billing/refunds.py, billing/webhooks.py. A new engineer working on refunds never has to open auth/ or notifications/ to find what they need.