How a senior engineer approaches architecture decisions — trading scalability against delivery speed, changing the architecture midway, build vs buy, evaluating new tech stacks, architectural tech debt, designing for future scale without over-engineering, validating and documenting decisions (ADRs, RFCs), running spikes and proofs of concept, resolving architecture disagreements, and building observability into the design.
Published September 25, 2026
How to use this lesson
Architecture questions at this level are about process and judgement, not naming technologies. A good answer:
starts from the requirements, especially the non-functional ones;
compares options with their trade-offs;
reduces risk with evidence (spikes, benchmarks);
writes the decision down;
keeps it reversible where possible.
Related technical choices (Kafka vs RabbitMQ, sync vs async, API versioning, caching, SQL vs NoSQL, cloud cost) are answered in the Senior architecture and system-design chapters.
Q1. How do you approach architectural decision-making?
Short answer:
Clarify the problem and the drivers:
the functional scope;
the quality attributes, with numbers: throughput, latency, availability, consistency, security, compliance, cost;
the constraints (team skills, timeline, existing platform, budget).
Generate at least two or three options, including "the simplest thing that could work" and "extend what we already have".
Evaluate the trade-offs against the drivers (a simple scoring matrix helps). Consider operability, the team's skills, cost of change, and reversibility (one-way versus two-way doors).
Reduce the uncertainty with spikes, prototypes, benchmarks, or references.
Decide with the right people (the team, plus architecture, security and operations for cross-cutting changes); make the owner of the decision clear.
Document it (an ADR), communicate it, and define when to revisit it (fitness functions, metrics, triggers).
Q2. How do you balance scalability against speed of delivery?
Short answer:
Design for the next order of magnitude, not infinity: if you have 1,000 users, design for about 10–50× growth, not 1,000×.
Keep the expensive-to-change decisions sound (data model, service boundaries, API contracts, idempotency, statelessness), and keep the cheap-to-change ones simple (a single database, a modular monolith, vertical scaling).
Leave seams so you can scale later: stateless services, a clean module boundary, an abstraction around storage or messaging.
Measure: load tests and capacity planning tell you when to invest, before it becomes an incident.
Be explicit with stakeholders: "shipping in 6 weeks means these known limits (for example, about 500 requests per second); here's the plan to lift them when we reach 60% of that."
Q3. Describe a time you changed the architecture midway. Why?
Short answer: Show evidence-driven change and how you managed it. For example: "We started with synchronous REST calls between order, inventory and payment. Load tests at 60% of projected peak showed cascading latency and failures when payment slowed down. We moved order placement to an event-driven saga (Kafka, the outbox pattern, idempotent consumers). I wrote an ADR, got buy-in with the load-test data, and migrated one flow at a time behind a feature flag. It cost 3 sprints, but p99 latency fell from 2.1 s to 250 ms, and a payment provider outage no longer took down checkout."
The lessons: test the non-functional requirements early; decisions should be revisited when the facts change; migrate incrementally.
Q4. How do you decide between building in-house and using a third-party product?
Short answer:
Is it core to our differentiation? Build what makes you different; buy or use open source for commodity capabilities (authentication, payments, email, observability, search).
The total cost of ownership, over 3–5 years:
build: development plus maintenance, on-call, security patches and the opportunity cost;
buy: licences or usage fees, integration, vendor management, and the limits on customisation.
Fit: does it meet the requirements (features, scale, compliance, data residency)?
Risk:
vendor lock-in and exit cost;
vendor health and support;
security review;
SLA;
open-source community health and licence (for example, the licence changes of some projects).
Time to market.
Mitigate lock-in with an internal abstraction (a port or adapter), and data export guarantees.
An example: using a managed identity provider (Keycloak, Auth0, Cognito) instead of building authentication, because security is too high-stakes to build casually.
Q5. How do you evaluate a new tech stack for adoption?
Short answer:
Start from a problem, not the technology: what does the current stack fail to do?
Criteria:
fit to the requirements;
maturity and community (releases, contributors, adoption, documentation);
operational cost (who runs, monitors and upgrades it);
team skills and hiring market;
integration with the existing ecosystem (build, CI, observability, security);
licence and support;
security track record;
exit cost.
Validate with a time-boxed proof of concept on a real use case, measured against success criteria agreed in advance.
Adopt gradually: one team or service first (the tech radar's trial ring), then decide on wider adoption. Document the decision and the guidelines (paved road).
Q6. What's your approach to technical debt in the architecture?
Short answer:
Make it visible: an architecture debt register (the issue, its business impact, risk, and cost to fix), reviewed regularly.
Distinguish deliberate, prudent debt (a known shortcut with a payback plan) from accidental or reckless debt.
Prioritise by impact: what slows delivery the most, or causes incidents, or blocks strategic goals.
Pay it down incrementally:
the strangler-fig pattern for big components;
refactoring during related features;
reserved capacity each sprint or quarter.
Prevent it:fitness functions (ArchUnit rules for dependencies and layers, performance budgets in CI), design reviews, and ADRs that record the known trade-offs.
Q7. How do you make sure the architecture supports future scalability?
Short answer:
Scalable design principles:
stateless services (state in databases, caches or tokens), so they scale horizontally;
asynchronous processing for slow or bursty work (queues, event streams);
caching at the right layers;
partition-friendly data models (a natural shard key, no cross-shard joins on hot paths);
idempotency and backpressure;
loose coupling, with clear contracts.
Capacity planning: measure the current headroom, and forecast from business growth; load test regularly (Gatling, k6, JMeter) against the targets.
Observability to spot the bottlenecks before customers do.
Avoid over-engineering: add the complexity (sharding, microservices, multi-region) when the data says you need it, while keeping the design able to evolve.
Q8. How do you validate architecture decisions with the team? What's your process for documenting them?
Short answer:
Validation:
an RFC or design doc shared for asynchronous comments, then a design review meeting with engineers, SRE and operations, security, and dependent teams;
an architecture review board only for cross-cutting or high-risk decisions (keep it light);
proofs of concept and load tests for the risky assumptions;
fitness functions that keep checking the decisions over time.
Documentation:
ADRs (Architecture Decision Records): a short Markdown file per decision, stored in the repository: title, status (proposed, accepted, superseded), context, decision, alternatives considered, and consequences. Never edit an accepted ADR; supersede it with a new one.
C4 model diagrams (context, containers, components), kept as code where possible (Structurizr, PlantUML, Mermaid).
Runbooks and API specifications (OpenAPI, AsyncAPI).
The ADR structure:
# ADR-012: Use the transactional outbox for order events
Status: Accepted (2026-03-10)
## Context
Orders must publish events reliably; dual writes to DB + Kafka lost events in incidents.
## Decision
Write events to an outbox table in the same transaction; relay with Debezium CDC.
## Alternatives
Kafka transactions only (can't include the DB); polling publisher (more DB load).
## Consequences+ No lost events; + ordering per aggregate. − Extra table and relay to operate.
Q9. How do you run an architectural spike or proof of concept?
Short answer:
Define the question the spike must answer ("can Postgres full-text search meet p95 < 100 ms for 5 million products?"), and the success criteria.
Time-box it (days, not weeks), with a small team.
Test the risky part with realistic data and load, not a toy demo. Measure it.
Write it up: the findings, numbers, limitations, recommendation, and remaining risks. Feed it into an ADR.
Throw away the code (or harden it deliberately). Don't let a prototype become production by accident.
Q10. How do you handle disagreements about architecture in the team?
Short answer:
Separate the people from the problem. Go back to the agreed requirements and quality attributes: they are the neutral criteria.
Put each option in writing, with its trade-offs, so it's compared on its merits (and quieter people are heard).
Resolve the facts with experiments: a spike, a benchmark, a prototype.
Prefer reversible options when it's close, and decide quickly.
Clear decision rights: if there's no consensus, the tech lead or architect decides, explains the reasoning, and records the dissent and the revisit triggers in the ADR.
Disagree and commit: once decided, the whole team supports the decision.
Q11. How do you integrate observability into your design?
Short answer: Treat observability as a design requirement, not an afterthought:
The three signals, plus context:
structured logs (JSON) with a correlation or trace ID;
metrics (Micrometer to Prometheus): the RED metrics (rate, errors, duration) for services, USE (utilisation, saturation, errors) for resources, and business metrics (orders per minute, payment success rate);
distributed tracing (Micrometer Tracing or the OpenTelemetry agent), with context propagated across HTTP and Kafka headers.
SLIs and SLOs defined per user journey, with alerts on symptoms (SLO burn rate), not on every cause.
Health endpoints (Spring Boot Actuator liveness and readiness).
Dashboards per service and per journey, with runbooks linked from the alerts.
Standardise it in a shared starter or platform, so every service gets it by default.
Operational readiness review before go-live: can we detect, diagnose and recover from the likely failures?
Common trap: Spring Cloud Sleuth isn't used in Spring Boot 3. Tracing is done with Micrometer Tracing (with a Brave or OpenTelemetry bridge), or the OpenTelemetry Java agent.
Q12. Follow-up: how do you know when an architecture decision was wrong?
Short answer: When the drivers recorded in the ADR change, or the fitness functions and metrics show the decision isn't meeting them: latency or cost trends, incident patterns, delivery slowing in that area, or repeated workarounds. Reviewing the ADRs periodically (and during postmortems) turns this into a routine check instead of a blame exercise.
Follow-up questions this topic invites — and their answers
Q: What are architecture fitness functions?
A: Automated checks that an architecture characteristic still holds: ArchUnit tests for layering and dependencies, performance budgets in CI, dependency-vulnerability gates, and SLO monitors. They protect the decisions as the code evolves.
Q: What is the C4 model?
A: A set of hierarchical architecture diagrams: system Context, Containers (deployable units), Components (inside a container), and optionally Code. It gives each audience the right level of detail.
Q: What is a one-way door versus a two-way door decision?
A: A one-way door is hard or impossible to reverse (a public API contract, a database choice for core data, a data-deletion policy), so it deserves more analysis. A two-way door is easy to reverse (a library, a feature flag), so decide fast and learn.
Q: When should an RFC be required?
A: For changes that affect several teams, public contracts, data models, security, or significant cost, or that are expensive to reverse. Small local decisions don't need one.