# Deprecation policy

Source: https://swapcard.dev/deprecation-policy


Learn how Swapcard tells you about API changes, which changes need a deprecation, and how long you have to migrate before a field, query or mutation is removed.

## Scope
This policy applies to:

- The **Content API** (Developer schema). See [About the Content API](/content-api/about-the-graphql-api).
- The **Analytics API**. See [About the Analytics API](/analytics-api/about-the-graphql-api).

It covers any change to the **public contract** of these APIs: field removals, type changes, behavioral changes, query and mutation removals, and enum value changes.

## Principles
1. **Backward compatible by default.** All schema changes are backward compatible. New fields, new optional arguments and new types are non-breaking and can ship at any time.
2. **No silent removals.** A field or query is never removed without going through the full [deprecation lifecycle](#5-deprecation-lifecycle).
3. **A migration path for every deprecation.** Every deprecation includes a concrete alternative or migration instruction.
4. **Early and repeated communication.** You hear about deprecations through several channels and have time to migrate.

## Types of change
### Non-breaking changes
No deprecation is required for:

- Adding a new field to an existing type
- Adding a new query or mutation
- Adding a new optional argument to an existing query or mutation
- Adding a new enum value
- Adding a new type, union or interface

These changes are documented in the changelog, but don't come with a deprecation notice or a migration period.

### Deprecations
These follow the standard [deprecation lifecycle](#5-deprecation-lifecycle):

- Marking a field, query or mutation for future removal
- Replacing a field with a new alternative (for example, `attendees` → `withEvent.attendees`)
- Phasing out an enum value

### Breaking changes
Breaking changes are only allowed after the full deprecation lifecycle is complete:

- Removing a field, query or mutation
- Changing a field's return type
- Making a nullable field non-nullable (or the reverse, for inputs)
- Changing the meaning of an enum value
- Changing the behavior of a resolver in a way that breaks existing consumers

## How changes are announced
Every API change is announced through these channels:

| Channel | When | What |
| --- | --- | --- |
| **Changelog** | Every release | All changes (additions, deprecations, removals) are listed in the [Content API changelog](/content-api/changelog) or the [Analytics API changelog](/analytics-api/changelog). |
| **Email** | At deprecation, then at each reminder | Sent to all mailing list subscribers at each phase of the deprecation lifecycle. |
| **Schema directive** | At deprecation | The `@deprecated(reason: "...")` directive is added to the field in the GraphQL schema, so it shows up in introspection and developer tooling. |

## Deprecation lifecycle
The standard deprecation lifecycle lasts **12 months** from announcement to removal.

| Phase | Timeline | Technical action | Communication |
| --- | --- | --- | --- |
| **Announcement** | Day 0 | `@deprecated` directive added to the field with migration instructions. Documented in the changelog. | Email to all mailing list subscribers with the deprecation details and migration guide. |
| **First reminder** | Month 3 | None. | Reminder email highlighting the upcoming removal and the migration instructions. |
| **Second reminder** | Month 6 | None. | Reminder email. If usage analytics are available, the email may include usage data to help you assess urgency. |
| **End of life** | Month 12 | Field, query or mutation removed from the schema. Changelog updated. | Final email confirming the removal, with a link to the migration guide. |

:::info[Timeline extensions]
If usage analytics show significant ongoing usage of a deprecated field at Month 6, Swapcard may extend the lifecycle beyond 12 months with additional reminders. Extensions are announced in the changelog and by email.
:::

## Emergency deprecation
In exceptional circumstances, a shorter, fast-track deprecation period may apply:

| Reason | Minimum notice | Process |
| --- | --- | --- |
| **Security vulnerability** | 30 days | Direct outreach to known consumers, email to the mailing list and a changelog entry. Removal after 30 days. |
| **Legal or compliance requirement** | 30 days | Same as for a security vulnerability. If the legal requirement mandates immediate removal, the field is removed immediately and you are notified afterwards. |
| **Critical bug in a deprecated field** | 30 days | If a bug is found in an already-deprecated field and fixing it would take significant effort, removal may be brought forward to 30 days, with direct outreach. |

:::caution
Every emergency deprecation is documented in the changelog with a clear justification.
:::

## What every deprecation includes
1. **Deprecation reason.** A clear `@deprecated(reason: "...")` message in the schema that explains *why* the field is being removed.
2. **Alternative.** The replacement field, query or approach. For example: `"Use withEvent.attendees instead"`.
3. **Migration guide.** A changelog entry, or a linked document, with before-and-after code examples.
4. **Timeline.** The planned removal date: 12 months after the announcement, unless fast-tracked.

A deprecation message looks like this:

```graphql
@deprecated(reason: "Use withEvent.attendees instead. Will be removed on 2027-03-01.")
```

## Summary
| Type of change | Deprecation required? | Notice period |
| --- | --- | --- |
| New field, query or mutation | No | None (ships at any time) |
| Field removal or type change | Yes | 12 months |
| Security or legal removal | Yes (fast-track) | 30 days minimum |
| Enum value removal | Yes | 12 months |
| Behavioral change | Yes | 12 months |
