No Silver Bullets, No Golden Hammers - Build What You Need, When You Need It

Share on:

Tech trends are loud. Your backlog is louder. Microservices, serverless, Clean Architecture, each one can shine, but none of them magically fix bad decisions. There is no silver bullet. The golden hammer is even worse, because hitting every problem with the same tool just makes dents.

It usually starts with a diagram that looks like a subway map. There are services with heroic names, a message bus, a service mesh, and a dozen dashboards. Then comes the reality check. One feature needs a change across five repos. Debugging crosses process boundaries. Incidents spread. Everyone is tired. The team did not pick a bad pattern. The team picked too much pattern too early.

Here is the fun part. You do not have to guess the final architecture on day one. You can start simple, learn fast, and keep smart escape hatches so future you can change course without tears.

Start simple, move fast, keep receipts

The best first step for most teams is a well structured monolith. I know, I know. For the last couple of years we have been taught that monoliths are the source of all evil, but hear me out. It's called a modular monolith if you want it to sound fancy. One deploy. One place to observe. One mental model. You ship faster and you learn faster. Keep the door open by designing with seams.

Hexagonal, Onion, and Clean are three ways to say the same thing. Put your business rules in the center. Hide HTTP, databases, queues, and cloud SDKs at the edges behind ports and adapters. If your core imports a web framework or a cloud client, your seam is leaking.

Model first, deploy decisions second. Name bounded contexts like Catalog, Orders, Billing. Package by feature instead of by technical layer. Give each context its own data model. Avoid cross context joins even inside the monolith. Let modules talk through contracts. This is the discipline that makes the future cheap.

How change happens without drama

When a seam hurts, you carve. The Strangler pattern is the gentle path. First stabilize the boundary inside the monolith. Then start publishing domain events even if no one listens yet. Use an outbox table so state change and event publish stay in sync. Create the new service and give it its own database. Route traffic gradually through a gateway or proxy. Watch it. If something goes wrong, flip traffic back. No panic.

On Azure, this is friendly. Keep the monolith on App Service or Container Apps with Azure SQL and Application Insights. Carve Pricing into a new service on Container Apps with its own database. Let the monolith proxy requests to the new service using YARP so clients do not change. Add Service Bus for events when you are ready. You are growing into services instead of migrating to them.

When to split, stated clearly

If you cannot name the bounded context in five words and draw its data boundary on a napkin, you cannot own it as a service. Split only when at least two of these are true: one slice needs independent deploy cadence; one slice has a very different scaling profile; compliance or risk demands isolation; the boundaries are stable and testable. If this feels fuzzy, keep it in the monolith.

Tie the decision to outcomes. Aim for fast feedback and safe recovery. As a rule of thumb, target weekly or better deploys, a CI time under 15 minutes, and a mean time to recovery under 1 hour. If these are not improving inside a monolith, splitting will not save you.

Observability, topology, security, and cost

Put logs, metrics, and traces in from day one. That includes correlation IDs so traces survive when a module becomes a service. Team topology matters because Conway's Law matters. If teams do not map to contexts, splitting code into services will not fix coordination. Keep secrets out of code and prefer managed identities. Isolate PII by boundary when needed. Be honest about costs at small scale. A monolith is cheaper to run and cheaper to understand. Microservices buy autonomy at the price of operations.not care. Watch dashboards. Shift traffic in small steps. Celebrate with cake.

Two quick stories

A five person team ships an internal ops tool as a modular monolith with clean seams. Reporting is heavy during business hours, so they add a background worker that precomputes read models and later sends events to a queue for analytics. They never split into services and they never felt stuck.

A marketplace starts the same way. Six months in, Pricing changes every week while the rest is steady. They stabilize the Pricing boundary, add an outbox, publish events, and extract Pricing as its own service on Container Apps with its own database. The monolith proxies requests to keep clients stable. A few months later, image processing gets its own service because it needs a very different scaling profile. Everything else stays monolithic. No drama.

The architecture tour in plain language

N-Tier is comfort food. Presentation, business, data. Great for straightforward CRUD and internal tools. Lines blur easily, so it can drift as the domain grows.

Hexagonal, Onion, and Clean make tests easy and technology choices boring. That is praise. You pay a little ceremony up front and you get independence from frameworks and vendors in return.

A Modular Monolith is the crowd pleaser. One deploy, strong internal boundaries, clear seams for later. The risk is human. Guard the seams or you get a ball of mud.

SOA is about capabilities and contracts across larger organizational boundaries. It helps enterprise systems play nice. Governance is real, so plan for it.

Microservices shine with many teams, clear bounded contexts, and different scaling needs. They also bring distributed tracing, eventual consistency, versioned contracts, and on call complexity. Autonomy is great. The bill is real.

Event-driven design fits when workflows are naturally asynchronous and when you want an audit trail. It scales well but flows are harder to see and duplicates happen.

Serverless is perfect for spiky traffic, glue jobs, and small services you do not want to babysit. In Azure that means Functions and managed services. Cold starts and platform limits are the trade.

Common objections, answered

We need microservices to hire. Strong teams join strong missions. Clear domain boundaries, fast deploys, and reliable systems are better recruiting stories than a buzzword on a slide.

Scalability will be impossible later. False. A modular monolith with good seams is easy to scale up and out, and the hotspots are the same hotspots you would split later. Start with caching, indexing, and query tuning. Split only when needed.

Our CTO wants Kubernetes. If you have Kubernetes expertise and concrete platform needs, go for it. If not, keep operations boring with platforms like Azure App Service or Container Apps. Moving to Kubernetes later is easier than moving away from it.

Next steps you can take today

Pick one seam in your codebase and write a contract test for it. Add request and correlation IDs to your logs if you do not have them. Put one feature behind a flag. If you are already feeling pain at a boundary, sketch the Strangler steps on a single page and practice routing a tiny slice of traffic through a proxy. Small steps compound.

The happy ending

There is no perfect architecture. There is only a good fit for right now and a better fit for later. Start with the simplest thing that cleanly covers your current needs. Put the business at the center. Hide the messy stuff at the edges. When real pain shows up, move one seam at a time. If you are in .NET and Azure, the tools are all there, but the guidance works anywhere. Build what you need today. Make tomorrow a refactor, not a rewrite.