Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/deprecations/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Deprecation List
description: Track deprecated endpoints, fields, and parameters in Glean's APIs
hide_title: true
---

import DeprecationsList from '@site/src/components/Deprecations/DeprecationsList'
import DeprecationsHeader from '@site/src/components/Deprecations/DeprecationsHeader'
import deprecationsData from '@site/src/data/deprecations.json'

<DeprecationsHeader />

This page lists all deprecations related to Glean's public APIs. Each deprecation includes the date it was introduced, the removal date after which the property will no longer be available, and guidance on what to use instead. See [API Evolution & Deprecations](/deprecations/overview) for details on how our deprecation process works.

<DeprecationsList endpoints={deprecationsData.endpoints} />
135 changes: 135 additions & 0 deletions docs/deprecations/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
title: API Evolution & Deprecations
description: How Glean evolves its REST API through additive changes and predictable deprecations
---

import CardGroup from '@site/src/components/CardGroup';
import Card from '@site/src/components/Card';
import { Steps, Step } from '@site/src/components/Steps';

# API Evolution & Deprecations

We evolve our API through additive changes and predictable deprecations — not versioned URLs. This ensures your integrations remain stable while allowing us to improve the API.

---

## What's a Breaking Change?

<CardGroup cols={2}>
<Card title="Breaking Changes" icon="AlertTriangle" color="#dc2626">
Require your code to be updated:

- Removing or renaming endpoints, fields, or parameters
- Changing data types or formats
- Making optional fields required
- Tightening validation rules
</Card>
<Card title="Non-Breaking Changes" icon="CheckCircle" color="#16a34a">
Work with your existing code:

- Adding new endpoints or fields
- Adding optional parameters
- Loosening validation
- Performance improvements
</Card>
</CardGroup>

---

## The Deprecation Timeline

When we need to make a breaking change, we follow a predictable process:

<Steps titleSize="h3">
<Step title="Announce Deprecation">
We mark the feature as deprecated in our API responses and in our documentation. Your code continues to work normally.
</Step>
<Step title="Migration Period (6+ months)">
Both old and new approaches work side-by-side. We provide migration guides and send email notifications.
</Step>
<Step title="Removal at Fixed Date">
The deprecated feature is removed only on a fixed date: **Jan 15**, **Apr 15**, **Jul 15**, or **Oct 15** (23:59:59 UTC). This gives you a predictable timeline to plan for a migration.
</Step>
</Steps>

#### Example:

Consider a field deprecated on **2026-01-01**. The field will be removed on **2026-07-15** (6 months + rounded to the next allowed removal date)

```
2026-01-01 2026-07-15
│ │
▼ ▼
────────────●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━●────────────
│ │
Deprecation Introduced Deprecation Removed

├──────────────────── Migration Period ────────────────────────┤
```

---

## How You'll Know About Changes

<CardGroup cols={2}>
<Card title="API Headers" icon="Code" color="#3b82f6">
Every response using deprecated features includes an `X-Glean-Deprecated` header with full details.
</Card>
<Card title="Documentation" icon="FileText" color="#06b6d4">
Inline warnings on API pages, migration guides, and a complete [deprecation schedule](/deprecations).
</Card>
</CardGroup>

When you use a deprecated feature, the API response includes an `X-Glean-Deprecated` header:

```http
X-Glean-Deprecated: kind=field;name=snippetText;introduced=2026-01-01;removal=2026-07-15;docs=https://developers.glean.com/deprecations
```

The header contains:

| Field | Description |
|-------|-------------|
| `kind` | Type of deprecation: `endpoint`, `field`, or `parameter` |
| `name` | Name of the deprecated item |
| `introduced` | Date the deprecation was announced (ISO-8601) |
| `removal` | Date the item will be removed (ISO-8601) |
| `docs` | Link to migration documentation |

:::tip Automated Monitoring
Parse this header in your integration tests or monitoring to catch deprecations early.
:::

---

## Testing Future Changes

Validate your integration before deprecated features are removed by including the `X-Glean-Exclude-Deprecated-After` header:

