{"course":{"slug":"cloud-and-shipping","title":"Shipping It","url":"https://addaly.com/learn/cloud-and-shipping","level":"intermediate","track":"build"},"lesson":{"slug":"upgrades-and-end-of-life","title":"The upgrade you keep postponing","url":"https://addaly.com/learn/cloud-and-shipping/upgrades-and-end-of-life","position":68,"of":74,"minutes":9,"answer":"End-of-life dates are published years ahead and arrive anyway, so keep dependencies moving weekly, take one major upgrade a quarter in small steps, rehearse database upgrades because they do not roll back.","keyIdea":"End-of-life dates are published years ahead and arrive anyway, so keep dependencies moving weekly, take one major upgrade a quarter in small steps, rehearse database upgrades because they do not roll back, and write down any version you are deliberately staying on."},"body":{"format":"markdown","text":"## Software has an expiry date, and it is published\n\nEvery language runtime, database and framework you depend on has a documented end-of-life date. After it, security fixes stop. The dates are public years in advance, and they arrive anyway, because nobody's calendar has them in it.\n\nAs a shape rather than a specific list: Node.js major versions get roughly three years, with a long-term-support line; Python versions get five; Ubuntu's long-term-support releases get five years of standard support; PostgreSQL major versions get five; Django and Rails maintain a small number of recent versions with a long-term line. The exact months change: look them up, and record the ones you depend on.\n\nThe fact that matters: an unsupported runtime does not stop working. It stops receiving patches. Your site runs exactly as well as before, which is precisely why it stays there.\n\n## The debt compounds, quietly\n\nA framework upgrade skipped once is a week of work. Skipped four times, it is a rewrite, because the upgrade guides assume you are coming from the previous version, the migration tooling has been removed, the packages you depend on have moved on, and nobody remembers why the custom patch in the middle exists.\n\nSo the practical rule is counter-intuitive to how it feels: **upgrade more often, in smaller steps**, and never skip a major version if the project's guide says not to.\n\n## A cadence that survives contact with real work\n\n- **Weekly, automatic:** patch and minor version updates for dependencies, applied by a bot, merged when CI is green. Dependabot and Renovate are free and do this. Group them so it is one pull request, not thirty.\n- **Monthly, 30 minutes:** read the failures. A minor update that breaks a test is a signal, not an inconvenience.\n- **Quarterly, half a day:** one major upgrade (the runtime, the framework, or the database) chosen from the list, done properly with a staging run and a rollback plan.\n- **Annually, one hour:** update the end-of-life dates for everything you depend on and put the next two into the calendar with a name against them.\n\nThat annual hour is the whole trick. The upgrade is not hard; being surprised by it is.\n\n## Database major versions are the one to plan\n\nApplication upgrades roll back. A database major version upgrade often does not, because the data directory format changes. Three honest options:\n\n- **A managed provider's in-place upgrade.** Simplest, and it needs a maintenance window measured in minutes, plus a tested backup taken immediately before.\n- **Dump and restore.** Reliable, slow, and the downtime scales with your data size. Fine at a few gigabytes, painful at a hundred.\n- **Logical replication to a new instance, then a cutover.** Near-zero downtime, more moving parts, and the approach worth learning once you are past the size where a dump is quick.\n\nWhichever you choose, rehearse on a copy. Time it. Write the number in the runbook, because that number is your maintenance window.\n\n## Deprecations from providers arrive on their schedule\n\nAPI versions get retired. Model snapshots get withdrawn. TLS versions and cipher suites get disabled by browsers. Payment providers change required flows to meet new regulation. Cloud instance families get deprecated.\n\nMost of these come with months of notice, sent by email, to an address somebody set up in year one. Two habits: make sure provider notices reach more than one person and get read, and put every announced retirement date into the same calendar as your expiry list. This is the same discipline as the expiry lesson, applied to other people's deadlines rather than your own.\n\n## Upgrading is also how you keep a site fast and cheap\n\nIt is easy to frame upgrades purely as risk avoidance, which makes them feel like a chore with no upside. In practice, runtime and database major versions routinely bring real performance and memory improvements, deprecate a workaround you were carrying, or add something that lets you delete code. A quarterly upgrade slot is one of the few maintenance activities that regularly makes the system smaller.\n\n## When you deliberately do not upgrade\n\nSometimes staying is correct: a library that is finished and unmaintained but does its job, a version whose successor removed something you need, a system nearing retirement anyway. That is a legitimate decision when it is a decision.\n\nWrite it down where the handover documents live: what you are staying on, why, what the risk is, and when you will look again. The difference between technical debt and technical negligence is whether somebody chose it on purpose and left a note."},"check":{"question":"A team skips three consecutive major versions of their web framework, then attempts the upgrade in one jump. Why is this usually harder than three separate upgrades?","options":[{"text":"The final version contains more code, so more of the application must change","correct":false,"tempting":"Size is not the issue; the difficulty comes from how changes are documented and tooled, not from how much code exists."},{"text":"Upgrade guides and migration tooling target adjacent versions, and deprecation shims from intermediate releases have already been removed","correct":true},{"text":"Skipping versions invalidates the lockfile, forcing every dependency to resolve afresh","correct":false,"tempting":"Dependencies do move, but a lockfile can be regenerated at any point; that is a mechanical step rather than the source of the difficulty."},{"text":"Security patches from the skipped versions must be applied individually first","correct":false,"tempting":"Patches are cumulative within a line and are superseded by the newer major; there is no backlog to replay."}]},"prev":{"slug":"managed-or-self-hosted","title":"What to run yourself, and what to pay for","url":"https://addaly.com/learn/cloud-and-shipping/managed-or-self-hosted"},"next":{"slug":"the-cost-per-user","title":"What one user costs you","url":"https://addaly.com/learn/cloud-and-shipping/the-cost-per-user"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'The upgrade you keep postponing', in Shipping It. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/cloud-and-shipping/upgrades-and-end-of-life (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"The upgrade you keep postponing","courseTitle":"Shipping It","module":{"index":8,"slug":"running-it-for-years","title":"Running it for years"},"answer":null,"keyIdea":"End-of-life dates are published years ahead and arrive anyway, so keep dependencies moving weekly, take one major upgrade a quarter in small steps, rehearse database upgrades because they do not roll back, and write down any version you are deliberately staying on.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"software-has-an-expiry-date-and-it-is-published","runs":[{"text":"Software has an expiry date, and it is published"}]},{"t":"p","runs":[{"text":"Every language runtime, database and framework you depend on has a documented end-of-life date. After it, security fixes stop. The dates are public years in advance, and they arrive anyway, because nobody’s calendar has them in it."}]},{"t":"p","runs":[{"text":"As a shape rather than a specific list: Node.js major versions get roughly three years, with a long-term-support line; Python versions get five; Ubuntu’s long-term-support releases get five years of standard support; PostgreSQL major versions get five; Django and Rails maintain a small number of recent versions with a long-term line. The exact months change: look them up, and record the ones you depend on."}]},{"t":"p","runs":[{"text":"The fact that matters: an unsupported runtime does not stop working. It stops receiving patches. Your site runs exactly as well as before, which is precisely why it stays there."}]},{"t":"h2","id":"the-debt-compounds-quietly","runs":[{"text":"The debt compounds, quietly"}]},{"t":"p","runs":[{"text":"A framework upgrade skipped once is a week of work. Skipped four times, it is a rewrite, because the upgrade guides assume you are coming from the previous version, the migration tooling has been removed, the packages you depend on have moved on, and nobody remembers why the custom patch in the middle exists."}]},{"t":"p","runs":[{"text":"So the practical rule is counter-intuitive to how it feels: "},{"text":"upgrade more often, in smaller steps","b":true},{"text":", and never skip a major version if the project’s guide says not to."}]},{"t":"h2","id":"a-cadence-that-survives-contact-with-real-work","runs":[{"text":"A cadence that survives contact with real work"}]},{"t":"ul","items":[[{"text":"Weekly, automatic:","b":true},{"text":" patch and minor version updates for dependencies, applied by a bot, merged when CI is green. Dependabot and Renovate are free and do this. Group them so it is one pull request, not thirty."}],[{"text":"Monthly, 30 minutes:","b":true},{"text":" read the failures. A minor update that breaks a test is a signal, not an inconvenience."}],[{"text":"Quarterly, half a day:","b":true},{"text":" one major upgrade (the runtime, the framework, or the database) chosen from the list, done properly with a staging run and a rollback plan."}],[{"text":"Annually, one hour:","b":true},{"text":" update the end-of-life dates for everything you depend on and put the next two into the calendar with a name against them."}]]},{"t":"p","runs":[{"text":"That annual hour is the whole trick. The upgrade is not hard; being surprised by it is."}]},{"t":"h2","id":"database-major-versions-are-the-one-to-plan","runs":[{"text":"Database major versions are the one to plan"}]},{"t":"p","runs":[{"text":"Application upgrades roll back. A database major version upgrade often does not, because the data directory format changes. Three honest options:"}]},{"t":"ul","items":[[{"text":"A managed provider’s in-place upgrade.","b":true},{"text":" Simplest, and it needs a maintenance window measured in minutes, plus a tested backup taken immediately before."}],[{"text":"Dump and restore.","b":true},{"text":" Reliable, slow, and the downtime scales with your data size. Fine at a few gigabytes, painful at a hundred."}],[{"text":"Logical replication to a new instance, then a cutover.","b":true},{"text":" Near-zero downtime, more moving parts, and the approach worth learning once you are past the size where a dump is quick."}]]},{"t":"p","runs":[{"text":"Whichever you choose, rehearse on a copy. Time it. Write the number in the runbook, because that number is your maintenance window."}]},{"t":"h2","id":"deprecations-from-providers-arrive-on-their-schedule","runs":[{"text":"Deprecations from providers arrive on their schedule"}]},{"t":"p","runs":[{"text":"API versions get retired. Model snapshots get withdrawn. TLS versions and cipher suites get disabled by browsers. Payment providers change required flows to meet new regulation. Cloud instance families get deprecated."}]},{"t":"p","runs":[{"text":"Most of these come with months of notice, sent by email, to an address somebody set up in year one. Two habits: make sure provider notices reach more than one person and get read, and put every announced retirement date into the same calendar as your expiry list. This is the same discipline as the expiry lesson, applied to other people’s deadlines rather than your own."}]},{"t":"h2","id":"upgrading-is-also-how-you-keep-a-site-fast-and-cheap","runs":[{"text":"Upgrading is also how you keep a site fast and cheap"}]},{"t":"p","runs":[{"text":"It is easy to frame upgrades purely as risk avoidance, which makes them feel like a chore with no upside. In practice, runtime and database major versions routinely bring real performance and memory improvements, deprecate a workaround you were carrying, or add something that lets you delete code. A quarterly upgrade slot is one of the few maintenance activities that regularly makes the system smaller."}]},{"t":"h2","id":"when-you-deliberately-do-not-upgrade","runs":[{"text":"When you deliberately do not upgrade"}]},{"t":"p","runs":[{"text":"Sometimes staying is correct: a library that is finished and unmaintained but does its job, a version whose successor removed something you need, a system nearing retirement anyway. That is a legitimate decision when it is a decision."}]},{"t":"p","runs":[{"text":"Write it down where the handover documents live: what you are staying on, why, what the risk is, and when you will look again. The difference between technical debt and technical negligence is whether somebody chose it on purpose and left a note."}]}],"check":{"question":[{"text":"A team skips three consecutive major versions of their web framework, then attempts the upgrade in one jump. Why is this usually harder than three separate upgrades?"}],"options":[{"text":[{"text":"The final version contains more code, so more of the application must change"}],"correct":false,"tempting":[{"text":"Size is not the issue; the difficulty comes from how changes are documented and tooled, not from how much code exists."}]},{"text":[{"text":"Upgrade guides and migration tooling target adjacent versions, and deprecation shims from intermediate releases have already been removed"}],"correct":true,"tempting":null},{"text":[{"text":"Skipping versions invalidates the lockfile, forcing every dependency to resolve afresh"}],"correct":false,"tempting":[{"text":"Dependencies do move, but a lockfile can be regenerated at any point; that is a mechanical step rather than the source of the difficulty."}]},{"text":[{"text":"Security patches from the skipped versions must be applied individually first"}],"correct":false,"tempting":[{"text":"Patches are cumulative within a line and are superseded by the newer major; there is no backlog to replay."}]}],"order":[3,0,1,2],"why":null},"prev":{"slug":"managed-or-self-hosted","title":"What to run yourself, and what to pay for","minutes":9},"next":{"slug":"the-cost-per-user","title":"What one user costs you","minutes":9}}}