Infrastructure grows through local decisions. A job needs a queue, a status page needs a data store, an internal tool needs its own deployment path. Each choice may be sound in isolation. The maintenance cost appears later, in the connections between them.
Count responsibilities, not products
A moving part is anything that has to be configured, observed, updated, recovered, or explained. Two services from one vendor are still two operational responsibilities. A single executable with three independent state stores may contain more moving parts than its process list suggests.
Write down those responsibilities before choosing what to remove. This makes hidden costs visible: duplicate credentials, overlapping health checks, separate backup paths, and one-off release steps.
Build an operational inventory
Count the things that require a distinct answer. A component that adds no new process may still add a new credential, schema, hostname, upgrade sequence, or recovery path.
- Runtime
- What must stay running, where it runs, and how it restarts
- Configuration
- Every authoritative source and the tool that applies changes
- Identity
- Credentials, certificates, keys, and their renewal or revocation path
- State
- Databases, queues, files, caches, and what must be backed up
- Network
- Names, addresses, ports, routes, and firewall assumptions
- Observation
- Health checks, logs, alerts, dashboards, and who responds
- Change
- Upgrade order, compatibility window, and rollback boundary
- Recovery
- Restore sequence, dependency order, and proof that service returned
If two components have nearly identical rows, they may be carrying duplicated intent. If one component creates several unique rows, removing it may save more work than its process count suggests.
Look for duplicated intent
The safest simplifications often remove two mechanisms that are trying to guarantee the same outcome. Perhaps a retry loop exists both in a client and a scheduled wrapper. Perhaps configuration is generated, copied, and then patched in place. Consolidating the intent is usually less risky than replacing the entire system.
Simplification should reduce the number of things that must be true at once.
Delete in small, observable steps
Remove one dependency or path, then watch the behavior that justified it. Keep a clear reversal path until the new shape has been exercised under normal work. This turns deletion into a measured change instead of an act of faith.
A simpler system is not one with the fewest possible components. It is one where each remaining component has a clear responsibility, a visible failure mode, and a reason that still holds.
Retire the surrounding obligations
Stopping a process is only the middle of a removal. The surrounding obligations should disappear in an order that keeps rollback possible without leaving permanent debris.
- Map consumers. Find callers, scheduled jobs, dashboards, documentation, and people who still rely on the old path.
- Stop new writes. Move traffic or producers first, then observe long enough to catch infrequent work.
- Preserve required state. Take a final backup or export and write down its retention deadline and restore method.
- Disable before deleting. Keep a short reversal window when the dependency graph is not fully certain.
- Revoke identity. Remove credentials, certificates, firewall rules, DNS records, and permissions that existed only for the retired component.
- Remove observations. Delete checks and alerts after the component is gone so future failures remain meaningful.
- Update the current truth. Remove stale diagrams, runbooks, examples, and comments rather than marking each one “deprecated.”