# Upgrade Triggers: Mandatory, Locking, Opportunity

> A Dependabot PR, an EOL notice and a CVE bulletin share one queue but not one urgency. Sort signals into three types and give each a response time.

- Type: Notes
- Published: 2026-10-25
- Tags: dependencies, decisions, security, production
- Series: Upgrading in Production
- Reading time: 8 min read
- Source: https://muhammetsafak.com/engineering/notes/upgrade-triggers/
- Language: en-US
- Author: Muhammet Şafak

---
> **TL;DR:** A Dependabot PR, an EOL notice, a deprecation warning and a CVE bulletin land in the same queue but not with the same urgency. EOL and a CVE that affects you are mandatory, a dependency lock and a deprecation are locking, and features and performance are opportunity. Writing a response time for each type keeps you from rushing a reminder and from being late to a lock.

Most of the PRs Dependabot opens are not obligations, they are reminders.

Four kinds of signal get mixed together in the queue: Dependabot PRs, deprecation warnings, EOL announcements and CVE bulletins. Each comes from a different source for a different reason, but all of them land on the same list in the same tone. The screen looks the same; the work is not the same size.

What is mandatory comes from somewhere else: an EOL date, a CVE, or a dependency that locks you to an old version. A team that reacts to all three the same way is either late or running for nothing.

## Two Different Clocks Drop Into the Queue

How urgent a PR is doesn't say in its title; it says in which clock opened it: one is tied to an advisory, the other to the schedule in `dependabot.yml`.

According to GitHub's documentation, when security updates are enabled, a new PR is opened when a dependency with a vulnerability is found. Version updates, on the other hand, are opened when a new version is released and run at the frequency you define in `dependabot.yml`. The first is triggered by something in the outside world, the second by a schedule you wrote.

One more detail: the `cooldown` setting delays PR creation for version updates only; it doesn't apply to security updates. So the setting you put in place to cut PR noise doesn't silence a security PR.

The practical consequence: a version update PR that arrives on a weekly schedule sits in the same queue as a PR born from an advisory, but it is not the same clock's job. You can deal with one this week and the other this quarter.

Before you open a PR, look at two things: who opened it, and which label it came with. If it's a security update, the question is no longer the version difference but whether you are affected. If it's a version update, the question is whether that version fixes anything. The answer to the second will most often be "no, a new version came out, that's all."

## EOL Is a Date, a CVE Is an Event

