New to Claude Skills? Learn how to install them →

Dposthog on GitHub

Django Startup Time

Free

Optimize Django startup by managing heavy imports effectively.

by posthog37.6k stars on posthog/posthog
1 views
Updated Aug 11, 2026
Get this skill

Free · Opens the source repo

What Django Startup Time does

Django Startup Time is a skill designed to help developers manage heavy imports during the Django application startup process. By keeping heavy imports off the django.setup() path, this skill ensures that every process—whether web, Celery, or CI—remains efficient and responsive. The skill provides a comprehensive checklist and guidelines for deferring imports, wiring signal receivers, and managing the lazy API router, which are crucial for maintaining optimal startup performance.

This skill is particularly useful when working with Django applications that require extensive dependencies or when merging long-lived branches. It emphasizes the importance of adhering to regression guards and offers strategies for adding code without negatively impacting startup time. The detailed documentation, available in the linked internal docs, serves as a valuable resource for understanding the mechanisms at play, including lazy routers and model registration.

Developers can leverage this skill to ensure that their applications do not suffer from slow startup times due to unnecessary imports. By following the provided guidelines, they can measure and manage the impact of new dependencies, ensuring that only essential components are loaded during startup. This not only improves application performance but also enhances the overall user experience by reducing latency in web requests and background processes.

In summary, Django Startup Time is an essential skill for Django developers who want to optimize their application's startup performance by effectively managing imports and dependencies. With its structured approach and detailed documentation, this skill is a must-have for any team looking to maintain high-performance standards in their Django projects.

When to use it

Use this skill when developing or maintaining Django applications with heavy dependencies or when merging branches that may affect import performance.

When not to use it

This skill may not be necessary for small Django projects with minimal dependencies or when startup performance is not a concern.

What you can build with it

Optimizing a Large Django Application

When developing a large Django application with many dependencies, use this skill to manage imports effectively and keep startup times low.

Merging Long-Lived Branches

During the merge of a long-lived branch, utilize this skill to ensure that heavy imports do not negatively impact the startup process.

Maintaining Performance in CI Environments

In CI environments where multiple processes run, apply this skill to maintain optimal startup performance across all processes.

How to install Django Startup Time

View source

1. Install with the skills CLI

npx skills add posthog/posthog/django-startup-time --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by posthog

Django startup time

Full background — the four mechanisms (lazy router, model registration, receiver wiring, boot GC window), the regression guards, how to add code without regressing them, how to measure, and the detailed trap write-ups: docs/internal/django-startup-time.md. That doc is the single source of detail; this skill is the trigger and the checklist.

The guards live in posthog/test/test_startup_import_budget.py. When one fails, defer the import — don't remove an entry to dodge it. Conversely, when you deliberately defer a significant heavy lib off setup, add it to FORBIDDEN_AT_SETUP (after confirming it's absent from a bare django.setup()). New imports nobody has named yet are caught by test_no_new_heavy_imports_at_setup: any package not in posthog/test/setup_import_baseline.txt costing ≥100ms at setup fails the build — defer it; baseline only what every process genuinely needs at setup.

Defaults when adding backend code

  • New app/product: register models from models/__init__.py, wire receivers from AppConfig.ready(), keep ready() light.
  • New receiver: wire it at the owning AppConfig.ready(), from a dedicated light module (activity_logging.py, signals.py) — never via the API/viewset module, even if it looks light today; those accumulate heavy imports and ready() silently inherits them.
  • New heavy dependency (vendor SDK, Temporal/AI/ClickHouse, pandas/pyarrow/scipy): import it function-locally on the path that uses it with # noqa: PLC0415, never at module scope.
  • Schema types on a setup-path module: enums from posthog.schema_enums (cheap); pydantic models from posthog.schema only inside the method that uses them. No module-level from posthog.tasks... on setup paths — CeleryQueue lives in posthog.celery_queues.
  • New viewset/route: it no longer loads at setup — don't rely on import side effects; routes go in rest_router.py, not the __init__.py shim.
  • Any deferral relocates cost — ask which process pays now, on what path, and whether that path is latency-sensitive (background workers paying lazily: fine; web workers paying on first requests: usually not).

Traps to check before you commit

One line each — the doc has the full write-up and the fix recipe for every entry.

  • ready() re-drags a heavy dep onto startup — importing a receiver's module runs its module-level imports too; re-measure after wiring.
  • Silent receiver loss — lost receivers don't error; reproduce in a process that does NOT build the router (manage.py shell, celery, migrate).
  • Models vanishing from the registry — a model reachable only via a viewset import breaks makemigrations/admin/mypy; import it from models/__init__.py, then dmypy stop.
  • Lazy-router merge conflicts — keep the __init__.py shim, port master's aggregator deltas into rest_router.py.
  • Aggregator package __init__s — shim with PEP 562 __getattr__, via importlib.import_module and an __all__ whitelist (catch-alls recurse or deadlock on sibling imports).
  • Serializer field kwargs run at importchoices=... inputs can't be function-locally deferred; move the constant to an import-light module.
  • Vanished re-exports — regenerating/shimming a module drops names other code imported from it incidentally (e.g. schema.BaseModel); grep for consumers of the old namespace, including relative-import spellings.
  • Patch targets break on call-time imports@patch("mod.helper") stops intercepting once mod imports helper at call time; patch the defining module instead.
  • Snapshot regen on a bad merge — regenerate .ambr against the merged branch, then confirm it isn't masking a regression.
  • Measuring the wrapper, not the work — time inside the process; importtime + tuna, not pyinstrument; grimp for cycles and door enumeration.
  • Phantom importtime costs from GC — re-capture with gc.disable(); if the cost vanishes, the finding is GC, not the module. Keep the entrypoints' GC window closing in a finally.

Frequently asked questions about Django Startup Time

Similar skills