Integration & APIs/
Lesson
AI pitfall
AI-generated deprecation plans almost always skip the monitoring step. The AI will tell you to add Deprecation and Sunset headers, write a migration guide, and set a removal date. But without tracking which consumers still use the deprecated endpoint, you are flying blind, you will either remove it too early (breaking someone) or too late (carrying dead code for years).

DeprecationWhat is deprecation?Marking a feature or API version as outdated and scheduled for removal, giving users time to switch to the replacement. is a promiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits.: "this still works, but it will stop working on a specific date, and here is what to use instead." Done well, it gives consumers time to migrate gracefully. Done badly, it breaks production systems or leaves zombie endpoints running forever because nobody tracked who was still using them.

The deprecationWhat is deprecation?Marking a feature or API version as outdated and scheduled for removal, giving users time to switch to the replacement. lifecycle

Good to know
HTTP 410 Gone is the correct status code after sunset, not 404. A 404 means "I have never heard of this." A 410 means "This used to exist and was intentionally removed." The distinction matters for consumers and for search engines.

Every deprecation follows four phases. Skipping any of them creates problems.

Phase 1: announce (months before sunset)

Communicate the deprecation before consumers feel any effect. Use every channelWhat is channel?A typed conduit in Go used to pass values between goroutines - can be unbuffered (synchronous) or buffered (async queue). available: changelog, email, dashboard notification, and response headers.

// Add deprecation headers to the response
app.get('/v1/users', (req, res) => {
  // RFC 8594: Deprecation header
  res.setHeader('Deprecation', 'true');

  // RFC 8594: Sunset header with exact date
  res.setHeader('Sunset', 'Sat, 01 Nov 2025 00:00:00 GMT');

  // Custom header pointing to migration docs
  res.setHeader('Link', '</docs/migration/v1-to-v2>; rel="deprecation"');

  // Still return normal response
  const users = await getUsers();
  res.json(users);
});

The Deprecation header is defined in RFC 8594. It tells automated tools and monitoring systems that this endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users. is marked for removal. The Sunset header gives the exact date. Together, they provide machine-readable deprecation metadata that consumers can build alerts around.

Phase 2: warn (weeks before sunset)

Increase the urgency. Add warning headers and start logging which consumers still use the deprecated endpoint.

// Middleware that tracks deprecated endpoint usage
function deprecationWarning(config) {
  return (req, res, next) => {
    const clientId = req.headers['x-client-id'] || 'anonymous';
    const daysUntilSunset = Math.ceil(
      (new Date(config.sunsetDate) - new Date()) / (1000 * 60 * 60 * 24)
    );

    // Set standard headers
    res.setHeader('Deprecation', 'true');
    res.setHeader('Sunset', new Date(config.sunsetDate).toUTCString());

    // Add a Warning header (RFC 7234)
    res.setHeader(
      'Warning',
      `299 - "Deprecated: use ${config.replacement} instead. ` +
      `Sunset in ${daysUntilSunset} days."`
    );

    // Log usage for tracking
    logger.warn('Deprecated endpoint accessed', {
      endpoint: req.path,
      method: req.method,
      clientId,
      daysUntilSunset,
      replacement: config.replacement
    });

    // Track in metrics
    metrics.increment('api.deprecated.request', {
      endpoint: req.path,
      client: clientId
    });

    next();
  };
}

// Apply to deprecated routes
app.get('/v1/users',
  deprecationWarning({
    sunsetDate: '2025-11-01',
    replacement: 'GET /v2/users'
  }),
  v1GetUsers
);

Phase 3: sunset (the removal date)

On the sunset date, stop serving the deprecated endpoint. Return a clear error that points consumers to the replacement.

function sunsetResponse(config) {
  return (req, res) => {
    res.status(410).json({
      error: 'Gone',
      message: `This endpoint was deprecated and removed on ${config.sunsetDate}.`,
      migration: config.replacement,
      documentation: config.docsUrl
    });
  };
}

// Replace the old handler with a sunset response
app.get('/v1/users',
  sunsetResponse({
    sunsetDate: '2025-11-01',
    replacement: 'GET /v2/users',
    docsUrl: 'https://api.example.com/docs/migration/v1-to-v2'
  })
);

HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. 410 Gone is the correct status codeWhat is status code?A three-digit number in an HTTP response that tells the client what happened: 200 means success, 404 means not found, 500 means the server broke. here, it tells clients (and search engines) that the resource existed but has been intentionally removed. It is different from 404, which means "never heard of it."

Phase 4: cleanup (after sunset)

Remove the old code, database columns, and infrastructure that supported the deprecated version. This is the step most teams skip, and then they carry dead codeWhat is dead code?Code that exists in the project but is never executed or referenced, adding confusion without serving a purpose. for years.

