Attach cross-cutting functionality as a companion process next to a service, instead of building it into every service's own code.
Logging, metrics collection, TLS, and retry logic are needed by every service in a fleet — but if each team implements them inside their own service code, you get five slightly different logging libraries across five languages, each needing its own updates and bug fixes.
The sidecar runs as a separate process (or container) deployed alongside the main service, on the same host or pod, sharing its network namespace. The main service stays focused on business logic; the sidecar handles the cross-cutting concern and can be upgraded independently, in any language.
The main service calls its sidecar over localhost; the sidecar handles logging and forwards the real request — the service code never sees that plumbing.
// Sidecar: runs as a separate process, shares the host with the service.
class LoggingSidecar {
async forward(request, actuallyCall) {
const start = Date.now();
console.log(`[sidecar] outbound ${request.method} ${request.path}`);
const response = await actuallyCall(request);
console.log(`[sidecar] completed in ${Date.now() - start}ms`);
return response;
}
}
// Main service: only knows about business logic.
const sidecar = new LoggingSidecar();
async function getOrder(orderId) {
const request = { method: "GET", path: `/orders/${orderId}` };
return sidecar.forward(request, async () => ({ id: orderId, total: 42 }));
}
const order = await getOrder("order-1");
console.log(order);