Service Decommissioning & Cost Optimization — Fetcher/Ingestor Sunset
Safely sunset two unused merchant-promotion backend services and an unused Redis cache instance, moved remaining responsibilities to the aggregator service, and refactored the main binary to Clean Architecture — delivering a meaningful monthly and yearly infrastructure cost reduction and a cleaner, more maintainable codebase.
- Role
- Backend Engineer
- Timeline
- 2022
- Team
- 2
- Domain
- E-Commerce / Promotion & Discounts
- Stack
- 7 tech
- Status
- Archived
Overview
This project sunset two unused backend services — a fetcher and an ingestor — inside the merchant-promotion domain, removed an unused Redis cache instance tied to merchant promotion, and refactored the main binary to Clean Architecture. The products those services once supported no longer fit current needs, so keeping them running was pure carrying cost. As backend engineer on this cleanup, I owned the audit, the responsibility handover to the aggregator service, and the refactor of the main binary.
Gallery
A backend has no screens, so its "UI" here is the operational surface: the target architecture after cleanup, the cost dashboards that reflected the sunset, and the pull requests that retired the legacy code.
Problem & Solution
The merchant-promotion stack had accumulated legacy: a fetcher service and an ingestor service that no longer served any active product, plus a Redis cache instance that was no longer read from or written to. On top of that, the main binary was structured as a large monolithic struct that mixed concerns and made maintenance hard — new features had to reach into shared internals, and small changes carried outsized regression risk. The result was ongoing infrastructure cost for capabilities that were not being used, and a codebase that was harder than it needed to be to evolve.
I ran a traffic and dependency audit to confirm the fetcher and the ingestor were genuinely unused, then moved their remaining responsibilities into the aggregator service and deleted the legacy code from the main binary. In parallel, I refactored the main binary to Clean Architecture, splitting it into clear handler, use-case, and repository layers so the aggregator could absorb the leftover responsibilities without inheriting the old coupling. Once readers and writers on the unused Redis instance were confirmed at zero, the two services and the Redis cache were shut down in a staged rollout, translating the cleanup into a meaningful monthly and yearly infrastructure cost reduction for server rentals.
Workflow
Three actors interact with the service; the diagrams are written in English to stay consistent across both languages.
Architecture
After the sunset, the merchant-promotion domain is served by a single aggregator service written in Go and organized around Clean Architecture: a handler layer for transport, a use-case layer for business logic, and a repository layer for data access against the merchant-promotion tables. The fetcher service, the ingestor service, and the unused Redis cache instance are removed from the topology entirely. Prometheus and Grafana continue to observe the aggregator to confirm that behavior stayed stable after the responsibilities were consolidated.
Features
- Sunset of the unused fetcher service
- Sunset of the unused ingestor service
- Removal of an unused Redis cache instance tied to merchant promotion
- Legacy code deleted from the main binary
- Responsibilities moved into the aggregator service
- Main binary refactored to Clean Architecture (previously a monolithic `struct` layout)
- Traffic and dependency audit before shutdown
- Staged rollout with observability on the aggregator
- Meaningful monthly and yearly infrastructure cost reduction
Tech Stack
Engineering Challenges
Two services in the merchant-promotion domain were suspected to be dead, but "suspected" is not enough to shut down production. Fix: an end-to-end traffic and dependency audit — checking callers, scheduled jobs, and cache read/write patterns — before draining and shutting the fetcher, the ingestor, and the associated Redis instance.
The main binary was hard to maintain because business logic, transport, and data access all lived inside a single large `struct`. Fix: refactor the main binary to Clean Architecture — separating handlers, use cases, and repositories — so the aggregator service could absorb the leftover responsibilities without inheriting the old coupling.
Deleting code alone does not reduce a cloud bill; unused server rentals and cache instances have to actually be released. Fix: coordinate with the infrastructure owners to shut down the decommissioned services and release the unused Redis instance, translating the cleanup into a meaningful monthly and yearly infrastructure cost reduction.
Outcomes
Lessons Learned
Retiring code is as valuable as writing new code when the retired code was carrying real infrastructure cost — but the savings only materialize when the underlying servers and cache instances are actually released, not just when the repository shrinks. Refactoring the main binary to Clean Architecture during the same cleanup paid off twice: it made the aggregator a natural home for the leftover responsibilities, and it left the domain in a shape that is easier to extend the next time a product needs to change.
Related Santekno Tutorials
Interested in working together?
Order, payment, or high-traffic event pipelines — let's talk architecture and trade-offs.