// Before cleanup: dead code still in the codebase
app.get('/v1/users', sunsetResponse({ /* ... */ }));
app.get('/v2/users', v2GetUsers);

// After cleanup: only the current version remains
app.get('/v2/users', v2GetUsers);
// v1 handler, v1 serializers, v1 tests - all deleted
Edge case
Mobile apps are the hardest consumers to migrate because users update on their own schedule. Even after you sunset an endpoint, there will be users running 6-month-old app versions. Consider keeping a thin compatibility layer for mobile clients or implementing a forced app update mechanism before sunsetting critical endpoints.
02

DeprecationWhat is deprecation?Marking a feature or API version as outdated and scheduled for removal, giving users time to switch to the replacement. best practices

PracticeDescriptionWhy it matters
Set a concrete sunset dateAlways include a date, never "soon" or "eventually"Consumers cannot plan without a deadline
Minimum 6 months for public APIsGive external teams time to schedule migration workEnterprise consumers have quarterly release cycles
1-3 months for internal APIsShorter timeline, since you control both sidesStill need time for testing and rollout
Provide migration docsStep-by-step guide mapping old to newWithout docs, deprecation is just abandonment
Monitor usage metricsTrack how many requests hit deprecated endpoints dailyKnow when it is safe to remove
Notify known consumers directlyEmail or message teams using the deprecated featureHeaders alone are not enough
Keep deprecated endpoints functional until sunsetNever break early, even if usage drops to zeroTrust matters, breaking your own timeline erodes confidence
Return 410 Gone after sunsetNot 404, 410 communicates intentional removalHelps consumers distinguish "removed" from "wrong URL"
Version your deprecation announcementsInclude them in your API changelogConsumers can search the changelog for relevant changes
03

Writing effective migrationWhat is migration?A versioned script that changes your database structure (add a column, create a table) so every developer and server stays in sync. guides

A migration guide must answer three questions: what changed, why, and exactly how to update.

markdown
# Migration Guide: /v1/users -> /v2/users

## What changed
- `email` field renamed to `contactEmail`
- `name` field split into `firstName` and `lastName`
- Response now includes `profile` object (new)
- `role` field values changed: "admin" -> "administrator"

## Why
The v1 user model combined data that belongs in separate domains.
v2 separates user identity from user profile, enabling independent updates.

## Step-by-step migration

### 1. Update the endpoint URL

// Before

GET /v1/users/123

// After
GET /v2/users/123

### 2. Update field references
javascript
// Before
const email = user.email;
const name = user.name;

// After
const email = user.contactEmail;
const name = ${user.firstName} ${user.lastName};

### 3. Handle new response shape
javascript
// Before
const { id, name, email, role } = await fetchUser(123);

// After
const { id, firstName, lastName, contactEmail, profile, role } = await fetchUser(123);
// Note: role is now "administrator" instead of "admin"

The migration guide should be specific enough that a developer can follow it mechanically without guessing.

04

Monitoring deprecated endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users. usage

Tracking usage is essential to know when you can safely remove a deprecated endpoint. Build a dashboard that shows:

// Express middleware to collect deprecation metrics
function collectDeprecationMetrics(req, res, next) {
  const isDeprecated = res.getHeader('Deprecation') === 'true';

  if (isDeprecated) {
    const clientId = req.headers['x-client-id'] || 'unknown';

    // Store in your metrics system (Datadog, Prometheus, etc.)
    metrics.gauge('api.deprecated.daily_requests', 1, {
      endpoint: `${req.method} ${req.route.path}`,
      client: clientId,
      version: req.baseUrl  // /v1, /v2, etc.
    });

    // Track unique consumers per day
    metrics.set('api.deprecated.unique_clients', clientId, {
      endpoint: `${req.method} ${req.route.path}`
    });
  }

  next();
}

app.use(collectDeprecationMetrics);

What to track on your dashboard:

MetricWhat it tells you
Daily request count to deprecated endpointsOverall usage trend (should be declining)
Unique consumers per dayHow many teams still need to migrate
Request count by consumer IDWhich specific consumers are lagging
Days until sunsetUrgency, are consumers migrating fast enough?
Error rate on new endpointWhether migration is causing issues on the new version
Consumer migration completion ratePercentage of consumers fully migrated

When the daily request count hits zero for a sustained period (at least a week after sunset), you can confidently clean up the deprecated code.

05

Timeline template

Here is a practical timeline for deprecating a public APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users.:

Day 0:     Announce deprecation in changelog + response headers
Day 0:     Publish migration guide
Week 1:    Email known consumers directly
Month 1:   Review usage metrics - are consumers migrating?
Month 3:   Send reminder notification to remaining consumers
Month 5:   Final warning - increase Warning header urgency
Month 6:   Sunset - return 410 Gone
Month 7:   Remove deprecated code from codebase

For internal APIs, compress this to weeks instead of months. The key is that every step is planned, communicated, and tracked.