How the Backend of Plane Is Developed and Organized
The Plane backend is a monolithic Django application located in apps/api that exposes a REST API via Django REST Framework, processes background jobs through Celery, and organizes code into domain-driven modules with environment-specific settings.
The open-source project management tool Plane stores its server-side logic in a single, well-structured Django project. Understanding how the backend of Plane is developed and organized reveals a clean architecture that separates web concerns, data models, and asynchronous processing while maintaining a cohesive monolithic design.
Project Structure and Entry Points
The backend lives inside the apps/api directory and follows standard Django conventions with custom organizational patterns for scalability.
WSGI and ASGI Bootstrapping
Plane supports both synchronous and asynchronous server interfaces. The entry points reside in the core plane package:
plane/wsgi.py– Configures the WSGI application for traditional synchronous request handling.plane/asgi.py– Configures the ASGI application for asynchronous capabilities, required for WebSocket support or async views.
Both files import settings from the hierarchical configuration system and initialize the Django application accordingly.
Hierarchical Settings Management
Instead of a single settings.py, Plane uses a layered approach in plane/settings/:
common.py– Contains base configuration shared across all environments, includingINSTALLED_APPS, middleware stack, DRF settings, and logging formatters.local.py– Development overrides enabling debug mode, SQLite support, and the Django debug toolbar.production.py– Production hardening with PostgreSQL database configurations, Sentry integration, and structured JSON logging.
This pattern allows developers to run python manage.py runserver --settings=plane.settings.local locally while production deployments automatically use the production module.
API Layer Implementation
The Plane backend exposes functionality through a JSON API built on Django REST Framework (DRF), with clear separation between routing, business logic, and data serialization.
URL Routing and Viewsets
Central dispatch happens in plane/urls.py, which includes app-specific routers. The primary API implementation resides in the plane/web/ package:
# apps/api/plane/web/views.py
from rest_framework import viewsets
from plane.db.models import Issue
from plane.utils.serializers import IssueSerializer
class IssueViewSet(viewsets.ModelViewSet):
"""CRUD API for issues."""
queryset = Issue.objects.select_related("project", "workspace")
serializer_class = IssueSerializer
permission_classes = [WorkspaceOwnerPermission]
Registration occurs in plane/web/urls.py using DRF's router pattern:
# apps/api/plane/web/urls.py
from rest_framework.routers import DefaultRouter
from .views import IssueViewSet
router = DefaultRouter()
router.register(r"issues", IssueViewSet, basename="issue")
urlpatterns = router.urls
Permissions and Utilities
Reusable components live in plane/utils/ to prevent code duplication:
- Permissions –
plane/utils/permissions/contains classes likeWorkspaceOwnerPermissionthat enforce workspace-level access control. - Pagination –
plane/utils/paginator.pydefines consistent pagination behavior across list endpoints. - Filtering –
plane/utils/filters/houses DRF filter backends for complex query operations.
Data Layer and Domain Models
Plane organizes its database schema through Django models grouped by domain in plane/db/models/. This directory contains the core entities including:
- Workspace – Top-level organizational containers
- Project – Software projects within workspaces
- Issue – Tickets and work items with relationships to projects and users
- User – Authentication and profile data
The models directory follows a flat structure where each major domain object has its own file, making imports predictable and maintaining clear boundaries between business entities.
Background Processing Architecture
Heavy or time-consuming operations are offloaded to Celery workers to keep HTTP responses fast.
Celery Configuration
The task queue is configured in plane/celery.py, which defines the Celery app instance, broker URLs (typically Redis), and task routing rules.
Task Organization
Background tasks reside in plane/bgtasks/ and are auto-discovered via plane.bgtasks.apps.BgtasksConfig listed in INSTALLED_APPS. Typical implementations follow this pattern:
# apps/api/plane/bgtasks/email_notification_task.py
from celery import shared_task
from plane.utils.email import send_email
@shared_task
def send_issue_notification(user_id, issue_id):
"""Send an email when an issue gets assigned."""
send_email(
to_user_id=user_id,
subject="You've been assigned a new issue",
template_name="issue_assignment.html",
context={"issue_id": issue_id},
)
Common task categories include:
- Email notifications for issue assignments and mentions
- Webhook dispatch to external integrations
- Data export generation (CSV, JSON)
- Periodic cleanup and maintenance jobs
Supporting Infrastructure
Beyond the core request-response cycle, the backend includes several specialized modules.
Middleware and Request Handling
The plane/middleware/ directory contains custom WSGI/ASGI middleware for:
- Request body size limits
- Request ID injection for distributed tracing
- Response time logging
License and Feature Gating
The plane/license/ app manages commercial features in the self-hosted edition. It stores instance-specific configuration and gates premium functionality through license key validation, keeping open-source and enterprise code paths separate but co-located.
API Documentation
Plane uses drf-spectacular to generate OpenAPI 3.0 specifications automatically. Extensions and schema customizations live in plane/utils/openapi/, ensuring the Swagger UI always reflects the current state of the JSON API without manual maintenance.
Summary
The backend of Plane demonstrates a mature Django monolith architecture that prioritizes:
- Configuration inheritance through layered settings files separating development from production concerns
- Modular API design using DRF viewsets with centralized permission and pagination utilities
- Asynchronous scalability via Celery for email, webhooks, and data exports
- Domain-driven organization with models, background tasks, and web views separated into focused packages
- Automatic documentation generated from code via OpenAPI extensions
All components reside under apps/api/plane, providing a single deployable unit while maintaining clear internal boundaries that facilitate testing and feature development.
Frequently Asked Questions
Is the Plane backend a microservices architecture?
No, the Plane backend is intentionally a monolithic Django application. All functionality—from issue management to webhook processing—runs within a single deployable unit in apps/api. Background tasks are handled asynchronously via Celery workers, but these share the same codebase and database, maintaining the operational simplicity of a monolith while gaining horizontal scaling for background work.
Why does Plane use separate settings files instead of environment variables alone?
Plane uses a hierarchical settings structure (common.py, local.py, production.py) to keep environment-specific logic organized and reviewable. While environment variables control secrets and hostnames, the settings files define structural differences like installed apps (debug toolbar in local), database engines (SQLite vs PostgreSQL), and logging formats. This approach prevents conditional spaghetti code and makes configuration differences explicit in version control.
How does Plane handle database migrations and model changes?
Database models are defined in plane/db/models/ and use Django's standard ORM migration system. Since the backend is a monolith, migrations live alongside the models in the respective app directories. The manage.py entry point provides the standard makemigrations and migrate commands, allowing both development and production deployments to evolve the schema predictably.
What message broker does Plane use for Celery tasks?
According to the source configuration in plane/celery.py, Plane typically uses Redis as the Celery broker and result backend. This setup handles task queuing for background operations like email notifications and webhook dispatches, providing the durability and visibility required for production task processing while maintaining low latency for task submission from the web processes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →