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, including INSTALLED_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 like WorkspaceOwnerPermission that enforce workspace-level access control.
  • Pagination – plane/utils/paginator.py defines 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →