Senior Java interviews increasingly focus on more than Java syntax and Spring Boot annotations. Interviewers want to know how you secure applications, coordinate distributed transactions, process millions of records, build reliable event-driven systems, and integrate AI into production applications.
Consider a few real-world situations:
These problems require a deep understanding of application behavior, system architecture, and failure handling.
In this guide, we explore 20 advanced Java and Spring Boot interview questions with detailed answers, code examples, architectural considerations, and production troubleshooting techniques.
Spring Security supports method-level authorization, allowing an application to enforce access-control rules when methods are invoked. This is useful when authorization must be enforced at the service layer rather than relying exclusively on controller-level checks.
Enable method security in a modern Spring Security application using:
@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {
}
@PreAuthorize checks an authorization expression before the method executes.
@Service
public class ReportService {
@PreAuthorize("hasRole('ADMIN')")
public Report generateReport() {
return new Report();
}
}
Only callers with the required role can invoke the method successfully through the configured Spring Security method interceptor.
You can also use expressions involving method arguments and the current authentication.
@PreAuthorize("#userId == authentication.name")
public UserProfile getProfile(String userId) {
return userRepository.findProfile(userId);
}
This example assumes that the authenticated principal's name matches the application's user identifier. Real applications must verify that the identity representation is correct and that the rule covers the intended access policy.
@PostAuthorize evaluates an authorization expression after the method executes, making the returned result available through the returnObject variable.
@PostAuthorize(
"returnObject.ownerId == authentication.name"
)
public Document getDocument(Long documentId) {
return documentRepository.findById(documentId)
.orElseThrow();
}
This can help enforce ownership rules when the authorization decision depends on the returned object. However, the method has already executed before the authorization check occurs. Avoid using it when executing the method itself could cause unauthorized side effects.
| Aspect | @PreAuthorize | @PostAuthorize |
|---|---|---|
| When it executes | Before the method body | After the method body |
| Primary purpose | Check permission before execution | Check permission using the returned result |
| Typical use | Role, permission, or argument-based checks | Ownership checks based on the returned object |
| Important limitation | Must correctly model the required permission | Cannot prevent side effects that already occurred |
Senior-level takeaway: Use authorization at the appropriate layer. Method-level security complements request-level security, but it does not eliminate the need for carefully designed data-access rules.
A JSON Web Token (JWT) is commonly validated using its signature and claims, such as its expiration time. In a stateless architecture, the server does not necessarily maintain a session record for every token.
As a result, logging out on the client does not automatically invalidate an already issued access token. The token may remain usable until it expires, provided it continues to pass validation and no additional revocation mechanism is enforced.
Consider this timeline:
10:00 - Access token issued
10:05 - User logs out
10:06 - Previously issued token is presented again
10:30 - Token expires
Without a revocation mechanism, the token may remain valid until 10:30.
1. Use short-lived access tokens.
Keep access-token lifetimes relatively short and use refresh tokens to obtain new access tokens when appropriate. This limits the period during which a stolen access token can be reused.
2. Maintain a token denylist.
Store identifiers such as a JWT's jti claim in a revocation store until the token expires. Each request must check whether the token has been revoked. This adds a stateful lookup to the authorization process.
3. Revoke refresh tokens.
At logout, revoke the refresh token or token family so that new access tokens cannot be obtained through that refresh credential. This does not automatically invalidate an already issued access token.
4. Use a token version or account security state.
An application can associate tokens with a user-level token version or security timestamp and reject tokens that predate the current value. This requires checking the relevant state when validating a request.
5. Use an identity provider's revocation mechanisms.
When using an external identity provider, use its supported token and session revocation capabilities where applicable.
Important trade-off: Immediate revocation usually requires server-side state, a lookup, or a short-lived token strategy. A completely stateless access-token design cannot guarantee immediate revocation without some additional mechanism.
Senior-level takeaway: Logout, refresh-token revocation, and access-token revocation are related but separate operations. Design them according to the application's security requirements.
Broken Access Control occurs when an application allows users to access operations or resources outside their permitted scope.
A common example is an API that accepts a customer ID from the request:
GET /api/customers/1001
GET /api/customers/1002
If user A can change the identifier from 1001 to 1002 and retrieve user B's information, the application has an authorization vulnerability.
This is often called insecure direct object reference (IDOR) or a related object-level authorization failure.
Authentication establishes who the caller is. Authorization determines what that caller may access. A valid JWT does not automatically grant permission to retrieve every resource whose identifier appears in a URL.
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(OrderRepository repository) {
this.repository = repository;
}
public Order getOrder(
Long orderId,
String authenticatedUserId) {
return repository
.findByIdAndCustomerId(
orderId,
authenticatedUserId
)
.orElseThrow(
() -> new OrderNotFoundException()
);
}
}
The repository query should constrain both the resource ID and the authenticated user's permitted scope.
Optional<Order> findByIdAndCustomerId(
Long orderId,
String customerId
);
The user identifier must come from a trusted authentication context, not from a request parameter that the caller can freely manipulate.
For sensitive resources, consider whether returning a not-found response rather than a forbidden response is appropriate to avoid revealing the existence of another user's resource.
Senior-level takeaway: Never assume that knowing a resource ID proves permission to access it. Enforce authorization against the actual resource and its security context.
Cross-Site Request Forgery (CSRF) occurs when a browser is tricked into sending an authenticated request that the user did not intend to make.
For example, suppose a banking application authenticates users using a session cookie. A malicious website may attempt to cause the browser to submit a transfer request to the banking application. Because browsers automatically attach eligible cookies, the request may carry the user's session credentials.
Spring Security provides CSRF protection mechanisms that validate an expected token for applicable requests.
A simplified flow looks like this:
Browser requests application
|
v
Application provides CSRF token
|
v
Browser submits state-changing request
with expected CSRF token
|
v
Server validates the token
|
+-- Valid: continue processing
|
+-- Invalid: reject request
Disabling CSRF protection for a browser-based application that authenticates through automatically attached cookies can expose state-changing operations to cross-site request forgery.
For example, a cookie-authenticated application might be vulnerable if CSRF protection is disabled and a malicious page can trigger a transfer or account-change request.
Some applications expose APIs that authenticate exclusively through explicitly supplied bearer tokens rather than ambient browser credentials. In that architecture, traditional CSRF exposure may be significantly different.
However, the decision must account for the actual authentication mechanism, browser behavior, cookie settings, and any endpoints that use session or cookie authentication. Merely calling an endpoint a REST API does not make it immune to CSRF.
Senior-level takeaway: Understand how credentials are sent by the browser before changing CSRF settings. Security configuration should reflect the real authentication model, not a blanket assumption that every API is stateless.
In a multi-tenant application, multiple customers share an application or infrastructure while their data must remain isolated.
For example:
Tenant A
|
+-- Customers
+-- Orders
+-- Invoices
Tenant B
|
+-- Customers
+-- Orders
+-- Invoices
A serious vulnerability occurs if a request authenticated for Tenant A can access Tenant B's records by modifying an ID or supplying another tenant identifier.
The application must establish the tenant identity through a trusted authentication claim, a validated tenant membership, or another controlled mechanism. Do not trust an arbitrary tenant ID supplied in a request without checking the caller's membership and permissions.
public interface InvoiceRepository
extends JpaRepository<Invoice, Long> {
Optional<Invoice> findByIdAndTenantId(
Long invoiceId,
String tenantId
);
}
Every relevant data operation must apply the tenant boundary. A single repository method that forgets the tenant predicate can expose data even when most other queries are correct.
Tenant isolation must also apply to scheduled tasks, Kafka consumers, exports, search indexes, cache keys, and asynchronous processing.
For example, a cache key should include tenant identity when the cached value is tenant-specific:
String cacheKey =
tenantId + ":invoice:" + invoiceId;
Otherwise, a value cached for one tenant might accidentally be returned to another.
Create tests that attempt cross-tenant reads, updates, deletes, exports, and cache access. Database constraints, row-level security, or separate database credentials can provide additional protection depending on the architecture.
Senior-level takeaway: Multi-tenancy is a system-wide isolation requirement, not simply a tenant ID field added to a table.
Spring manages objects known as beans through its application context. During startup, it reads configuration, registers bean definitions, resolves dependencies, creates bean instances as needed, injects dependencies, and invokes lifecycle callbacks.
A simplified lifecycle for a typical singleton bean is:
Bean definition registration
|
v
Bean instantiation
|
v
Dependency injection
|
v
Aware callbacks
|
v
BeanPostProcessor before initialization
|
v
Initialization callbacks
|
v
BeanPostProcessor after initialization
|
v
Bean ready for use
The actual lifecycle contains additional details, including proxy creation and special handling for some bean types.
Circular dependencies: Bean A depends on Bean B, while Bean B also depends on Bean A. Constructor-based circular dependencies generally cannot be resolved by ordinary dependency injection.
Premature access: A bean may call another bean before the latter has completed its initialization.
External dependency failures: Initialization code may attempt to connect to a database, broker, or remote API that is unavailable.
Heavy initialization: Expensive work performed during bean creation can increase startup time or make application startup fragile.
@Service
public class OrderService {
public OrderService(
PaymentService paymentService) {
}
}
@Service
public class PaymentService {
public PaymentService(
OrderService orderService) {
}
}
This design creates a dependency cycle.
Rather than immediately introducing lazy injection to hide the problem, examine whether the responsibilities should be separated. A third orchestration service, domain event, or narrower interface may remove the cycle.
Senior-level takeaway: Bean initialization should be predictable and should avoid unnecessary external side effects. Design dependencies so the application can start and recover cleanly.
Spring commonly implements @Async through a proxy. When a call passes through the proxy, Spring can intercept the invocation and execute it using the configured task executor.
Consider this example:
@Service
public class ReportService {
public void generateReports() {
generateAsync();
}
@Async
public void generateAsync() {
// Expensive operation
}
}
When generateReports() invokes generateAsync() directly on the same object, the call is known as self-invocation. It bypasses the Spring proxy, so the @Async interceptor is not applied as expected.
@Service
public class AsyncReportService {
@Async("reportExecutor")
public CompletableFuture<Void> generateAsync() {
// Perform report generation
return CompletableFuture.completedFuture(null);
}
}
@Service
public class ReportService {
private final AsyncReportService asyncReportService;
public ReportService(
AsyncReportService asyncReportService) {
this.asyncReportService = asyncReportService;
}
public void generateReports() {
asyncReportService.generateAsync();
}
}
Enable asynchronous execution with @EnableAsync and configure an appropriate executor.
Senior-level takeaway: An annotation does not guarantee that an operation runs asynchronously. Understand proxy boundaries, executor configuration, and how failures are reported.
Consider an e-commerce workflow:
Order Service
|
v
Payment Service
|
v
Inventory Service
|
v
Shipping Service
Each service may own its own database. A local Spring transaction generally controls resources within its configured transaction manager; it does not automatically make updates across independently managed service databases atomic.
If payment succeeds but inventory reservation fails, the business workflow may be left in a partially completed state.
A Saga breaks a distributed business operation into a sequence of local transactions. If a later step fails, the workflow may execute compensating actions to reverse or offset earlier business effects.
Example:
1. Create order
2. Reserve inventory
3. Authorize payment
4. Confirm order
If payment fails:
5. Release inventory
6. Mark order as failed
Compensation is not necessarily a literal database rollback. A captured payment may require a refund, which is a separate business operation and may itself fail.
A central workflow component coordinates the steps, tracks progress, handles timeouts, and decides which action comes next.
This makes workflow visibility clearer, but the orchestrator becomes an important component that must itself be made reliable.
Services react to events published by other services. For example, an order-created event triggers inventory reservation, which publishes an inventory-reserved event that allows the workflow to continue.
Choreography reduces central coordination but can become difficult to understand when many services and failure paths are involved.
Senior-level takeaway: Distributed business workflows usually require explicit consistency and recovery design. Do not assume that a local @Transactional annotation creates an atomic transaction across microservices.
Processing millions of records in a single in-memory collection can consume excessive memory, increase garbage-collection pressure, and make recovery difficult.
Spring Batch commonly supports chunk-oriented processing, where items are read, processed, and written in manageable groups.
Reader
|
v
Processor
|
v
Writer
|
v
Commit chunk
|
v
Continue with next chunk
For example, a job might read customer records, calculate updated eligibility, and write the results in chunks of a configured size.
1. Use bounded chunk sizes.
Choose a chunk size based on memory usage, database behavior, transaction cost, and failure-recovery requirements. A larger chunk may reduce commit overhead but increases the amount of work affected by a failure.
2. Persist job execution metadata.
Spring Batch job repositories record job and step execution state, allowing supported restart scenarios to resume from saved execution information.
3. Make processing restartable.
Use readers and writers whose state and checkpoint behavior are appropriate for the source and destination. Ensure restart logic does not incorrectly skip or repeat business work.
4. Make writes idempotent where possible.
If a failure occurs around a commit boundary or an external side effect, retries can potentially repeat work. Design writes and external operations to tolerate duplicates.
5. Handle invalid records explicitly.
Use skip policies for appropriate data-quality failures and retry policies for transient failures. Record rejected data so it can be reviewed and corrected.
6. Monitor progress.
Track read, process, and write counts; duration; failure rates; skip counts; throughput; and estimated completion time.
Senior-level takeaway: A production batch job must be designed for restart, observability, data quality, and controlled resource usage—not just maximum throughput.
Skip, retry, and recovery address different failure scenarios.
| Mechanism | Purpose | Typical Example |
|---|---|---|
| Skip | Continue processing after an eligible item failure | Reject a malformed input record |
| Retry | Attempt an operation again when a failure may be transient | Temporary database or remote-service failure |
| Recovery | Apply a defined action after retry or failure handling cannot complete normally | Record a rejected item or move it to a recovery workflow |
Skip example: A CSV file contains a record with an invalid date. If the business policy allows the job to continue, the record can be skipped within a configured limit and recorded for correction.
Retry example: A database write fails because of a transient connection problem. Retrying may succeed after the connection recovers.
Recovery example: If a record continues to fail after permitted retries, the application may store it in a rejection table, send it to a dead-letter workflow, or raise an operational alert.
Not every exception should be skipped or retried. A permanent schema error, invalid configuration, or deterministic business-rule violation may never improve with another attempt.
Similarly, a retry can be dangerous when an operation has side effects and its completion status is unknown. For example, a remote payment request may have succeeded even though the response timed out.
Senior-level takeaway: Classify exceptions before defining skip and retry policies. Use retry for potentially transient failures, skip for acceptable item-level failures, and recovery for explicitly designed handling of work that cannot complete normally.
When an application is deployed on multiple instances, each instance may run its own scheduled method. Without coordination, two instances can start the same logical job at the same time.
For example:
Instance A ----+
|
+---- Process daily settlement
|
Instance B ----+
This can result in duplicate payments, repeated notifications, or concurrent updates to the same records.
Spring Batch uses job names and identifying job parameters to distinguish job instances. Its job repository coordinates execution metadata, but correct job identity alone does not guarantee that arbitrary business data cannot be processed concurrently by different jobs or different parameter sets.
A distributed lock or scheduler designed for clustered execution can ensure that only one instance runs a particular scheduled task at a time. The lock must have appropriate expiry, ownership, and failure-recovery behavior.
For record-level processing, mark records as claimed or move them through explicit processing states using atomic database operations. This helps prevent different workers from claiming the same work.
Even with distributed locks, retries, crashes, and uncertain outcomes can create duplicate attempts. Use unique business keys, database constraints, idempotency records, and safe update semantics.
Production checks:
Senior-level takeaway: Scheduling coordination, job identity, atomic work claiming, and idempotency solve different parts of the duplicate-processing problem. A robust design considers all of them.
In real production files, some records may be malformed or violate business rules. Failing the entire job for one invalid record can prevent thousands or millions of otherwise valid records from being processed.
A robust design separates file parsing, validation, business processing, and error reporting.
Check required fields, date formats, numeric ranges, identifier formats, and business constraints before writing data.
Use a skip policy for known, acceptable record-level failures, with a maximum skip count. Use retry for selected transient exceptions, and avoid retrying deterministic validation failures.
For each rejected record, retain enough information to identify and correct it, such as the source file, row number, validation error, and a safe representation of the input.
Be careful not to write sensitive personal information into unrestricted error logs.
File: customers.csv
Records read: 1,000,000
Records processed: 992,450
Records rejected: 7,500
Infrastructure errors: 50
The exact treatment of infrastructure errors depends on whether they are retried, cause a restart, or terminate the job. The report should reflect the actual job outcome rather than treating every non-processed record as an ordinary validation rejection.
Rejected records should be recoverable through a defined correction and reprocessing workflow. Ensure that replaying them cannot accidentally duplicate previously successful business operations.
Senior-level takeaway: Fault-tolerant batch processing requires clear error classification, bounded skip and retry policies, auditable rejection handling, and safe reprocessing.
Retrieval-Augmented Generation, or RAG, combines information retrieval with a large language model (LLM). Instead of relying only on the model's internal knowledge, the application retrieves relevant information from a trusted knowledge source and supplies it as context for generating a response.
A typical RAG architecture looks like this:
User Question
|
v
Embedding / Query Processing
|
v
Vector or Hybrid Search
|
v
Retrieve Relevant Documents
|
v
Build Grounded Prompt
|
v
LLM Generates Answer
|
v
Return Answer with Sources
For example, an internal HR assistant might retrieve the company's leave policy before answering a question about annual leave. The model can then base its response on the retrieved policy instead of guessing from general training data.
The retriever may return irrelevant or incomplete documents. The model may misunderstand the context, combine conflicting facts, or generate claims that are not supported by the retrieved material.
Therefore, production RAG systems should also include:
Senior-level takeaway: RAG improves grounding by supplying relevant evidence, but answer quality depends on retrieval, context construction, model behavior, and verification.
A RAG pipeline commonly has two major workflows: indexing documents and answering user questions.
Documents
|
v
Extract Text and Metadata
|
v
Clean and Normalize
|
v
Split into Chunks
|
v
Generate Embeddings
|
v
Store Vectors and Metadata
Documents may come from PDFs, HTML pages, Word files, internal knowledge bases, or databases. Preserve important structure such as headings, section names, document IDs, timestamps, and access-control metadata.
Very large chunks can contain irrelevant information, while very small chunks can lose context. Use document-aware chunking where possible, preserving paragraphs, sections, tables, and meaningful boundaries.
Chunk size and overlap should be selected through evaluation rather than a universal rule. The right settings depend on the document structure, embedding model, context window, and query types.
An embedding model converts text into numerical vectors that represent semantic characteristics. Documents and user queries must use compatible embedding representations for meaningful similarity search.
Store the embedding alongside relevant metadata such as document ID, chunk ID, source, version, tenant, and access-control attributes.
At query time, generate an embedding for the user's question, retrieve candidate chunks, and optionally apply keyword search, metadata filtering, or reranking.
Supply the selected evidence to the LLM with clear instructions about how to use it. Respect context-window limits and avoid including irrelevant documents.
Important security consideration: Filtering results after retrieval may be too late if unauthorized content has already entered the model context. Enforce access control as part of retrieval and context construction.
Senior-level takeaway: A production RAG system is a data pipeline with retrieval, indexing, access control, versioning, and evaluation—not merely a vector database connected to an LLM.
Prompt injection occurs when untrusted input attempts to influence an LLM to ignore intended instructions, disclose information, or perform unauthorized actions.
It can originate from user prompts, uploaded documents, retrieved web pages, emails, or other external content included in the model context.
For example, a retrieved document might contain text instructing the model to ignore its original task and reveal confidential information.
Do not treat instructions found in retrieved documents as authoritative application policy. Clearly separate trusted instructions from user input and retrieved content in the prompt design.
The LLM must not be the only mechanism deciding whether a user can read a document, access an account, or execute a transaction. Enforce permissions in the application and data-access layers.
If the model can call tools, give each tool the minimum permissions required. Use explicit allowlists, validate arguments, and require authorization before performing sensitive actions.
Do not blindly execute SQL, shell commands, URLs, or arbitrary API calls generated by the model. Use constrained interfaces and validated parameters.
Apply access controls before information enters the model context. Avoid mixing confidential data from different users or tenants in a shared prompt.
Require explicit approval or a separate authorization step for payments, account changes, destructive actions, and other high-risk operations.
Test prompts that attempt to override system instructions, reveal secrets, misuse tools, or exploit retrieved documents. Evaluate both direct and indirect prompt-injection cases.
Senior-level takeaway: Prompt engineering alone cannot guarantee security. Treat the model as an untrusted decision-making component and enforce access control, validation, and side-effect authorization in deterministic application code.
AI applications need evaluation beyond checking whether the endpoint returns HTTP 200. A response can be syntactically valid and still be inaccurate, unsupported, unsafe, or irrelevant.
Build a set of realistic questions with expected answers, acceptable variations, relevant source documents, and examples of questions the system should refuse or escalate.
For RAG systems, evaluate whether the correct documents and chunks appear in the retrieved results. Useful measures can include precision, recall, ranking quality, and whether the required evidence is present in the final context.
Assess dimensions such as:
Test prompt injection, unauthorized information retrieval, sensitive-data leakage, unsafe tool calls, and incorrect authorization behavior.
Measure response latency, token consumption, model errors, timeout rates, concurrency behavior, and the cost of processing representative workloads.
Run the evaluation dataset when changing the model, prompt, embedding model, chunking strategy, retriever, or tool configuration.
Start with a limited rollout, compare quality and operational metrics, and retain a rollback path if the new version performs worse.
Automated LLM-as-a-judge evaluation can help scale assessments, but it should be calibrated against human-reviewed examples. It is not a substitute for independent verification of critical outcomes.
Senior-level takeaway: Evaluate AI quality, security, latency, and cost as separate dimensions. A model upgrade is not automatically an application-quality improvement.
When an AI application scales, model cost and concurrency can become major production constraints. A request may consume tokens for system instructions, conversation history, retrieved documents, tool results, and the generated response.
A simplified cost model is:
Total Cost =
Input Tokens × Input Token Price
+ Output Tokens × Output Token Price
Actual billing depends on the model and provider, including any cached-token, tool, or other pricing rules.
Set sensible maximums for request size, retrieved context, conversation history, and generated output. Do not send an entire document collection when only a few relevant passages are needed.
Improve search relevance, use metadata filters, rerank candidates, and remove redundant chunks before building the prompt.
Use a lower-cost model for straightforward tasks where evaluation shows acceptable quality, and reserve more capable models for tasks that need them.
Cache reusable embeddings, stable retrieval results, or eligible responses when correctness and data-isolation requirements permit it. Include relevant user, tenant, permission, and version context in cache decisions.
Track input and output tokens, cost per request, cost per tenant, model latency, error rates, retries, and output quality.
When a provider is overloaded, unrestricted retries can multiply traffic and cost. Use bounded retries, backoff, jitter, and circuit-breaking strategies where appropriate.
Senior-level takeaway: AI cost control is a combination of prompt design, retrieval quality, model selection, admission control, caching, and operational monitoring.
Consider an order service that saves an order in a database and then publishes an event to Kafka.
A naive implementation might do this:
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
kafkaTemplate.send(
"orders",
new OrderCreatedEvent(order.getId())
);
}
This code does not automatically make the database transaction and Kafka publication one atomic operation.
For example:
Write both the business update and an outbox record within the same database transaction.
@Transactional
public void createOrder(Order order) {
Order savedOrder =
orderRepository.save(order);
OutboxEvent event = new OutboxEvent(
"Order",
savedOrder.getId().toString(),
"ORDER_CREATED",
serialize(savedOrder)
);
outboxRepository.save(event);
}
The order and outbox record are committed or rolled back together within the same database transaction.
A separate publisher reads pending outbox records and publishes them to Kafka. After successful publication, it records the appropriate delivery state.
Business Transaction
|
+-- Save Order
|
+-- Save Outbox Event
|
v
Database Commit
|
v
Outbox Publisher
|
v
Kafka Topic
|
v
Consumer
The publisher may publish the same event again when it retries. Therefore, the outbox pattern helps prevent lost events but does not by itself guarantee exactly-once business effects across the database and Kafka.
Consumers should be idempotent, typically using a unique event ID and a durable mechanism to prevent duplicate processing.
Senior-level takeaway: The outbox pattern coordinates reliable event publication with database changes. Idempotent consumers and operational recovery complete the reliability design.
Repeated processing can occur when a consumer processes a message but its progress is not committed, when it loses its group assignment, or when application behavior causes a message to be retried.
Common causes include:
Verify that the new deployment uses the intended consumer group ID. If the group ID changed, the new group may begin reading according to its offset-reset configuration instead of continuing the previous group's progress.
Compare committed offsets, current consumer positions, lag, and the records being processed. Confirm whether the consumer commits automatically or manually and whether the acknowledgment behavior matches the application design.
If processing takes too long relative to the consumer's polling configuration, the consumer may leave the group and trigger a rebalance. Another consumer can then receive the same uncommitted records.
Look for repeated exceptions, retry loops, deserialization failures, and dead-letter configuration problems. Confirm whether the same record is failing repeatedly or whether multiple records are being replayed.
Kafka consumers can encounter duplicate delivery under common at-least-once processing designs. Use event identifiers, unique constraints, deduplication records, or safe state transitions to prevent repeated business effects.
For example, a payment event should not trigger a second charge merely because the consumer processed the same message again.
Review changes to group IDs, concurrency, acknowledgment mode, error handlers, polling settings, serialization, and processing timeouts.
Senior-level takeaway: Repeated Kafka delivery is not always a broker defect. Investigate offsets, consumer lifecycle, error handling, deployment configuration, and the idempotency of the business operation.
A single endpoint can look fast in an isolated test while the full user journey remains slow because the request crosses multiple services and asynchronous components.
For example:
Web / Mobile Client
|
v
API Gateway
|
v
Order Service
|
+---- Database
|
+---- Kafka
|
+---- AI Service
|
v
LLM Provider
Each component contributes latency, and some work may happen asynchronously after the initial HTTP response.
Establish end-to-end latency and identify whether the issue affects all requests or only particular operations, tenants, or traffic patterns. Use percentiles such as p50, p95, and p99 rather than relying only on average response time.
Instrument the gateway and services so a trace shows how long each span takes and how requests move between components.
A trace might reveal:
Total request latency: 2,400 ms
API Gateway: 40 ms
Order Service: 180 ms
Database query: 120 ms
AI model call: 1,700 ms
Other processing: 360 ms
These figures are illustrative. The point is to identify which operation dominates the latency instead of assuming that the Java controller is responsible.
Use trace IDs and request IDs to correlate structured logs across services. Metrics help identify whether latency is associated with saturation, traffic changes, error rates, or a particular dependency.
Inspect query duration, query plans, indexes, lock waits, connection-pool acquisition time, and the number of database round trips. A fast query measured manually may behave differently under production concurrency.
For asynchronous workflows, monitor consumer lag, processing duration, retries, partition distribution, and time spent waiting for downstream operations.
Kafka processing may not contribute to the initial HTTP response if it is asynchronous. In that case, measure business-workflow completion time separately from request latency.
Separate retrieval time, prompt construction, model-provider latency, time to first token, total generation time, token consumption, and retry overhead. Large prompts and unnecessary context can increase both latency and cost.
If model inference dominates, investigate model selection, prompt length, retrieval quality, streaming, and caching. If database time dominates, inspect SQL and connection contention. If a thread pool is saturated, determine whether work is blocking, excessive, or poorly bounded.
Do not optimize every component at once. Make a targeted change, measure the result, and check that the improvement does not create a new bottleneck elsewhere.
Senior-level takeaway: End-to-end performance is a distributed-system property. Distributed traces, metrics, structured logs, and workload-aware tests help identify where time is actually spent.
Before releasing a Spring Boot application, review these areas:
Focus on method-level authorization, JWT lifecycle and revocation, resource ownership, CSRF, and tenant isolation. Be prepared to explain both the security mechanism and its limitations.
Use appropriate job and step configuration, persisted execution metadata, suitable readers and writers, clear job parameters, and idempotent business operations. Test restart behavior after failures rather than assuming it will work automatically.
No. RAG supplies relevant evidence to the model, but retrieval can be incorrect or incomplete and the model can still produce unsupported answers. Evaluate retrieval and generation quality separately.
The transactional outbox pattern stores the business update and event record in the same database transaction. A separate publisher delivers the event, while idempotent consumers handle possible duplicate delivery.
Use distributed tracing to identify expensive spans, correlate them with structured logs and metrics, inspect database and downstream behavior, and distinguish synchronous request latency from asynchronous workflow completion time.
Senior developers are not defined by how many annotations they know. They are defined by how well they handle security, reliability, scalability, and complex production problems.
A strong developer understands why a JWT remains valid after logout, how a batch job recovers after a failure, why database transactions and Kafka publishing can become inconsistent, and why RAG does not guarantee correct AI responses.
More importantly, a senior engineer can explain the trade-offs, identify failure scenarios, design recovery mechanisms, and prove that a solution works under realistic conditions.
Which question would you find the hardest?
Java Spring Boot & Microservices Interview Q&A (200 Questions)
https://lnkd.in/gqhJhmcZ
Full Interview Kit (600 Questions)
Java, Spring Boot, database, DevOps, cloud, system design, and microservices.
https://lnkd.in/gPi2W587
Complete Java Full Stack Developer Kit (900 Questions)
Core Java, Spring Boot and microservices, cloud, DevOps, system design, React, and Angular.
https://topmate.io/logicbrace/2306382
0 Comments