EOL is known in advance. [php.net](https://www.php.net/supported-versions.php) publishes the end date of security support for each branch, and the policy is simple: two years of active support from release, then two years of security fixes only. PHP 8.2's security support ends on **31 December 2026**. That is about 9.5 weeks after this post's publication date.

The framework layer keeps its own clock too. Laravel's [12.x release notes](https://laravel.com/docs/12.x/releases) say: bug fixes are provided for 18 months, security fixes for 2 years. For Laravel 12, that means bug fixes ended on 13 August 2026 and security fixes continue until 24 February 2027. So a team running Laravel 12 on the day this post is published has already lost bug fix support, but has not yet lost security support.

A CVE doesn't keep to a calendar. It can show up on a Tuesday morning, and it doesn't care which version you run.

The aftermath of EOL shouldn't be exaggerated either. Upstream support ending doesn't mean no fix will ever arrive; it means whether one arrives is no longer in your control. Ubuntu's [USN-6305-2](https://ubuntu.com/security/notices/USN-6305-2) bulletin is an example: upstream support for PHP 7.4 had ended in November 2022; on 27 February 2024, Ubuntu released fixes to its PHP packages for CVE-2023-3823 and CVE-2023-3824. `php7.4` on 20.04 got it from the standard repository; `php7.2` on 18.04 and `php7.0` on 16.04 required ESM and Ubuntu Pro.

That can be a way out. But the price is staying tied to a vendor, and leaving the question of which package the fix comes in and how fast to their schedule.

The value of an EOL date is that it is predictable. You can count the date backwards: set the line by which you want to be in production before the end date, and count back from there to staging, and from there to the test work. If I applied my recommendation below to 8.2, the line would be 30 September 2026. If you are still running 8.2 as you read this, that line has passed; this is not an accusation, it's the reason to write the plan today.

## The Locking Trigger Is Invisible

A locking trigger carries neither a date nor a bulletin. When a package stops supporting the old runtime, its new version becomes impossible to install, and nobody tells you.

In the PHP 7.1 case, the trigger wasn't EOL, it was a dependency lock; the details are in [The Cost of Deferring an Upgrade](/engineering/essays/the-cost-of-deferring-an-upgrade/).

Since a lock has no date like an EOL, you have to measure it: of the pending package updates, how many can't be installed because of your runtime version? Every number above zero shows that a door has closed.

The lock grows quietly. Because everything keeps working on the old runtime, it raises no alarm; the first visible signal is the day the package you want to update can't be installed. On that day the type is no longer opportunity but locking, and the way out is likely to be a single piece.

## Three Types, Three Response Times

I sort signals into three types:

- **Mandatory:** a signal that comes from outside and whose date or risk is independent of your preference (EOL, a CVE that affects you).
- **Locking:** a signal that breaks nothing today but narrows your options tomorrow.
- **Opportunity:** a signal where nothing breaks if you don't act, and you gain something if you do.

The response times below are my recommendation. They are not an industry standard; shift them to your own team's capacity, but write a time down. A signal with no time sits in the queue, and a signal that sits in the queue eventually comes back as "urgent."

| Signal | Type | Response time (my recommendation) |
|---|---|---|
| A CVE that affects you | Mandatory | Triage the same business day, patch within days |
| A CVE that doesn't affect you | Opportunity | Normal cycle; the record that you're not affected is kept |
| EOL date | Mandatory | The date goes on the calendar; in production at least 3 months before the end date |
| Dependency lock | Locking | Goes into next quarter's plan |
| Deprecation warning | Locking | Before the next major |
| Feature or performance | Opportunity | Backlog; no date unless there's a measured gain |

Two rows of the table deserve a separate pause. First, the CVE: the same bulletin is mandatory if it touches a code path you use, and an opportunity if it doesn't. The urgency is set not by the bulletin's headline but by your system. Two questions are enough to settle it: am I using the affected component, and can the affected code path be reached from outside? If you answer "yes" to both, you are on the first row of the table; if the fix hasn't reached your version branch yet, this doesn't extend the time, it turns the job from patching into a temporary mitigation. If one of the answers is "no," put that on record; the record keeps you from debating the same bulletin again a month later. Second, performance: "the new version is faster" is an opportunity, not an obligation. Don't open a calendar slot on that sentence without measuring.

Why is the time a quarter rather than days on the locking rows? Because a lock breaks nothing today; but if one more package drops the old runtime every quarter, the cost of the way out grows. A quarter is short enough to notice that and write it into the next planning.


> **Tan:** Write the type and the response time where the signal lives, not in a separate doc: a label on the issue or one line in the PR description is enough. Whoever triages the queue then reads the decision from the list itself instead of opening a wiki. The "not affected" note lives there too, so when the same advisory comes back a month later, the answer is already attached.


## The Limit of This Framework

This classification doesn't replace a decision matrix. The type tells you the upper bound of urgency; it doesn't tell you in which system, with what risk, in what order the work will be done.

The same CVE can be an opportunity in an internal tool and mandatory in a payment flow. What makes that distinction is how critical the system is. The decision matrix is in part 3 of the series.

The type is also not fixed. An opportunity, as it is deferred, turns into a locking one; a lock, as the EOL date approaches, turns into a mandatory one. Don't classify once and bury it in the queue; look again in every planning round.

## Back to the Queue

Back to the start: that Dependabot PR. Which clock opened it? An advisory, or a schedule? Is it releasing a lock, or only telling you a new version came out? Three questions send most PRs to the opportunity or reminder class, and what remains is a few jobs that really need looking at this week.

The aim of classification is not to do fewer upgrades; it's to do the right one at the right time. A team that meets a reminder as if it were an obligation runs for nothing; a team that takes a lock for a reminder is late. The price of both comes out of the same queue.

Here I classified the signals. In part 2 of the series I'll cover which systems to track in which layers, in part 4 how to choose the target version, and in part 8 when not upgrading is the right decision.

Take the last 10 upgrade signals in your queue, sort them into mandatory, locking and opportunity, and write a response time for each.

## Sources

- [Supported Versions](https://www.php.net/supported-versions.php) — PHP
- [Laravel 12.x Release Notes](https://laravel.com/docs/12.x/releases) — Laravel
- [Optimizing PR creation for version updates](https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/optimizing-pr-creation-version-updates) — GitHub
- [USN-6305-2: PHP vulnerabilities](https://ubuntu.com/security/notices/USN-6305-2) — Canonical
