Buttondown Breaks Down Its Custom Django Setup
08 Aug 2026
Buttondown, the newsletter platform, published an engineering blog post titled "What I love about Django," laying out the specific architectural conventions it has built on top of the framework. The post offers a granular look at how one production SaaS company has diverged from Django's defaults while still shipping a working application.
What Buttondown Changed
According to the post, Buttondown uses Django as its web framework but has layered several custom conventions on top of it:
- Middleware-heavy request handling. Buttondown implements middlewares for subdomain-based newsletter routing, UTM/referrer attribution, Content-Security-Policy headers, pageview recording, binding request context to structured logs, and stamping the deployed build version.
- A standardized base model. Every model inherits from a base model that includes a UUID primary key, a creation_date field, public type-prefixed IDs, implicit change tracking, durable provenance, per-field validation hooks, and a soft-delete manager.
- An "actions folder" pattern. Model behaviors live in separate files, organized one verb per module, each exposing a single
call()function. - Function-based, single-file views. Buttondown requires views to live in their own file, be function-based rather than class-based, and expose a function named
view. - Custom test fixtures. The team uses pytest and pytest-django but builds fixtures manually rather than relying on Factory Boy.
- Minimal signals usage. Buttondown reports exactly one signal in play — a lightweight link into django-allauth.
- No forms, no class-based views. The company has opted out of Django's built-in form abstraction and class-based views entirely.
- Hydration-based rendering. For most authenticated views, Django renders a thin HTML shell seeded with data, while Vue handles front-end interactivity. Notably, a single view backs nearly every page of the app, resolving the session and serializing data into
json_scripttags.
Trade-offs Worth Noting
The report flags a few risks embedded in this approach. Deviating this far from Django's defaults — skipping forms and class-based views — could raise onboarding complexity for engineers unfamiliar with the custom conventions. Concentrating so much page-rendering logic into a single view may also centralize complexity and risk into one code path. And building fixtures by hand instead of using an established tool like Factory Boy could add to the long-term maintenance burden of the test suite.
On the upside, the standardized base model and actions-folder pattern may support more consistent, maintainable code across the codebase, and the structured logging plus build-version stamping via middleware could ease debugging and observability. The hydration-based rendering pattern also illustrates one way to blend server-rendered shells with a rich client-side framework like Vue.
What's Missing From the Picture
The source post doesn't say when Buttondown adopted Django or how the architecture evolved to its current state. There's also no detail on team size or engineering headcount, no performance, cost, or reliability metrics tied to these choices, no comparison to alternative frameworks, and no discussion of challenges or trade-offs the team actually encountered while building this system. Readers should treat the post as a snapshot of current conventions rather than a full account of the journey or its costs.
Why Founders Should Care
For early-stage teams building on Django, this case likely offers more of a pattern-matching exercise than a prescriptive playbook. It's plausible that heavily customizing a framework's defaults — dropping forms, class-based views, and third-party fixture tools — can still support a functioning production application, as Buttondown's example suggests. Teams with a strong front-end framework like Vue may find the hydration-based, function-based-view approach appealing as a way to reduce boilerplate.
That said, founders should weigh the likely trade-offs carefully. A single shared view backing nearly every page probably simplifies certain things, but it may also concentrate risk in one code path, so teams considering a similar structure should think through how failures there would be contained. Similarly, opting out of established tools like Factory Boy could mean a heavier long-term testing-infrastructure burden — a cost that may or may not be worth it depending on team size and velocity needs, neither of which Buttondown's post specifies. Given the missing context on adoption timeline, headcount, and performance outcomes, founders should view this as one company's snapshot rather than a validated best practice.