A Zero-Diff Commit: Removing a Third-Party Integration
The commit removed an integration and changed zero lines of code. The real cleanup lives in credentials, caches, breakers, and billing.
TL;DR
A zero-diff commit declared a Plane integration removed, but the real cleanup happened entirely outside the codebase. Credentials, client caches, billing entitlements, and circuit breaker configs all outlive the code and need manual revocation. The lesson: treat integrations as detachable backing services and write a removal plan listing vendor touchpoints from day one.
A commit in a portal project called KotaPortal carried the message "remove plane-gate integration (Plane no longer used)". Its diff statistics showed an unusual result: 0 additions, 0 deletions, 0 files changed. Not a single line of code was touched, yet the integration was declared no longer in use. The cleanup that mattered had not happened inside that commit.
The wrong assumption
The most common reading of such a commit: removing a third-party integration means deleting its module or package from the codebase. Once the module is gone, the system is considered clean and the job is done.
In reality, credentials, caches, dashboards, and documentation outlive the code itself. Decommissioning an integration cleanly is a reverse installation with a different checklist. Code is only the tip of a wider ecosystem, and its operational footprint is spread across several infrastructure layers.
The mechanics behind it
The backing-services doctrine from 12-Factor explains why this happens [1]. Code should not distinguish between local and third-party services; both are attached resources accessed through a URL or credentials stored in configuration. The consequence: the resource is designed to be detached at any time. Only the detachable part is not just code; its locator and credentials remain in configuration, and the provider keeps its own record of the access on its side.
On the vendor side, Plane gives access revocation its own mechanics. The API authenticates with an X-API-Key header holding a personal access token, and an invalid or expired key immediately returns an authentication failure [2]. The Plane MCP tool surface also churns fast: version 0.2.x shipped 177 per-operation tools, version 0.3.0 collapsed them into 28 resource tools (169 kept as hidden aliases, the 7 unmappable ones returning replacement messages), and version 0.3.3 carries 30 tools covering 207 actions. The header guidance itself switched from x-api-key to an Authorization header with the Bearer scheme [3]. The same page documents the offboarding side: revoke access by disconnecting the connector in the client, deleting the PAT in Plane settings, or clearing the mcp-remote cache. A 401 means the token is wrong, revoked, or sent with an old header; 402 means the feature is not part of the current subscription plan.
Client-side caches are the easiest residue to miss because they never appear in any repository. The Plane MCP documentation describes one concrete case: OAuth credentials stored in the mcp-remote cache survive server-side changes, and clearing them means removing one cache directory shared by every mcp-remote server unless it was split off through a dedicated variable [3]. The same page recommends checking the workspace audit log for API token events. Neither location is touched by any commit in any repository.
Diagnostics during decommissioning have their own vocabulary. A 401 with a previously valid token usually means the token was revoked, while a 402 indicates the feature is not included in the current plan [3]. Both codes double as verification tools: once every credential is revoked, leftover calls from systems that were forgotten during cleanup stop with a 401, not with an error pointing at some unrelated bug.
At the runtime layer, leftover configuration can trigger defense mechanisms of their own. A circuit breaker works as a state machine with three main states: CLOSED, OPEN, and HALF_OPEN. When the failure rate touches a threshold, say 50 percent, the breaker moves to OPEN and rejects further calls with a CallNotPermittedException [4]. An integration that was deleted but is still called by leftover configuration keeps poisoning that failure window; a guard built to protect the system turns into a routine source of errors.
The billing layer keeps its own residue. An entitlement represents a customer's access to a feature, and a provider like Stripe sends notifications to provision or de-provision access as subscription status changes [5]. An unsynchronized status means access left open without oversight, or a feature still being billed under a plan that was actually abandoned.
The install and removal asymmetry
Installation and removal are not symmetric. The table below summarizes where the difference sits.
| Aspect | During installation | During removal |
|---|---|---|
| Credentials | Created, then stored in configuration | Manually revoked in the vendor dashboard |
| Configuration | Added to environment variables | Removed from the repository and the environment |
| Runtime guards | Configured to let traffic through | Must be disabled so they stop raising errors |
| Caches | Filled to speed up responses | Must be force-cleared |
| Documentation | Updated with new endpoints | Must be archived or deleted |
The pattern is clear: removal work piles up outside the repository. Credentials are revoked in a vendor dashboard, caches are cleared on clients, entitlements are verified on the billing side, and the zero-diff commit is only the marker that all of it is done. The decision this case leads to is cheap to write down and expensive to execute: a new integration counts as installed only when its removal plan exists from day one, complete with the list of vendor-side touchpoints to hit when it is time to let go.