Spring Cloud Config vs application.yml, @RefreshScope and its caveats, Spring Cloud Gateway vs Zuul, client-side load balancing (Ribbon → Spring Cloud LoadBalancer), Hystrix vs Resilience4j, rate limiting in the gateway, Sleuth and its Spring Boot 3 replacement (Micrometer Tracing + OpenTelemetry), traceId/spanId, OpenTelemetry, Prometheus integration, logs vs metrics vs traces, centralised logging and tracing practices, Spring Cloud Stream, and implementing a saga with Spring.
Published September 25, 2026
Many Spring Cloud interview questions still mention Netflix OSS (Zuul, Ribbon, Hystrix) and Sleuth. Answer with what they did, and what replaced them:
That shows you've actually run Boot 3 in production.
application.yml?Short answer:
application.yml is packaged with each service, or mounted per instance. Changes mean redeploying, or editing each environment separately, and there's no central audit trail.spring.config.import=configserver:http://config:8888), and receive properties for their application name + profile + label (branch or tag):
orders-service-prod.yml, layered over application.yml shared by all services;{cipher});/actuator/refresh, or Spring Cloud Bus broadcasting to all instances).The trade-offs: another critical component to run (make it highly available, and make clients use optional: or fail-fast deliberately). On Kubernetes, ConfigMaps and Secrets (optionally with Spring Cloud Kubernetes) or GitOps often replace it.
Learn it in depth → Configuration Management
@RefreshScope in Spring Cloud Config for? What are the caveats?Short answer: Beans in @RefreshScope are lazily re-created after a refresh event (POST /actuator/refresh, or a Bus event). The scope proxy discards the cached instance, so the next call builds a new one with the new property values. @ConfigurationProperties beans are rebound on refresh automatically, even without the scope.
The caveats:
final classes don't work.DataSource URLs, server ports and already-created infrastructure beans need restarts.Many teams prefer rolling restarts (immutable configuration), and feature flags for runtime toggles.
Short answer:
lb://service);It's the recommended Spring-based API gateway.
Learn it in depth → API Gateway
Short answer: Ribbon (Netflix) is deprecated, and removed from Spring Cloud. Its replacement is Spring Cloud LoadBalancer:
@LoadBalanced RestClient.Builder/RestTemplate/WebClient.Builder, or OpenFeign or HTTP interface clients, resolves http://inventory-service through a ServiceInstanceListSupplier (backed by discovery: Eureka, Consul, Kubernetes; with caching).ReactorServiceInstanceLoadBalancer: round-robin by default, random, or custom (zone preference, health checks, same-instance or sticky preference, weighted).@LoadBalancerClient(name = "inventory", configuration = ...)).On Kubernetes, you often skip client-side balancing, and rely on Service or mesh load balancing instead.
Short answer:
The migration: replace @HystrixCommand(fallbackMethod) with @CircuitBreaker(name, fallbackMethod) plus the YAML configuration, and turn the thread-pool isolation into bulkheads where needed.
Learn it in depth → Circuit Breaker Pattern
Short answer: Use the built-in RequestRateLimiter filter, with RedisRateLimiter: a token bucket in Redis, run as a Lua script, shared across the gateway instances. It's configured with:
replenishRate (tokens per second);burstCapacity;requestedTokens (the cost per request);KeyResolver bean deciding whom to limit: the user ID from the JWT, an API key, a tenant, or the IP address.When limited, it returns 429 Too Many Requests, with X-RateLimit-* headers.
spring:
cloud:
gateway:
routes:
- id: search
uri: lb://search-service
predicates: [ "Path=/api/search/**" ]
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 20
redis-rate-limiter.burstCapacity: 40
key-resolver: "#{@principalKeyResolver}"
@Bean KeyResolver principalKeyResolver() {
return exchange -> exchange.getPrincipal().map(Principal::getName).defaultIfEmpty("anonymous");
}
Key points to cover:
Learn it in depth → Bulkhead & Rate Limiting
Short answer: Sleuth auto-instrumented Spring applications for distributed tracing:
traceId/spanId into the logging MDC, so every log line carried them ([app,traceId,spanId]);In Spring Boot 3, Sleuth isn't supported. Its core moved to Micrometer Tracing:
micrometer-tracing-bridge-otel (OpenTelemetry) or -bridge-brave;logging.pattern.correlation property).Common trap: the source says "the alternative is OpenTelemetry". More precisely, it's Micrometer Tracing, typically bridged to OpenTelemetry. (You can also use the OpenTelemetry Java agent directly.)
Short answer: A tracing facade (like SLF4J, but for tracing), used by Spring Boot 3's observability:
Observation API: Spring components (MVC, WebFlux, RestClient/WebClient, @Scheduled, Kafka, JDBC through datasource-micrometer, Spring Security, and so on) create observations, which produce timers (metrics) and spans (traces) with shared tags.management.tracing.sampling.probability, management.otlp.tracing.endpoint, and propagation types (W3C by default).@Observed on methods, or Observation.createNotStarted(...).observe(...).@Observed(name = "pricing.quote", contextualName = "quote-price", lowCardinalityKeyValues = {"region", "IN"})
public Quote quote(String sku) { ... } // produces the pricing.quote timer and a span
traceId and spanId, and what are they used for?Short answer:
traceId: a unique ID for one end-to-end request across all services (128-bit in W3C). Every span in that request shares it.spanId: the ID of one unit of work (an incoming request, a database call, an outgoing HTTP call), with a parent span ID that forms the call tree.They travel between services in traceparent headers (W3C: 00-<traceId>-<spanId>-<flags>), including through message headers in Kafka.
Uses:
traceId);traceId to clients in error responses, for support.Learn it in depth → Distributed Tracing
Short answer: OpenTelemetry (OTel) is the CNCF vendor-neutral standard for telemetry (traces, metrics and logs):
In Spring Boot 3: use Micrometer Tracing with the OTel bridge plus the OTLP exporter, the OTel Java agent, or Spring Boot's OTLP metrics export. It avoids vendor lock-in: change the backend by changing the Collector's configuration.
Short answer:
micrometer-registry-prometheus (plus spring-boot-starter-actuator).management.endpoints.web.exposure.include=health,prometheus. Metrics appear at /actuator/prometheus, in the text format.http.server.requests (with histograms and percentiles: management.metrics.distribution.percentiles-histogram.http.server.requests=true), HikariCP, Tomcat or Netty, cache, executor, Kafka and logback metrics.MeterRegistry, then Counter, Timer, Gauge, DistributionSummary, or @Timed/@Counted, with low-cardinality tags (never user IDs).management.metrics.tags.application=orders.Learn it in depth → Metrics & Monitoring
Short answer: They're the three pillars of observability:
Correlate them: trace IDs in logs, exemplars linking metrics to traces, and shared service and version tags. The workflow is: a metrics alert → traces → logs. Continuous profiles are an emerging fourth pillar.
Short answer:
logging.structured.format.console=ecs|logstash) written to stdout.Learn it in depth → Centralized Logging
Short answer: A framework for message-driven microservices, with a binder abstraction over brokers (Kafka, RabbitMQ, Kafka Streams, and cloud binders: Pub/Sub, Kinesis, Azure Event Hubs, Solace):
@Bean Function<In, Out>, Consumer<In> and Supplier<Out> beans, bound to destinations by configuration (spring.cloud.stream.bindings.process-in-0.destination=orders).StreamBridge for dynamic publishing.@Bean
Function<OrderPlaced, InvoiceRequested> invoice() { // consumes from orders, produces to invoices
return event -> new InvoiceRequested(event.orderId(), event.total());
}
The trade-off: portability and less boilerplate, against less direct access to broker-specific features. Teams often use Spring Kafka directly for Kafka-heavy systems, when they need fine control.
Short answer:
@Scheduled scans or delayed messages. Or use a workflow engine with Spring Boot starters: Temporal, Camunda 8/Zeebe, or Axon (sagas plus event sourcing).@Transactional local transactions;@TransactionalEventListener(AFTER_COMMIT) for in-process steps;Learn it in depth → Saga Pattern
Q: How does trace context cross a Kafka boundary?
A: The producer's observation writes traceparent into the record headers. The consumer's observation extracts it, and continues the trace (as a child or a linked span). Enable observation on KafkaTemplate and the listener containers.
Q: What's the risk of high-cardinality metric tags? A: Every unique tag combination creates a new time series. Tags like user ID or order ID explode memory and storage in Prometheus, and can take it down. Use bounded values (status code, endpoint template, region).
Q: Head-based vs tail-based sampling? A: Head-based sampling decides at the start of a trace (for example, 10%). It's cheap, but can miss rare errors. Tail-based sampling (in the Collector) decides after seeing the whole trace, so it can keep every error and slow trace, at the cost of buffering.
Q: Is Eureka still needed on Kubernetes? A: Usually not. Kubernetes Services, DNS and readiness-based endpoints provide discovery and load balancing. Eureka is still useful for hybrid VM/Kubernetes estates, or client-side zone-aware balancing.