Over-Engineering Kills Developer Velocity

In a FastAPI codebase following strict clean architecture, layers like repositories, services, use cases, domain entities, DTOs, mappers, ports, and adapters forced traversing seven files across four layers just to fetch a user by ID. Adding a single bio field to the user profile demanded changes in the domain entity, repository interface, implementation, mapper, DTO, use case, and service—seven modifications total. This rigidity turned feature development into architecture maintenance, causing two developers to quit as the team spent more time upholding patterns than delivering value.

Contrast this with Django: updating a model and serializer takes one line each, enabling rapid iteration without layer sprawl. The key insight is that clean architecture's dependency inversion and separation of concerns shine in massive, long-lived systems with frequent framework swaps, but for most Python apps, it creates friction that outweighs benefits, especially early on.

Pragmatic Refactoring Beats Rigid Patterns

Refactoring a Django monolith with zero architecture revealed the opposite extreme's pains: business logic crammed into views, database queries scattered in templates, and a 20-line pricing calculation copy-pasted across four files. Changing the pricing formula meant hunting duplicates, risking inconsistencies. Yet, this raw structure allowed quick fixes without pattern enforcement.

The author's approach: start simple and layer architecture only when pains like duplication or tight coupling emerge. For Python projects, use Django's ORM and serializers for 80% of needs—they handle mapping and validation implicitly. Reserve full clean architecture for teams >10 or apps with >100k LOC, where evolving requirements justify the overhead. Trade-off: upfront simplicity risks tech debt, but refactoring targeted messes (e.g., extracting pricing logic once pains hit) preserves momentum better than premature abstraction.