Software Architecture Interview Questions and Answers (2026)
Software architecture interview questions test judgment under tradeoffs — sync vs async, scalability vs reliability, when CQRS is worth its complexity — more than any single right answer. Here are the ones that come up most.
-
What's the difference between architecture and design?
Architecture is about the high-level structural decisions that are expensive to change later: how the system is decomposed into major components, how they communicate, where data lives, and what quality attributes (scalability, availability, security) the structure optimizes for — e.g., "microservices communicating over async messaging" or "modular monolith with a shared database." Design is the lower-level, cheaper-to-change decisions within a component: class structure, which design patterns to apply, method signatures, how a specific feature is implemented. A useful heuristic: if getting it wrong means a rewrite, it's architecture; if getting it wrong means a refactor, it's design. In interviews, conflating the two is a common tell — describing "I'd use the Repository pattern" is a design answer to what's usually meant to be an architecture question about system decomposition and communication.
-
What's the difference between coupling and cohesion, and why do both matter?
Cohesion measures how closely related the responsibilities within a single module are — high cohesion means a module does one well-defined thing (a
PaymentProcessorthat only handles payment logic). Coupling measures how much one module depends on the internal details of another — low coupling means modules interact through narrow, stable interfaces rather than reaching into each other's internals. The goal is always high cohesion, low coupling: cohesive modules are easier to understand and test in isolation; loosely coupled modules can change independently without cascading breakage. They're related but distinct — you can have low cohesion with low coupling (a disorganized but independent module) or high cohesion with high coupling (a well-focused module that's still tightly wired to five others). Most architecture patterns (layering, microservices, DDD bounded contexts) exist specifically to push a codebase toward this combination. -
Compare monolithic, layered, microservices, and modular monolith architectures.
A monolith is a single deployable unit; "layered monolith" adds internal structure (UI/Application/Domain/Data layers) but is still one deployment and typically one database. Microservices split the system into independently deployable services, each owning its data, communicating over the network — this buys independent scaling and deployment but adds distributed-systems complexity: network failures, eventual consistency, service discovery, distributed tracing. A modular monolith keeps a single deployment and process but enforces strict module boundaries internally (separate assemblies/namespaces, no cross-module direct data access, communication through defined interfaces) — it gets much of microservices' organizational discipline without the operational cost of a distributed system. In practice, modular monolith is increasingly the recommended default: start there, and only split out a true microservice when a component has genuinely different scaling, deployment cadence, or team-ownership needs that justify the operational overhead.
-
What's the core idea behind Clean Architecture / Onion Architecture?
Both organize the system as concentric layers with a strict dependency rule: dependencies only point inward, toward the domain/business logic at the center, never outward. The core (entities, business rules) has zero knowledge of the database, UI, or frameworks surrounding it; those outer layers depend on the core through interfaces defined *by* the core (this is Dependency Inversion applied at the architecture level). For example, the domain defines an
IOrderRepositoryinterface; the infrastructure layer implements it with EF Core, but the domain never references EF Core. The payoff: business logic is testable without spinning up a database or web framework, and you can swap infrastructure (SQL Server to Postgres, REST to gRPC) without touching business rules. The tradeoff is more upfront ceremony (extra interfaces, mapping between layers) that isn't worth it for a simple CRUD app with no real business logic complexity. -
What are the fundamentals of good REST API design, and what do the Richardson Maturity Model levels mean?
Good REST design uses resource-oriented URLs (
/orders/123/items, not/getOrderItems?id=123), correct HTTP verbs (GET is safe/idempotent, PUT is idempotent, POST is not), and meaningful status codes (201 for creation, 404 vs 400 distinction, 409 for conflicts). The Richardson Maturity Model describes four levels: Level 0 (single endpoint, everything via POST — basically RPC over HTTP), Level 1 (multiple resource URLs but ignoring HTTP verbs), Level 2 (proper use of HTTP verbs and status codes — where most production APIs sit), and Level 3 (HATEOAS — responses include links to related actions/resources, letting clients navigate the API dynamically). Versioning is the other core concern — via URL (/v2/orders), header, or content negotiation — because breaking changes are inevitable and consumers need a migration path rather than a surprise outage. -
What are the tradeoffs between synchronous and asynchronous architecture?
Synchronous (request/response, e.g. REST/gRPC calls) is simple to reason about — the caller gets an immediate result or error — but couples the caller's availability and latency to the callee's: if the downstream service is slow or down, the caller is blocked or fails too, and chained synchronous calls compound latency and failure probability. Asynchronous (message queues, event buses like Kafka/RabbitMQ/Azure Service Bus) decouples producer and consumer in time — the producer publishes and moves on, the consumer processes when ready — which improves resilience (a downstream outage doesn't cascade) and enables load leveling. The cost is complexity: eventual consistency instead of immediate confirmation, harder debugging (a request's effects are spread across time and services), and the need for idempotency since messages can be redelivered. The practical rule: use sync for anything the user is waiting on that must confirm immediately (payment authorization), async for anything that can tolerate delay and benefits from decoupling (sending a confirmation email, updating a search index).
-
What are the basics of event-driven architecture?
In event-driven architecture, components communicate by publishing and subscribing to events (facts that something happened, e.g.
OrderPlaced) rather than calling each other directly. A producer publishes an event to a broker without knowing or caring who consumes it; consumers subscribe to the events they care about and react independently. This decouples services in both time and knowledge — theOrderServicedoesn't need to know thatInventoryService,EmailService, andAnalyticsServiceall care aboutOrderPlaced; adding a new consumer requires zero changes to the producer. The tradeoffs: harder-to-trace flow (you can't just read one call stack to understand what happens after an order is placed — you need distributed tracing/correlation IDs), eventual consistency, and the need for careful event schema versioning since many consumers depend on the same event shape. It shines for workflows that naturally fan out to multiple independent reactions. -
What is CQRS and when is it actually worth the added complexity?
CQRS (Command Query Responsibility Segregation) splits the model used to write data (commands — validated, business-rule-enforcing operations) from the model used to read data (queries — often denormalized, optimized purely for fast retrieval), instead of using one model for both. In its simplest form this can just mean separate DTOs/service methods for reads vs writes; in its most involved form it means entirely separate read and write databases kept in sync via events, often paired with Event Sourcing. It's worth the complexity when read and write workloads have genuinely different scaling or shape needs — e.g., writes are complex and low-volume (order processing with business rules) while reads are high-volume and need to be fast and denormalized (a dashboard aggregating data from five entities). It's overkill for standard CRUD screens where the read and write shapes are basically identical — there the added synchronization complexity and eventual consistency just add risk without a corresponding benefit.
-
What are the core DDD concepts of bounded context and aggregate?
A bounded context is an explicit boundary within which a domain model and its terminology are consistent — the same word can mean different things in different contexts (a "Customer" in the Sales context has different data and behavior than a "Customer" in the Support context), and DDD says stop trying to force one unified model across the whole company; instead define clear boundaries and explicit translation between them. An aggregate is a cluster of related objects treated as a single consistency boundary for writes, with one entity as the aggregate root controlling access — e.g., an
Orderaggregate root owns itsOrderLines; you never modify anOrderLinedirectly, only through theOrder, which enforces invariants like "total can't go negative." Aggregates should be as small as possible while still protecting true invariants, because everything inside one aggregate is typically loaded and saved together, and large aggregates hurt performance and concurrency. -
What's the tradeoff between scalability and reliability, and are they the same thing?
They're related but distinct: scalability is the system's ability to handle increased load (more users, more data) by adding resources, typically horizontally (more instances) or vertically (bigger instances); reliability is the system's ability to keep functioning correctly, including under failure conditions (a node dying, a network partition). A system can be highly scalable but unreliable (auto-scales fine under load but has no redundancy, so one instance crash takes down active requests), or highly reliable but not scalable (a resilient single powerful server with failover, that simply can't handle 10x traffic). Achieving both together costs real design effort: horizontal scaling requires statelessness (session state externalized to Redis, not in-process), and reliability requires redundancy, health checks, retries with backoff, and circuit breakers — all of which add operational complexity. In an interview, naming this tension explicitly, and giving one concrete mechanism for each (load balancing + stateless services for scale; redundancy + circuit breakers for reliability), is a strong signal of real experience.
-
What is technical debt, and how should an engineer communicate it to non-technical stakeholders?
Technical debt is the implied cost of rework caused by choosing a faster, lower-quality solution now instead of a better approach that would take longer — like financial debt, it's not inherently bad (sometimes shipping fast and refactoring later is the right call), but it accrues "interest": the longer it's unaddressed, the more it slows down future changes and the riskier it becomes to fix. The key communication skill is translating it out of technical jargon into business impact: not "the OrderService has too much coupling" but "adding a new payment method currently takes 3 weeks instead of 3 days because of how this module is structured, and that gap grows every quarter we don't address it." Framing it with a concrete cost (time, risk, incidents caused) rather than an abstract code-quality complaint is what actually gets debt prioritized against feature work, because it puts it in terms stakeholders can weigh against other tradeoffs.
-
How should you approach designing a system in an interview — what's a practical framework?
Start by clarifying requirements before designing anything: functional requirements (what must it do), non-functional requirements (expected scale, latency, availability targets), and explicit constraints (team size, existing tech stack) — designing without this is the most common failure mode. Next, sketch the high-level components and how data flows between them at a coarse level (client, API layer, core services, data store) before drilling into any one piece. Then dig into the 1-2 areas with the most interesting tradeoffs relevant to the stated requirements — for a high-read system that might be caching and read replicas; for a high-write system that might be write scaling and consistency. Throughout, narrate tradeoffs explicitly rather than presenting one "correct" answer — "I'd use async messaging here because it decouples these services, at the cost of eventual consistency, which is acceptable here because..." Finally, address failure modes and scaling explicitly (what happens when this component goes down, how does this scale to 10x). Interviewers are grading your reasoning process and tradeoff awareness far more than the specific diagram you land on.
Practicing for a real interview? Get live AI help with your answers, in the browser, free to start.
Start a free session