```http
X-Glean-Exclude-Deprecated-After: 2027-01-15
```

This header simulates how the API will behave after the deprecation date, helping you verify your code handles the changes correctly.

---

## After Removal

When deprecated items are removed, the API provides clear error responses:

| Item Type | Response |
|-----------|----------|
| **Endpoints** | `410 Gone` with migration instructions |
| **Request fields** | `400 Bad Request` with details |
| **Response fields** | Simply omitted from the response |

```json title="Example error response"
{
"error": {
"code": "deprecated_field_removed",
"message": "Field 'userId' was removed on 2027-01-15. Use 'userIdentifier' instead.",
"docs": "https://developers.glean.com/docs/migrations/2027-01-15"
}
}
```
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"changelog:aggregate": "pnpm run changelog:compile:json",
"generate:rss": "pnpm run changelog:compile:rss",
"generate:redirects": "tsx scripts/generate-redirects.ts",
"generate:deprecations": "node scripts/generate-deprecations.mjs",
"generate:deprecations:rss": "node scripts/generate-deprecations-rss.mjs",
"typecheck": "tsc",
"format": "prettier --write .",
"test": "vitest run",
Expand All @@ -37,9 +39,9 @@
"format:check": "prettier --check .",
"links:check": "scripts/check-links.sh https://developers.glean.com true",
"links:check:local": "pnpm build && (pnpm serve --port 8888 & SERVER_PID=$!; sleep 5; scripts/check-links.sh http://localhost:8888 true; RESULT=$?; kill $SERVER_PID 2>/dev/null || true; exit $RESULT)",
"openapi:regenerate:all": "pnpm run openapi:clean:before:all && pnpm run openapi:transform:all && pnpm run openapi:generate:all && pnpm run openapi:clean:after:all",
"openapi:regenerate:client": "pnpm run openapi:clean:before:client && pnpm run openapi:transform:client && pnpm run openapi:generate:client && pnpm run openapi:clean:after:client",
"openapi:regenerate:indexing": "pnpm run openapi:clean:before:indexing && pnpm run openapi:transform:indexing && pnpm run openapi:generate:indexing && pnpm run openapi:clean:after:indexing",
"openapi:regenerate:all": "pnpm run openapi:clean:before:all && pnpm run openapi:transform:all && pnpm run generate:deprecations && pnpm run generate:deprecations:rss && pnpm run openapi:generate:all && pnpm run openapi:clean:after:all",
"openapi:regenerate:client": "pnpm run openapi:clean:before:client && pnpm run openapi:transform:client && pnpm run generate:deprecations && pnpm run generate:deprecations:rss && pnpm run openapi:generate:client && pnpm run openapi:clean:after:client",
"openapi:regenerate:indexing": "pnpm run openapi:clean:before:indexing && pnpm run openapi:transform:indexing && pnpm run generate:deprecations && pnpm run generate:deprecations:rss && pnpm run openapi:generate:indexing && pnpm run openapi:clean:after:indexing",
"openapi:clean:before:all": "pnpm run openapi:clean:before:client && pnpm run openapi:clean:before:indexing",
"openapi:clean:before:client": "find docs/api/client-api -type f ! -name 'overview.mdx' -delete",
"openapi:clean:before:indexing": "find docs/api/indexing-api -type f ! -name '*-overview.mdx' -delete",
Expand Down Expand Up @@ -94,6 +96,7 @@
"docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-plugin-search-glean": "^0.7.0",
"docusaurus-theme-openapi-docs": "^4.4.0",
"feed": "^4.2.2",
"js-yaml": "^4.1.0",
"lucide-react": "^0.548.0",
"prism-react-renderer": "^2.3.0",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 169 additions & 0 deletions scripts/generate-deprecations-rss.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/usr/bin/env node

/**
* Generate deprecations RSS feed from deprecations.json
*
* This script reads the deprecations data and generates an RSS 2.0 feed
* for subscribers to track API deprecation announcements.
*
* Usage: node scripts/generate-deprecations-rss.mjs
*/

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { Feed } from 'feed';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT_DIR = path.resolve(__dirname, '..');

