Start with the operating contract
Boring software is software whose behavior is easy to predict and explain. That does not require old technology or an absence of ambition. It requires choosing complexity for a demonstrated need instead of treating complexity as evidence that a design is sophisticated.
Before choosing an architecture, write a small operating contract. Who depends on the service? How fresh must its data be? What should happen when a dependency is unavailable? What data would be difficult to reconstruct? These answers give design discussions something more useful than competing preferences.
Select the simplest design that meets those constraints and can be supported by the team. A single service may be appropriate; multiple services may have a concrete isolation or ownership benefit. Record the reason for the choice and the evidence that would justify revisiting it.
Make failure behavior explicit
A dependency failure should lead to a deliberate outcome. Set bounded timeouts, limit concurrent work, and decide which operations are safe to retry. Retrying every error can amplify an outage or repeat a side effect. A retry policy needs an understanding of the operation, not just a timer.
For operations that change state, define how repeated requests are recognized and how ambiguous outcomes are reconciled. A client timeout does not prove the server did nothing. If an idempotency key is used, specify its scope, retention period, and treatment of a reused key with different input.
Make degraded behavior visible. Serving cached information can be useful when its age is displayed and the use case permits it. Silently substituting old data for current data turns an availability decision into a correctness problem.
Test at the boundaries
Use focused unit tests for deterministic rules and interface tests for assumptions shared with other components. Reserve broader workflow tests for paths where the integration itself matters. A large test count is less informative than knowing which consequential assumptions are checked.
Introduce failures through controlled simulation or isolated test doubles, not by disrupting a live dependency. Test slow responses, unavailable services, repeated requests, invalid input, and restart recovery. Check the user-visible result as well as the internal error. A technically accurate log message cannot compensate for a misleading success screen.
Keep fixtures readable and representative of documented edge cases. When a defect is fixed, add a test at the narrowest boundary that would have exposed it. Document anything the test deliberately substitutes, especially persistence, authentication, and external delivery.
Design for the next maintainer
Provide a short path from an alert to a useful diagnosis. Name the service owner, identify the signals that matter, and explain where to find recent changes. Logs should connect related work without copying secrets or unnecessary personal data into a new storage system.
Before release, have a human reviewer examine migration behavior, configuration changes, and recovery limits alongside the code. Rehearse restoration using test data. A backup that has never been restored is an unverified recovery mechanism, and a rollback button cannot undo every external effect.
Review complexity periodically. Remove unused options, retire duplicate paths, and document the few surprising decisions that remain. Predictability is not the absence of change. It is the ability to make a change, explain its boundaries, and recover when an assumption turns out to be wrong.
