Integration debt accumulates quietly. It starts with a reasonable decision: write a simple HTTP client for the first connector, ship it, move on. It becomes a problem three years later when you have eight connectors, six engineers who've touched the codebase, and every integration has slightly different retry logic, slightly different error handling, and slightly different response type definitions.
Auditing and refactoring integration code is different from refactoring application code. The integration layer has external dependencies that you don't control: the upstream API can change response shapes, rotate credentials, or alter rate-limit behavior. A refactor that looks safe in your codebase can break in production when the API behaves differently than your tests assume.
What Integration Debt Looks Like
Integration debt manifests in several recognizable patterns. Run a search in your codebase for these signals:
setTimeoutorsleepcalls in API client code — manual delay before retry, not driven byRetry-After- Hardcoded strings like
'next_cursor','cursor','page','offset'scattered across different files — each connector has its own pagination implementation anytyped API response objects — TypeScript is present but types are bypassed- Multiple files named
*-client.tsor*-api.tswith similar structure but different implementations - Token or credential management logic duplicated across connectors
- No-op catch blocks that swallow errors from API calls
Auditing Your Codebase
Before refactoring, inventory what you have. For each connector your system integrates with:
- Which auth method does it use? Is the token refresh logic correct — does it check expiry proactively or only refresh after a 401?
- What pagination model does it use? Is the implementation correct for that model — does it handle the empty final page case? Does it handle cursor expiry?
- Is there retry logic? Does it distinguish between 4xx and 5xx? Does it honor
Retry-Afterheaders? - Are API responses typed? If yes, are the types kept in sync with the actual API schema?
- Are write operations idempotent? Is an idempotency key sent on creates and updates?
- Are there tests? Do the tests mock the HTTP layer, or do they test actual integration behavior?
Document the answers. You're building a map of what's correct, what's partially correct, and what's wrong. The connectors that have auth bugs or incorrect pagination are your highest priority — they're producing data errors that may not be visible yet.
Common Anti-Patterns
The 3-second sleep retry: catch (e) { await sleep(3000); return apiCall(); }. No jitter, no exponential backoff, retries on non-retryable 4xx errors. Replace with a proper retry strategy that checks status codes and backs off correctly.
The offset assumption on cursor APIs: Code that increments a page number or offset against an API that actually uses cursor pagination. Works on small datasets; skips records when the dataset size exceeds the page size assumption.
The one-time credential: API key stored in the application config that was generated by a developer two years ago who has since left. No rotation mechanism, no monitoring for expiry. Replace with credential management that stores secrets in a vault, has a documented rotation procedure, and alerts on approaching expiry.
The swallowed 429: Catch block that catches a 429, logs a warning, and continues — treating a quota error as a recoverable error without actually waiting for the quota to reset. This causes silent data gaps in syncs that run into rate limits.
Refactoring Strategy
Don't attempt a big-bang rewrite. Refactor one connector at a time, starting with the highest-risk connector (the one with incorrect auth or pagination, or the one that's business-critical). The goal for each connector:
- Centralize auth logic in a single auth provider class with correct proactive refresh
- Replace the pagination implementation with a correct strategy for the connector's pagination model
- Add retry logic with proper status code discrimination and exponential backoff with jitter
- Add TypeScript types for the response shapes, even if you can't validate them against the live API immediately
- Write at least a unit test for the retry and pagination behavior
After refactoring the first two connectors, you'll notice the infrastructure code is nearly identical. Extract it into a shared connector base class or utility module. The third and subsequent connectors should require only the connector-specific details — endpoint URLs, auth method, pagination type, rate limits.
Preventing Future Debt
The most effective prevention is making the correct approach the easiest approach. If your team has a well-documented internal SDK or uses an external SDK like Devloom, new connectors start from the correct foundation. The developer who adds the next connector doesn't need to know how cursor pagination works — they call .autopage() and it works correctly.
Code review gates that check for the anti-patterns listed above — sleep-based retry, pagination implementations that don't handle cursor expiry, untyped response objects — catch new debt before it's committed. The review checklist is the same as the audit checklist: auth, pagination, retry, types, idempotency.