const DEPRECATIONS_DATA_FILE = path.join(
ROOT_DIR,
'src',
'data',
'deprecations.json',
);
const RSS_OUTPUT_DIR = path.join(ROOT_DIR, 'static');
const RSS_OUTPUT_FILE = path.join(RSS_OUTPUT_DIR, 'deprecations.xml');

const SITE_URL = 'https://developers.glean.com';
const DEPRECATIONS_URL = `${SITE_URL}/deprecations`;

/**
* Check if RSS regeneration is needed based on file modification times
*/
function needsRegeneration(dataFile, outputFile) {
if (!fs.existsSync(outputFile)) {
return true;
}

if (!fs.existsSync(dataFile)) {
return true;
}

const dataStats = fs.statSync(dataFile);
const outputStats = fs.statSync(outputFile);

return dataStats.mtime > outputStats.mtime;
}

/**
* Get location label from endpoint path
*/
function getLocationFromPath(endpointPath) {
if (endpointPath.startsWith('/indexing/')) {
return 'Indexing API';
}
return 'Client API';
}

/**
* Generate RSS feed from deprecations data
*/
function generateRss() {
if (!fs.existsSync(DEPRECATIONS_DATA_FILE)) {
console.error(
'Deprecations data file does not exist:',
DEPRECATIONS_DATA_FILE,
);
console.log(
'Run "pnpm generate:deprecations" first to generate the data file.',
);
process.exit(1);
}

if (!needsRegeneration(DEPRECATIONS_DATA_FILE, RSS_OUTPUT_FILE)) {
console.log(
'No changes detected in deprecations data, skipping RSS generation',
);
return;
}

const deprecationsData = JSON.parse(
fs.readFileSync(DEPRECATIONS_DATA_FILE, 'utf-8'),
);
const { endpoints, generatedAt } = deprecationsData;

const feed = new Feed({
title: 'Glean API Deprecations',
description:
'Deprecated endpoints, fields, and parameters in Glean\'s APIs',
id: SITE_URL,
link: DEPRECATIONS_URL,
language: 'en',
image: `${SITE_URL}/img/glean-developer-logo-light.svg`,
favicon: `${SITE_URL}/img/favicon.png`,
copyright: `Copyright © ${new Date().getFullYear()} Glean`,
generator: 'Glean Developer Site',
feedLinks: {
rss2: `${SITE_URL}/deprecations.xml`,
},
author: {
name: 'Glean',
link: 'https://glean.com',
},
updated: new Date(generatedAt),
});

// Flatten all deprecations from all endpoints and sort by introduced date
const allDeprecations = [];

for (const endpoint of endpoints) {
const location = getLocationFromPath(endpoint.path);

for (const deprecation of endpoint.deprecations) {
allDeprecations.push({
...deprecation,
endpoint: {
method: endpoint.method,
path: endpoint.path,
},
location,
});
}
}

// Sort by introduced date (most recent first)
allDeprecations.sort(
(a, b) => new Date(b.introduced) - new Date(a.introduced),
);

// Add each deprecation as an RSS item
for (const dep of allDeprecations) {
const title = `Deprecation: ${dep.name} ${dep.type} on ${dep.endpoint.method} ${dep.endpoint.path}`;
const pubDate = new Date(dep.introduced);

const descriptionHtml = `<p>${dep.message}</p>
<p>Removal date: ${dep.removal}</p>`;

feed.addItem({
title,
id: dep.id,
link: DEPRECATIONS_URL,
description: descriptionHtml,
author: [
{
name: 'Glean',
link: 'https://glean.com',
},
],
date: pubDate,
category: [{ name: dep.location }],
});
}

// Ensure output directory exists
fs.mkdirSync(RSS_OUTPUT_DIR, { recursive: true });

// Write RSS feed
const rssXml = feed.rss2();
fs.writeFileSync(RSS_OUTPUT_FILE, rssXml);

console.log(
`Generated deprecations RSS feed with ${allDeprecations.length} entries at ${RSS_OUTPUT_FILE}`,
);
}

// Run if called directly
generateRss();
Loading