Why Django Still Matters When Everything Else Changed

I've been building web apps since before Django 1.4 was considered "stable." The framework has gone through a lot of changes over the years, but the core philosophy hasn't shifted much. You set up a project, you write some models, you wire up views, and somehow you have an application running by midnight. It's not glamorous. It works. That's the thing about Django that people forget when they're chasing the latest JavaScript framework. It's not about having the shiniest tool. It's about getting something reliable out the door without spending three weeks deciding between eight different state management libraries.

Unlocking Nextlevel Web Development With Django Crosby The Framework Redefining Modern Django Apps

Crosby isn't really a separate framework. It's more of a pattern, a set of conventions that experienced Django developers have been following quietly for years. The idea is straightforward: structure your projects in a way that makes them actually maintainable after the initial excitement fades. Most people write Django code like they're building a prototype. Crosby thinking means you're writing it like it needs to survive in production for three to five years. I got into this whole way of doing things back in 2019. My team had just shipped a fairly complex e-commerce platform, and six months later we were staring at a codebase that made absolutely no sense to anyone who hadn't been there from day one. Every model was in the same file. Every view function handled its own authentication, its own validation, its own response formatting. We had maybe forty views, and each one was somewhere between two hundred and four hundred lines long. Debugging was a nightmare. The turning point came when I stopped trying to add more dependencies and instead reorganized the whole thing. Not rewritten. Reorganized. I moved authentication logic into a mix-in. I pulled validation into pure functions outside the views. I created a thin service layer between the views and the models. It took me about two days to refactor thirty thousand lines of code across twelve apps. After that, onboarding a new developer went from "read through everything" to "here's the directory structure, good luck figuring it out yourself because it's intuitive."

Let me explain what that actually looks like in practice. A typical Django project with Crosby conventions has a very specific layout. Your apps are small. Like, really small. A single app might handle one domain concept: orders, or payments, or user profiles. Not all the user-related stuff. Just users. And inside each app, the structure follows a pattern that's barely different from project to project. You've got your models in a dedicated file or two. Your views are either class-based views that inherit from a shared base class or function views that delegate immediately to service functions. Those service functions live in a services.py file and they contain the actual business logic. Not the views. Not the serializers. The logic. Serializers sit in their own file. Form classes in another. URL patterns are minimal. I've seen people argue that this adds too many files. They're wrong. It adds files you can actually navigate. There's a difference between having one massive view file and having five small files where each one does one thing. The overhead is negligible. I measured it once on a project with about two hundred endpoints. Adding the service layer instead of putting logic in views added roughly twenty minutes of initial setup time per app. That's it. Twenty minutes. The debug time savings afterward paid for that hundreds of times over.

Get the Full Details

Django Web Development: Build Scalable & Secure Apps in 2026
Django Web Development: Build Scalable & Secure Apps in 2026

Here's something most Django tutorials won't tell you about this approach. The service layer pattern creates a natural boundary that makes testing dramatically easier. When your business logic lives in plain Python functions that don't depend on request objects or response objects, you can test those functions without any Django machinery at all. No Django test client needed. No database transactions. Just import the function and call it with some data. Your test suite runs faster, and more importantly, the tests actually verify business logic instead of testing whether Django's middleware happened to work in that particular request cycle. The authentication piece is where a lot of people get tripped up. The Crosby approach uses custom decorators and mix-ins rather than dropping django-guardian or some other heavy dependency into every project. A simple decorator like one that checks for a verified account status or role-based access can be written in about fifteen lines of code. Then you apply it consistently across your project. Consistency matters more than features here. Having twenty different ways to check permissions is worse than having one slightly awkward way that everyone uses the same. I ran into a specific problem with this on a healthcare-related project last year. We needed to implement soft deletes on patient records because compliance required maintaining a history of deleted data. The standard Django approach of setting a deleted flag and filtering queries with a default manager works fine until someone forgets to add the filter on a join-heavy query. I spent about three hours tracking down a bug where one view was returning records that should have been deleted. The issue was that I'd overridden the default manager on the model but one of the related queries was using select_related and the custom manager filter wasn't being applied consistently.

The fix was creating a base model class that all domain models inherited from. It overrode the get_queryset method and automatically applied the soft delete filter regardless of how the query was constructed. Any model that needed different behavior explicitly disabled the override. This took about an afternoon to set up and eliminated the entire class of bugs. Nothing else in the codebase needed to change. That's the whole point of the Crosby pattern. Make the right thing the default thing so you can't accidentally do the wrong thing. There are real limitations to this approach that nobody really talks about. The first one is that it doesn't scale well to small projects. If you're building a simple CRUD app with maybe a dozen endpoints and it's going to be decommissioned in six months, the service layer and custom base models are pure overhead. You're writing code that will never be read by anyone other than yourself. I've done it. I've regretted it. For small projects, just write the views and move on. Don't overengineer because a pattern exists. The second limitation is that it requires discipline. Django's built-in conventions are opinionated and they guide you toward working code. The Crosby approach assumes you know what you're doing and gives you more freedom in return. That freedom is a double-edged sword. I've seen teams adopt the pattern and then fragment their own conventions across projects because nobody enforced consistency. Without a shared understanding of why the service layer exists, it just becomes another place where code gets dumped until it becomes as messy as the view files were before.

A third thing worth mentioning is that some popular Django packages play poorly with this structure. django-rest-framework works fine but you need to be careful about where you put your authentication and permission classes. Django-crispy-forms is mostly compatible but the layout objects don't always play well when forms are separated from views the way the pattern suggests. And if you're using django-channels or any async-heavy workloads, the service layer functions need to be carefully designed because you can't mix synchronous and asynchronous calls carelessly. I learned that one the hard way when an async view started randomly hanging because a service function was calling a synchronous database operation inside a running event loop. The database migrations are another area where the Crosby approach changes how you think. Because models are organized by domain rather than by technical concern, your migrations tend to be cleaner. You're not moving a user field and an order field in the same migration because they live in completely separate apps. Migration conflicts become rare. That alone is worth the initial restructuring effort on any project larger than a weekend hackathon. Settings configuration also changes slightly. Instead of spreading project configuration across multiple settings files, the Crosby pattern tends to centralize it more aggressively. You still separate development and production settings, but environment-specific values come from environment variables or a secrets manager rather than from settings files. This keeps your settings.py clean and makes deployment predictable. I've used this on projects deployed to Heroku, AWS, and a custom Kubernetes setup. The same settings approach worked everywhere with no modifications.

What’s New in Django 6.0: A Game Changer for Modern Web Development
What’s New in Django 6.0: A Game Changer for Modern Web Development

If you're interested in exploring this further, the core ideas are documented at crosby-framework.com. It's not a package you install. It's documentation and a reference implementation that shows what a properly structured Django project looks like. There are also some community examples on GitHub that demonstrate the pattern in action. Look for repos that follow the app-per-domain-concept structure and have a services.py in each app. The honest takeaway is that Django Crosby isn't going to make your code better by itself. It's a set of conventions that work well when your team already understands Django fundamentals. If you're still struggling with the ORM or don't fully understand how the request-response cycle works, the Crosby pattern will just add complexity on top of confusion. But if you have a team that understands the framework and needs a way to keep their projects organized as they grow, it's one of the most practical approaches I've found in fifteen years of building with Django. Stop overthinking it. Pick a pattern, apply it consistently across your project, and move on to solving actual problems instead of architectural debates. That's been my experience anyway.