What Django upgrade cadence would you set for a team's services: LTS-to-LTS jumps, or following every feature release, and why?
answer
- eight months versus about two years
- size of each jump
- security window of each choice
- third-party package support
- CI against the next release early
basics
~20 sA trade-off: LTS-to-LTS means upgrading about every two years with three releases of change per jump; tracking feature releases means small steps every eight months and short support windows. A common middle path deploys the LTS and tests newer releases in CI.
solid answer
~50 sIt is a trade-off between upgrade frequency and upgrade size. **LTS-to-LTS** (4.2 → 5.2 → 6.2) means one planned upgrade about every two years and a long security window, but each jump spans three feature releases, and teams that ignore warnings in between face a large, deadline-driven project. **Following each feature release** gives small, routine steps and access to new features, but every eight months and with a short support window, so falling one step behind quickly becomes falling out of support; it also needs dependencies that keep up. My default for business services is **deploy the LTS, keep the codebase current**: warnings as errors for Django's own deprecations, a CI job against the latest feature release (and its pre-releases), and a fixed budget each release cycle, so the LTS jump becomes a version bump. Services that want new features, or that have strong test coverage, can track feature releases instead.
code
bash · 4 lines# CI matrix idea: blocking job on the LTS, early-warning job on the newest release
DJANGO_SPEC="Django>=5.2,<5.3" ./ci/test.sh # required
DJANGO_SPEC="Django>=6.1,<6.2" ./ci/test.sh # allowed to fail, reviewed weekly
DJANGO_SPEC="Django>=6.2a1,<6.3" ./ci/test.sh # pre-releases when they appeargo deeper
Recall that teams choose between staying on LTS releases and following each feature release, and that each choice has a different support window.
Explain the trade-off in numbers: roughly two years versus eight months between upgrades, three releases of change versus one, and the different security windows.
Describe the mechanics that keep either choice cheap: warnings as errors, CI against newer releases and pre-releases, prompt patching, interpreter planning.
Own the policy: allowed versions, maximum lag, inventory, owners and budgets, and expiring exceptions, so upgrades stay routine across many services.
## The two poles Django's release model offers two natural cadences: | | LTS-to-LTS | Every feature release | |---|---|---| | Upgrade frequency | About every two years (4.2 → 5.2 → 6.2) | About every eight months | | Size of each upgrade | Three feature releases of change | One feature release | | Security window | Typically three years per LTS | While among the two newest feature releases | | New features | Up to two years late | Available quickly | | Third-party packages | Usually support the current LTS | May lag a new release for a while | | Main risk | A large, deadline-driven jump | Falling behind quickly means falling out of support | Neither is universally right; the choice depends on test coverage, how much the service benefits from new features, how many services the team owns, and how many Django-related dependencies each one has. ## The failure mode to design against The expensive pattern is **LTS without continuous work**: the team stays on 4.2, ignores deprecation warnings for three years, and discovers at the end of the support window that the upgrade touches every app, three feature releases' worth of removals, a Python upgrade and a dozen packages — under time pressure. The cadence policy exists to prevent that, whichever pole you choose. ## A pragmatic default: deploy LTS, stay current For most business services a hybrid works well: 1. **Production runs the current LTS**, taking every patch release promptly. 2. **Django's own deprecation warnings fail CI** (a filter on `RemovedInDjango…Warning` classes in the test settings), so new warnings are fixed as they appear rather than accumulating. 3. **A non-blocking CI job runs the suite against the newest feature release**, and against alphas and release candidates when they appear. Breakages surface early and cheaply, and they can be reported upstream while still in the pre-release phase. 4. **A fixed slice of each release cycle** (for example, one sprint every eight months) goes to whatever that job found. 5. **The interpreter version is part of the plan**, since releases raise their Python floor (6.0 needed 3.12). With this in place, moving to the next LTS is mostly a version bump plus the documented backwards-incompatible changes. ## When to track feature releases instead - The service benefits from new Django features soon after they ship (for example the built-in CSP support in 6.0 or fetch modes in 6.1). - Test coverage is strong and deploys are cheap. - The dependency set is small or well maintained. Tracking feature releases then keeps every step small — but the policy must include the rule that no service falls more than one feature release behind, because the support window is short. ## Making the policy enforceable - **One document** stating the allowed releases in production and the maximum age behind the newest supported release. - **An inventory** of services with their Django and Python versions, reviewed each release cycle. - **Owners and budgets**, not good intentions: each service's team owns its upgrade, with time allocated. - **Exceptions with expiry dates**, for services blocked by a dependency, tracked like any other risk. ## Signs the policy is working - Every service runs a supported Django release, and the latest patch of it, within days of a security release. - The count of Django deprecation warnings in each service is zero, or trending there. - The early-warning CI job is green most of the time, and its failures are triaged within a release cycle. - The last LTS move took days of focused work, not a quarter. - Exceptions to the policy have owners and expiry dates, and few of them exist. ## What to say in an interview Name the trade-off, name the failure mode, and propose the mechanism — CI on the next release, warnings as errors, budgeted time — rather than only a preference for one pole.
- A service is still on 4.2 in September 2026; how do you prioritise it?As a security risk first: Django's FAQ gives April 2026 as the end of 4.2 LTS security support, so new vulnerabilities will not be fixed. Schedule the 4.2 to 5.2 upgrade as planned work with an owner and a date, apply compensating controls meanwhile, and record why the policy failed so the fix is structural.
- Why run CI against Django pre-releases if you only deploy the LTS?Breakages found in an alpha or release candidate are cheap: nothing is deployed, the fix can be scheduled, and a genuine regression can be reported to Django before the final release. Found two years later during an LTS jump, the same breakages arrive together, under deadline, mixed with dependency and interpreter changes.
saying these in an interview costs you the question
- Staying on an LTS means no upgrade work until the window ends
- Following every feature release is always too risky for production
- LTS and feature releases have the same security window
- An upgrade policy only needs to name the target Django version
- Deprecation warnings can be left until the LTS upgrade project