Plain

A Django-inspired Python web framework rebuilt from scratch for explicit, typed, agent-friendly application code.

Framework
PyPI
v0.159.0
1,043stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
75/100Good
Development Activity100
Maintenance52
Community56
Maturity52
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
85/100Excellent
Architecture85
Code Quality80
Innovation88
Learning Curve90

Plain is a Python web framework for building full-stack applications, originally forked from Django in 2023 and reshaped over years of production use at PullApprove. It ships class-based views, a dedicated typed URL router, forms, CSRF protection, its own HTTP(/2) dev server, and a package registry — the same architectural shape as Django, rebuilt with modern Python (3.13+) typing throughout.

Where Plain diverges is its focus on explicitness and AI-agent workflows: typed model fields that carry through views and forms, OpenTelemetry tracing and slow-query detection built in from the start, and on-demand CLI documentation (plain docs) designed to be read by coding agents as easily as by humans. A first-party ecosystem of 30+ companion packages (Postgres ORM, auth, sessions, jobs, email, htmx, Tailwind, admin) covers the rest of a production stack, all sharing one typed API surface.

What You Get

  • Full request/response cycle: class-based Views, a typed URL Router, and a WSGI/HTTP-2-capable dev server (its own arbiter + worker processes)
  • Forms, CSRF protection, and a package registry modeled directly on Django’s installed-apps system
  • Built-in OpenTelemetry tracing, slow-query detection, and preflight checks that run identically in CI and locally
  • A plain CLI with on-demand framework docs (plain docs <package> --api --search), a project scaffolder, and agent-oriented slash commands
  • Access to 30+ first-party companion packages (Postgres ORM, auth, sessions, jobs, email, htmx, Tailwind, admin) sharing one typed API surface

Common Use Cases

  • Full-stack web apps that want Django’s batteries-included structure but with modern Python typing and less implicit framework magic
  • Teams building primarily with AI coding agents that need typed signatures and searchable, on-demand docs the agent can query from the CLI
  • Incrementally migrating an existing Django app, given Plain’s shared lineage and similar App/URL/View conventions
  • Small teams standardizing on one opinionated stack (Postgres, Jinja2, htmx, Tailwind CSS) to avoid re-deciding tooling per project

Under The Hood

Architecture - Plain mirrors Django’s request-handling shape but rebuilds each layer from scratch: a PackagesRegistry (plain/packages/registry.py) loads installed packages the same way Django’s app registry does, View subclasses (plain/views/base.py) dispatch to typed get/post/put/patch/delete handlers computed at __init_subclass__ time via implemented_methods, and a dedicated urls/ module (routers.py, resolvers.py, converters.py, reverse.py) provides a typed Router class in place of Django’s flat urls.py lists. Rather than delegating to an external WSGI/ASGI server, the server/ package ships its own arbiter and worker processes (arbiter.py, workers/, connection.py) with native HTTP/2 support via the h2 library — a deliberate choice to keep the whole request lifecycle, from socket to response, inside one framework boundary.

Tech Stack - The core plain package targets Python 3.13+ and keeps its direct dependency list intentionally small: click (CLI), h2 (HTTP/2), opentelemetry-api + opentelemetry-semantic-conventions (tracing), and watchfiles (dev-server reload) — everything else (Postgres ORM, Jinja2 templates, htmx, Tailwind, auth) lives in separate first-party packages in the same monorepo (plain-postgres, plain-templates, plain-htmx, etc.), installed a la carte. The project uses uv for dependency management, ruff for linting/formatting, ty for type checking, and hatchling as the build backend, with modern typing syntax (from __future__ import annotations, PEP 695 generics like View[HandlerResult = Response]) used throughout the codebase.

Code Quality - The core package carries 31 test files under plain/tests/ (internal/, app/, public/ subdirectories) covering CSRF, URL resolution, HTTP host handling, CLI commands, and OpenTelemetry span emission, plus a companion plain-pytest package providing test fixtures/helpers for downstream apps. Every submodule (agents, cli, csrf, forms, http, packages, preflight, server, urls, views, etc.) ships its own README.md documenting its API, and error handling favors explicit, named exceptions (ImproperlyConfigured, PackageRegistryNotReady) over generic exceptions or silent failure. A CODEREVIEW.toml and .claude/ rules directory further indicate an established internal review/quality process.

API Design - Plain’s public API deliberately mirrors Django’s naming (View, Router, PackageConfig) to minimize relearning cost for the framework’s primary migration audience, while trimming boilerplate — a new project starts with a single uvx plain-start command or an agent-driven install script. Its standout DX choice is agent-oriented tooling: plain docs <package> --api --search surfaces typed, on-demand documentation from the command line, and the repo ships slash-command “skills” (/plain-install, /plain-upgrade, /plain-optimize, /plain-bug) plus short .claude/rules/ guardrail files, treating AI coding agents as a first-class API consumer alongside human developers.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search