skip to content

On Packagist, how do you retire a PHP package by marking it abandoned with a replacement, and what do consumers' Composer runs then show?

level: seniorimportance: should knowfreq 25%

answer

  1. abandon, never delete
  2. abandoned: true or a package name
  3. is abandoned, you should avoid using it
  4. versions keep installing
  5. composer audit lists it

basics

~10 s

Mark the package abandoned, optionally naming a replacement package, rather than deleting it. Existing versions keep installing, but Composer warns that the package is abandoned and suggests the replacement, and composer audit reports it.

solid answer

~40 s

Retiring a package means **abandoning** it, not deleting it: consumers' lock files point at its versions, and deleting them breaks their installs. The abandoned state is package metadata. Composer's schema defines it as the `abandoned` key, either `true` or the name of a recommended replacement such as `"abandoned": "mira/relative-time"`, and Packagist lets a maintainer set it on the package. Nothing is removed. Consumers keep installing their versions, but Composer prints `Package mira/date-phrases is abandoned, you should avoid using it. Use mira/relative-time instead.` after an install or update, `composer show` adds an 'Attention' line, and `composer audit` lists it as abandoned. Migration stays the consumer's job; the flag only makes it visible.

code

bash · 5 lines
bash
composer update
# Package mira/date-phrases is abandoned, you should avoid using it. Use mira/relative-time instead.

composer show mira/date-phrases
# Attention: This package is abandoned and no longer maintained. The author suggests using the mira/relative-time package instead.

go deeper

for a junior

Know that a package can be marked abandoned, that Composer then warns about it, and that it still installs.

for a middle

Explain the abandoned key's forms (true or a replacement name), the warnings in install and show, and why deleting a package breaks consumers.

for a senior

Retire packages without breaking lock files, give a migration path, and treat abandoned dependencies as scheduled work alongside advisory findings.

for a principal

Set a deprecation policy for published packages: notice periods, final releases, replacement naming, and how consumers across teams are tracked to completion.

## Why abandon instead of delete A published package lives on in other people's `composer.lock` files, each pinned to an exact version and commit. Deleting the package or its tags breaks every one of those installs at the next deploy. **Abandoning** keeps every version installable and attaches a clear signal: this package is no longer maintained, and here is what to use instead. ## How the flag is set The abandoned state is part of the package's metadata: - Composer's schema defines an **`abandoned`** property for `composer.json`. It defaults to `false` and accepts `true`, or a **package name or URL** pointing to the recommended alternative: ```json { "name": "mira/date-phrases", "abandoned": "mira/relative-time" } ``` - On Packagist a maintainer marks the package abandoned, optionally naming the replacement, and Packagist publishes that state in the metadata it serves to Composer. Either way, consumers see the same field: abandoned, with or without a suggested replacement. ## What consumers see | Where | Message or effect | |---|---| | `composer install` / `update` | `Package mira/date-phrases is abandoned, you should avoid using it. Use mira/relative-time instead.` (or `No replacement was suggested.`) | | `composer show mira/date-phrases` | `Attention: This package is abandoned and no longer maintained. The author suggests using the mira/relative-time package instead.` | | `composer show` / `outdated` in JSON format | an `abandoned` field on the package | | `composer audit` | lists it as abandoned; whether that fails the audit or blocks updates is set by the consuming project's Composer policy config | The package keeps resolving and installing. Abandonment is a warning and an audit finding, not a removal. ## Doing it well as a maintainer 1. **Name a replacement** when one exists. It is the difference between a dead end and a migration path. 2. **Publish a final release** that fixes anything urgent, so users are not left on a broken version. 3. **Explain the migration** in the README: what changed between the old and new package, and whether namespaces or APIs differ. 4. **Do not reuse the name.** A new major version under the same name suits a continuation; a different design belongs under a new name, with the old one abandoned towards it. ## How consumers find abandoned dependencies A warning scrolling past during `composer update` is easy to miss, so teams look for abandoned packages deliberately: - `composer show vendor/package` prints the `Attention` line for that package, including any suggested replacement. - `composer show` and `composer outdated` with `--format=json` include an `abandoned` field, which a script can collect across many repositories. - `composer audit` lists abandoned packages alongside advisories, so a scheduled audit job surfaces them without anyone reading install logs. ## Advisory data: the other flag Packagist publishes Packagist also serves **security advisories** for packages, which Composer uses for blocking during updates and for `composer audit`. Each advisory record carries: - an **advisory ID** (Packagist's own IDs start with `PKSA-`) and optionally a **CVE**; - the **package name** and an **affected versions** constraint; - a **title**, a **link**, the **reported-at** date and a **severity**; - the **sources** it was aggregated from, each with its own remote ID. The two flags answer different questions. An advisory says *these versions are known to be vulnerable*; abandoned says *nobody will fix the next one*. A maintainer can help both. The `support.security` key in `composer.json` publishes the URL of the project's **vulnerability disclosure policy**, so researchers know where to report before an advisory exists. ## For consumers An abandoned dependency is technical debt with a date on it: the next vulnerability in it will not be patched. Treat the warning as a ticket, not noise. Plan the move to the replacement, and if the team decides to keep the package for now, record that decision where the audit configuration lives rather than muting the warning everywhere.

  • Does marking a package abandoned stop existing projects from installing it?
    No. Every published version stays installable, and `composer install` from a lock file keeps working. Composer prints a warning and `composer audit` reports the package. Whether a consuming project treats that as a failure, or blocks the package during updates, is that project's own Composer configuration.
  • What is the difference between an abandoned package and one with a security advisory?
    An advisory marks specific versions as vulnerable, with an affected-versions constraint, and by default Composer refuses to pick those versions during `update`. Abandoned marks the whole package as unmaintained: no version is refused, but no future fix should be expected, so the risk is about what happens next rather than a known flaw.

saying these in an interview costs you the question

  • Deleting the package is the clean way to retire it.
  • Abandoned packages can no longer be installed from a lock file.
  • The abandoned key only accepts true or false.
  • Composer switches consumers to the replacement automatically.
  • An abandoned package is the same as one with a security advisory.