Apple Java Developer Interview Questions & Answers

apple icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

21. What API design would you use for one application?API DesignMediumApple

Question Details

Describe the API surface, request and response shape, and versioning for one application.

Short Interview Answer (30-60 seconds)

At a high level, I would design a simple REST API for a task application. The API exposes versioned task routes under /api/v1/tasks for reading, creating, updating, and deleting tasks. The client sends HTTPS requests with a Bearer JWT. The Controller validates and binds the request, the Service applies business rules and transaction handling, and the Repository uses JPA or JDBC with PostgreSQL. A successful create returns 201 Created with the saved task data and links. I would keep additive changes in v1 and use v2 for breaking changes. The trade-off is extra maintenance while versions overlap.

Detailed Explanation

This question asks me to design a clear way for a web or mobile application to work with tasks. The client needs to create tasks, read them, change them, and delete them. I also need to show what information the client sends and what comes back after a request. Another goal is to let the interface change later without suddenly breaking older clients. I would explain the design in the same order as the diagram: client request, application processing, database access, response, versioning, errors, and monitoring.

Useful Questions to Ask the Interviewer
  • Is this interface mainly for our own web and mobile clients?
  • Do we expect older clients to remain active when breaking changes are introduced?
What API design would you use for one application? diagram
How to Explain It in an Interview
1. Define the application boundary

I would use one Spring Boot Task API inside the Java Application Boundary. The Web / Mobile Client stays outside that boundary. PostgreSQL also stays outside because it is the persistent data store.

The client sends requests to the API over HTTPS with a Bearer JWT. The diagram keeps the design focused. It does not add an API gateway, cache, queue, or other infrastructure.

2. Define the API surface

The task resources use URI versioning under /api/v1.

The API surface is:

  • GET /api/v1/tasks
  • GET /api/v1/tasks/{id}
  • POST /api/v1/tasks
  • PATCH /api/v1/tasks/{id}
  • DELETE /api/v1/tasks/{id}

GET reads tasks. POST creates a task. PATCH changes part of one task. DELETE removes one task. The {id} value identifies one task.

3. Explain the create request

For the create flow, the client sends POST /api/v1/tasks. The request contains title, description, dueDate, priority, and status.

The Controller receives the REST request. It owns the REST endpoints, request validation, and DTO binding. A DTO is a small object that carries request data inside the application.

The request then moves from the Controller to the Service. The flow includes validation and DTO mapping before the business work continues.

4. Apply business rules and store the data

The Service owns business logic, transaction management, and DTO mapping. It applies the rules for the task and then calls the Repository.

The Repository owns data access, JPA repositories, and entity mapping. It communicates with PostgreSQL through JPA or JDBC.

This separation gives each layer one clear responsibility. HTTP handling stays in the Controller. Business rules stay in the Service. Database work stays in the Repository.

5. Return the successful response

After the task is created, the API sends a separate response back to the Web / Mobile Client.

The response status is 201 Created. The response contains id, title, description, dueDate, priority, status, createdAt, updatedAt, and links.

The id identifies the stored task. The timestamps show when it was created and updated. The links provide related API locations. The important point is that this response travels from the API back to the client.

6. Explain errors and versioning

The diagram defines one standard error body: { code, message, details, traceId }. This gives clients one predictable shape for API errors. The traceId can also help connect a client-visible error with operational records.

For versioning, I would keep non-breaking additive changes in /api/v1. Breaking changes move to /api/v2. When an old version is being retired, the API can mark it with Deprecation and Sunset headers.

7. Record logs and metrics

The Spring Boot Task API sends request logs, latency, and status information to Logs / Metrics. That flow supports centralized logging and metrics collection.

This is a supporting path. It is not part of the normal business response path. The client response still returns directly from the API to the Web / Mobile Client.

Practical Complexity & Trade-offs

The design is intentionally simple. Resource-style routes make the task operations easy to understand. The Controller handles REST requests and validation. The Service keeps business rules and transaction handling. The Repository isolates PostgreSQL access. The benefit is clear ownership. The downside is that even a small application has several layers to maintain. URI versioning is also easy for clients to see. Additive changes can stay in /api/v1, while breaking changes move to /api/v2. The downside is that two versions may need support at the same time. A standard error body makes failures easier for clients to handle. Request logs, latency, and status metrics make production problems easier to investigate, but they add operational work.

Why Interviewers Ask This

Interviewers ask this question to see whether I can turn one application into a clear API contract. They want sensible resources, correct HTTP methods, clear request and response shapes, and proper separation between web handling, business logic, and data access. They also check whether I understand request validation, versioning, predictable errors, database access, and monitoring. The main test is engineering judgment: keeping the first design simple while leaving a controlled path for future changes.

Interviewer may ask next
What would you change if a new requirement needs a breaking change to the task API?

I would introduce /api/v2 for the breaking contract and keep the existing /api/v1 routes working during the migration period. The current POST /api/v1/tasks contract would continue accepting title, description, dueDate, priority, and status. The new v2 contract could change that shape without silently changing v1 behavior. The Controller would still own the REST endpoints and request validation. The Service would still own business rules, transaction management, and DTO mapping. The Repository would still handle PostgreSQL access through JPA or JDBC. I would mark the retiring v1 interface with Deprecation and Sunset headers, as shown in the design. This lets clients move on a known schedule. Correctness is maintained because each version has an explicit contract. The main downside is maintenance cost. During migration, the application may need to support and test both versions. I would therefore keep additive, non-breaking changes in v1 and create v2 only when compatibility cannot be preserved.

How would you make failures easier for clients and operators to understand?

I would keep the standard error contract and the Logs / Metrics flow already shown in the design. API errors use the same body shape: { code, message, details, traceId }. A stable structure means the Web / Mobile Client does not need a different error parser for each task endpoint. The traceId also gives the client and operations team a common value for investigating a failure. Separately, the Spring Boot Task API sends request logs, latency, and status information to Logs / Metrics. That monitoring path does not replace the client response. It also does not become part of the Controller-to-Service-to-Repository business path. It only records operational information about requests. The existing task routes, PostgreSQL access, and response flow remain unchanged. The main downside is extra operational work because logs and metrics must be collected, stored, and reviewed. The benefit is faster diagnosis when requests fail or become slow.

22. Design API and code that with Java.API DesignMediumApple

Question Details

Design an API in Java and explain the interface, inputs, outputs, and implementation approach.

Short Interview Answer (30-60 seconds)

At a high level, I would design a small CRUD API for tasks. The API Consumer sends an HTTP request to TaskController, which implements the TaskApi interface and handles request mapping and validation. Validated task input goes to TaskService for business rules, then TaskRepository performs the database operation. Results return through the same layers as TaskResponse plus an HTTP status. For consistent failure handling, validation, business, and data errors go through one Exception Mapper. The trade-off is extra layers, but each responsibility stays clear and easier to maintain.

Detailed Explanation

This question asks me to design a simple way for another program to create, read, change, and delete tasks. I need to explain what information comes in, what comes back, and which part does each job. The main challenge is keeping the work clear and easy to change without adding too many moving pieces. I also need to show what happens when something goes wrong. I will follow the picture from the caller, through the task-handling parts, to stored task data, and then explain how the result or error returns.

Useful Questions to Ask the Interviewer
  • Are these four task operations enough for the first version?
  • What fields should TaskRequest and TaskResponse contain?
  • What validation rules should apply to task input?
  • Are there database rules that affect create, update, or delete operations?
Design API and code that with Java. diagram
How to Explain It in an Interview
1. Start with the API contract

I would start by defining the operations the API supports. The diagram shows POST /tasks, GET /tasks/{id}, PUT /tasks/{id}, and DELETE /tasks/{id}. The TaskApi interface represents these operations in Java. It has createTask(TaskRequest), getTask(id), updateTask(id, TaskRequest), and deleteTask(id). TaskController implements this interface.

The request can contain path parameters, query parameters, or a JSON TaskRequest. The output is TaskResponse JSON plus an HTTP status. Keeping this contract clear makes it easier for callers and the implementation to agree on expected behavior.

2. Receive and validate the request

The API Consumer can be a web, mobile, or partner application. It sends an HTTP request to TaskController inside the Java Application boundary.

TaskController handles HTTP and maps the request and response. It produces validated task input before calling TaskService. If validation fails, the controller sends the validation error to the Exception Mapper instead of continuing through the normal success path.

This keeps request-specific work near the edge of the application.

3. Apply business rules in TaskService

TaskController sends validated task input to TaskService. TaskService owns the business rules and orchestration. The controller delegates this work instead of putting business decisions inside HTTP handling.

TaskService decides which persistence action is needed and sends a CRUD command to TaskRepository. If a business or data error occurs on this path, TaskService sends that error to the Exception Mapper.

This keeps business behavior separate from both HTTP handling and database access.

4. Persist data through TaskRepository

TaskRepository owns persistence. Its responsibilities are save, find, update, and delete.

TaskService sends a CRUD command to TaskRepository. The repository sends an SQL query or transaction to the Relational Database, which stores the tasks table. The database returns rows or a result to TaskRepository. TaskRepository then returns the Task entity to TaskService.

This prevents the controller and service from directly handling SQL details.

5. Return the successful response

The response uses separate arrows back through the application. TaskRepository returns the Task entity to TaskService. TaskService returns TaskResponse plus an HTTP status to TaskController.

TaskController maps that result into the HTTP response and sends it back to the API Consumer. The diagram does not define exact success status numbers, so I would not invent them.

6. Handle failures consistently

The Exception Mapper provides one consistent error path. Validation errors from TaskController and business or data errors from TaskService flow into it.

The Exception Mapper converts exceptions into standard error JSON. It then returns a 4xx or 5xx error JSON response to the API Consumer, as shown in the diagram.

The main trade-off is extra layers and mappings. That adds code, but each layer has one clear responsibility. The design is easier to test, maintain, and change without mixing HTTP, business, and persistence concerns.

Practical Complexity & Trade-offs

The main design choice is separation of responsibility. TaskController handles HTTP, request mapping, response mapping, and validation. TaskService handles business rules and orchestration. TaskRepository handles persistence. The benefit is that each part stays focused and easier to test. The downside is more classes and more mapping between layers. The TaskApi interface also makes the contract clear, but it adds another abstraction to maintain. A single Exception Mapper keeps 4xx and 5xx error JSON consistent instead of formatting errors in several places. The database path is synchronous, so the current request waits for the repository and database result before returning. This design is simple enough for an interview while still showing clear boundaries and failure handling.

Why Interviewers Ask This

Interviewers ask this question to see whether I can create a clear API boundary instead of putting everything inside one controller. They want to check that I understand HTTP methods, request and response direction, input validation, business logic, persistence, and error handling. They also look for good ownership decisions, such as keeping database work in the repository and business rules in the service. The important skill is explaining those choices clearly and discussing the cost of adding layers.

Interviewer may ask next
What happens if the database operation fails while handling a task request?

The request should use the existing error path instead of introducing a new architecture. TaskController still receives the HTTP request and sends validated task input to TaskService. TaskService sends the needed CRUD command to TaskRepository, and TaskRepository performs the SQL query or transaction against the Relational Database. If that work produces a data error, the error follows the diagram's business/data error path from TaskService to the Exception Mapper. The Exception Mapper converts the exception into standard error JSON and returns a 4xx or 5xx error JSON response to the API Consumer. I would not add retries, queues, or fallback behavior because the current diagram does not show them. The benefit is predictable error handling using the same path as other failures. The downside is that a database failure can still make the current request fail because this design uses a direct synchronous database flow.

How would you keep request validation consistent across the task endpoints?

I would keep validation at the TaskController boundary shown in the diagram. POST /tasks receives a JSON TaskRequest. PUT /tasks/{id} uses the path id together with a TaskRequest. GET /tasks/{id} and DELETE /tasks/{id} use the path id. The input callout also shows query parameters as a supported input type. TaskController handles the HTTP mapping and produces validated task input before calling TaskService. If validation fails, the controller sends the validation error to the Exception Mapper. The mapper then returns the same standard 4xx/5xx error JSON path used by the design. TaskService, TaskRepository, the Relational Database, and the normal response path remain unchanged. The benefit is one clear validation boundary before business logic starts. The downside is that TaskController owns more request-specific work, so its validation rules must remain aligned with the TaskApi contract.

23. How would you design an API to reverse an array?API DesignMediumApple

Question Details

Define the inputs, outputs, in-place behavior, and other considerations for an array-reversal API.

Short Interview Answer (30-60 seconds)

At a high level, I would expose one versioned endpoint, POST /v1/arrays/reverse, that accepts an items array and an optional inPlace flag. The Java application validates the request, then uses a two-pointer algorithm to reverse the array in O(n) time. With inPlace=true, it reverses the server-owned working array. With false, it creates a separate reversed copy. A 200 response returns reversedItems, inPlace, and length. The main trade-off is memory: copy mode is simpler for preserving the working array, while in-place mode uses less auxiliary space.

Detailed Explanation

This question asks us to design a small service that receives a list of values and sends those values back in reverse order. We need to decide what the caller sends, what the service returns, and what the in-place option really means. We also need simple rules for bad input and very large lists. The goal is a clear and predictable API. The diagram keeps the solution small: one Java application validates the request, reverses the array, formats the result, and sends the response back.

Useful Questions to Ask the Interviewer
  • Should the array allow mixed value types and null values?
  • What maximum array length should the API accept?
  • Should inPlace default to false when the caller omits it?
How would you design an API to reverse an array? diagram
How to Explain It in an Interview
1. Start with the API contract

I would use the versioned endpoint POST /v1/arrays/reverse. The request uses Content-Type: application/json. Its required field is items, which must be an array. The optional inPlace field is a boolean and defaults to false. The diagram allows strings, numbers, booleans, and null values, including mixed types. An empty array is also valid.

For example, the caller can send {"items":["a","b","c"],"inPlace":true}. The client sends this HTTP request to the Java application.

2. Validate the request before reversing anything

The Input & Behavior Validation step checks the request. The payload must not be null. The items field must exist and must be an array. The inPlace field must be a boolean when provided. The design also uses a maximum-length guardrail, shown as about 100,000 elements.

If the JSON is invalid, items is missing, or a field has an invalid type, the API returns 400. If the payload is too large and exceeds the limit, it returns 413.

3. Define the in-place behavior carefully

This is an HTTP JSON API. The server receives serialized data and creates its own in-memory representation. Therefore, inPlace=true means the Java application reverses its server-owned deserialized working array directly. It does not mutate the caller's original array or preserve client-side object identity.

When inPlace=false, which is the default, the server creates a separate reversed copy. The flag therefore controls server-side memory behavior, not mutation of a collection in the caller's process.

4. Reverse the array with two pointers

The Reverse Engine performs the reversal. It uses two pointers, one at the left end and one at the right end. It swaps those elements, moves both pointers inward, and continues until they meet.

This takes O(n) time. The in-place reversal algorithm needs O(1) auxiliary working space after request deserialization. Copy mode needs O(n) additional space for the separate reversed server-side array.

5. Build and return the response

The Result Formatter builds the success response. It returns the reversed array, the effective inPlace value, and the array length. For the example request, the result is {"reversedItems":["c","b","a"],"inPlace":true,"length":3}.

The Java application returns the result to the client as 200 OK with Content-Type: application/json. The response is a separate flow from the Java application back to the client.

6. Explain the main design considerations

I would keep this API stateless. The server does not retain request state after returning the result, and no persistence layer is needed. The /v1 path provides a version boundary for backward compatibility.

The main trade-off is memory. In-place reversal uses less auxiliary working memory, but the HTTP request and response still require memory for deserialization and serialization. Copy mode preserves the original server-side working array, but it needs O(n) additional memory. The maximum-length guardrail helps control memory use and processing time.

Why Interviewers Ask This

Interviewers use this question to see whether you can turn a simple algorithm into a clear API contract. They are checking whether you define inputs, outputs, defaults, validation rules, status codes, and request and response flow correctly. They also want to see whether you understand that HTTP serialization breaks client-side object identity, so in-place behavior must be explained carefully. A strong answer also discusses time, memory, size limits, statelessness, and versioning without adding unnecessary infrastructure.

Interviewer may ask next
What would you do if clients need to send arrays larger than the current 100,000-element guardrail?

I would keep POST /v1/arrays/reverse and change the size guardrail only after checking the memory and latency impact. In the current design, the Input & Behavior Validation step rejects an oversized payload with 413 before the Reverse Engine processes it. If we raise the limit, the request contract, two-pointer reversal logic, Result Formatter, and 200 OK success response can remain unchanged. The main concern is resource use. The reversal is still O(n), but the server also needs memory for the deserialized request and serialized response. With inPlace=false, it needs another O(n) array for the reversed copy. Correctness stays the same because validation still happens before reversal and both modes preserve the same ordering rules. The downside is that larger requests use more memory and take longer to process, so increasing the limit makes each call more expensive.

Why keep an inPlace option if an HTTP API cannot mutate the caller's original array?

I would keep inPlace only as a server-side processing choice, which is how the diagram defines it. For POST /v1/arrays/reverse, the caller sends JSON, so the Java application receives its own deserialized working array instead of the caller's original object. With inPlace=true, the Reverse Engine swaps elements inside that server-owned array and uses O(1) auxiliary working space after deserialization. With inPlace=false, it creates a separate reversed array and uses O(n) additional space. The response contract stays the same: the client receives reversedItems, inPlace, and length in a 200 OK response. Both modes produce the same reversed ordering. The main downside is naming clarity. Some callers may think inPlace means their local array changes, so the API contract must clearly state that client-side mutability and object identity are not observable through JSON and HTTP.

24. How would you design a Java client to call REST APIs and maintain versions?API DesignHardApple

Question Details

Explain the client interface, request handling, and versioning strategy for REST API calls in Java.

Short Interview Answer (30-60 seconds)

At a high level, I would hide REST details behind a typed Java client so application code stays simple. The Application Service calls a versioned UserApiClient interface. The client maps DTOs, adds a JWT or API key, and sends HTTPS through Java HttpClient or Spring WebClient. The API Gateway routes the request to /api/v1/users or /api/v2/users. Responses return as JSON plus a status code, then become typed DTOs or mapped Java exceptions. I would also use timeouts, retries, circuit breaking, logging, and a clear deprecation window. The trade-off is extra client maintenance for safer version upgrades.

Detailed Explanation

This question asks how I would build a Java client that talks to a REST service while supporting more than one API version. The goal is to keep normal application code simple even when the remote API changes. The main challenge is letting old callers continue with v1 while newer callers move to v2. The client also needs authentication, clear error handling, and protection from slow or failing remote calls. I would explain the design by following the exact path in the diagram from the Java application to the REST service and back.

Useful Questions to Ask the Interviewer
  • Do we need to support v1 and v2 at the same time?
  • Should the version be carried in the URI path, a header, or both?
  • How long should an old version remain supported before deprecation?
  • Should the client use a JWT, an API key, or either option?
How would you design a Java client to call REST APIs and maintain versions? diagram
How to Explain It in an Interview
1. Keep REST details behind a typed client

I would start with the Application Service calling UserApiClient through a typed method call. Inside the Java REST Client Library, I would expose versioned interfaces such as V1Client and V2Client. This keeps URLs, headers, JSON handling, and HTTP details outside business code. Separate interfaces or packages also let old callers remain on v1 while new callers move to v2.

2. Choose the version explicitly

The Version Config / Registry stores the base URL, default version, supported versions, and deprecation policy. It selects v1 or v2 for the client interface. The diagram prefers explicit versioning through a URI or header. For example, the service exposes /api/v1/users and /api/v2/users. A header such as X-API-Version can also carry the selected version. The important point is that the chosen contract is clear to both the client and server.

3. Build and authenticate the request

The selected interface sends the call to the Request Builder + DTO Mapper. It maps domain objects into request DTOs. DTOs can be shared when the versions remain backward compatible. Otherwise, each version should use its own DTOs. The Auth Interceptor then adds the JWT or API key. This keeps authentication handling in one client component instead of duplicating it in every caller.

4. Send the request with reliability controls

The HTTP Transport uses Java HttpClient or Spring WebClient. It sends an HTTPS GET or POST request with the selected version in the path or header. Retry, timeout, and circuit-breaker behavior surrounds outbound calls. These controls reduce the effect of slow or temporarily failing remote calls. The transport also sends latency, status, and retry information to Logging + Metrics for logs, metrics, and tracing.

5. Route the request to the correct REST version

The API Gateway / REST Endpoint receives the request and routes it to v1 or v2. The selected REST resource is /api/v1/users or /api/v2/users. That resource calls the Backend Service, which owns the business logic and data access. The Backend Service returns its payload and status to the versioned resource. The HTTP response then moves back through the gateway toward the Java client.

6. Convert the response into Java results

The gateway returns JSON and the HTTP status code to the HTTP Transport. The response body then moves to the Response Parser + Error Mapper. This component parses JSON into DTOs. It also handles HTTP 4xx and 5xx responses and converts them into Java exceptions. Finally, the Application Service receives either a typed result or a mapped exception.

7. Migrate versions without breaking old callers

I would introduce a new interface or package beside the existing version instead of changing v1 in place. Both versions can remain available during a compatibility window. The deprecation policy should advertise the sunset date for the older version. The benefit is safer migration. The downside is temporary duplication in interfaces, DTOs, tests, and maintenance while both versions are supported.

Practical Complexity & Trade-offs

The benefit of this design is that application code does not need to manage URLs, JSON parsing, authentication headers, or HTTP errors directly. Versioned interfaces also let v1 and v2 exist together, which makes migration safer. The downside is extra client code, tests, and maintenance while both versions are supported. URI versioning is easy to see and debug, while header versioning keeps URLs cleaner but makes the chosen version less visible. Timeouts, retries, and circuit breaking improve resilience around outbound calls, but they add configuration and operational complexity. Mapping 4xx and 5xx responses into Java exceptions gives callers a simple model, but the mapping must preserve enough information for debugging. We accept this added client complexity because it reduces breaking changes for application callers.

Why Interviewers Ask This

The interviewer is checking whether I can create a clean boundary between Java application code and an external REST API. They want correct request and response flow, sensible versioning, authentication placement, DTO mapping, HTTP error handling, and reliability controls. They also want to see judgment about backward compatibility and deprecation. A strong answer explains how v1 and v2 can coexist, how failures return to callers, and what maintenance cost comes with supporting several versions.

Interviewer may ask next
What would you do if the v2 API became unreliable while many callers still depended on v1?

I would keep the existing versioned interfaces and contain the problem around the outbound HTTP Transport. V1 callers would continue using the v1 contract and route. V2 callers would continue using the v2 contract, but their outbound requests would still use the configured timeout, retry, and circuit-breaker behavior shown in the design. If v2 becomes slow or starts failing, the timeout limits how long the client waits, while the circuit breaker can stop repeatedly sending calls to an unhealthy remote path. Logging + Metrics would record latency, status, and retry activity so the failure is visible. I would not silently redirect a v2 call to v1 because the two contracts may differ. The affected components are the HTTP Transport, resilience controls, and observability flow. The downside is that some v2 calls may fail quickly while the remote version is unhealthy, but the rest of the design remains unchanged and v1 callers are not forced onto a different contract.

How would you migrate callers from v1 to v2 without breaking existing Java code?

I would add the new client version beside the old one instead of changing the existing interface in place. The Java REST Client Library would keep V1Client for current callers and expose V2Client for callers that are ready to migrate. The Version Config / Registry would continue listing both as supported versions during the compatibility window. If request and response shapes remain backward compatible, some DTOs can be shared. When the contracts diverge, I would keep version-specific DTOs and packages so v2 changes cannot alter v1 behavior. The API Gateway would continue routing /api/v1/users and /api/v2/users to their matching versioned resources. Teams could then migrate one caller at a time and verify the typed result and mapped exceptions. The deprecation policy would advertise when v1 support ends. The downside is temporary duplication in interfaces, DTOs, tests, and maintenance, but it avoids a risky all-at-once migration.

25. How would you handle API retries when a call fails?API DesignMediumApple

Question Details

Explain how you would retry a failed API call three times with a one-second delay and what failure handling you would add.

Short Interview Answer (30-60 seconds)

At a high level, I would keep retry handling inside the Java Application. The Business Service sends the outbound call through a Retry Executor and HTTP Client to the External API. A 2xx response returns normally. For temporary failures such as timeouts, 429, or 5xx, I wait one second and retry up to three times. I do not retry 400, 401, 403, or validation errors. If all attempts fail, I use structured logging, metrics, a circuit breaker, and a meaningful error or fallback. The trade-off is more latency and downstream load during failures.

Detailed Explanation

The question asks how to make an outside service call more reliable when something temporarily goes wrong. We want the caller to get a useful result without trying forever. We also want to avoid repeating requests when another try will not help. The design makes one normal call first. Temporary problems can be tried three more times, with a one-second wait before each retry. Permanent problems stop immediately. If every allowed attempt fails, the application records the problem and returns an error or fallback. The explanation follows that exact flow.

Useful Questions to Ask the Interviewer
  • Does three retries mean three retries after the initial call?
  • Which failures should be considered temporary and safe to retry?
  • Can the operation create duplicate side effects when repeated?
  • Is a fallback acceptable when the External API remains unavailable?
How would you handle API retries when a call fails? diagram
How to Explain It in an Interview
1. Start with the normal request path

I would first explain the normal call. The Caller / Upstream Service sends a request to the Business Service inside the Java Application. The Business Service sends the outbound API request to the Retry Executor. The Retry Executor owns the retry policy. It sends HTTP request attempt n through the HTTP Client. The HTTP Client then sends the request to the External API. Keeping retry ownership inside the Java Application makes the policy clear and controlled.

2. Classify the API response

The External API returns the HTTP response to the HTTP Client. The application then evaluates the Response status? decision. A 2xx response follows the success path. That success response returns to the Business Service and then to the Caller / Upstream Service. A timeout, 429, or 5xx follows the retryable path. These failures may be temporary. A 400, 401, 403, or validation error follows the non-retryable path. Repeating the same request normally will not fix those problems.

3. Retry temporary failures with a fixed delay

For a retryable failure, the flow goes to Sleep / scheduled delay and waits one second. It then checks Attempts exhausted?. The design allows one initial call plus up to three retries, which means four total attempts. If fewer than three retries have been used, the flow returns to the Retry Executor. The Retry Executor sends another request through the HTTP Client. The loop stops as soon as one attempt succeeds or all three retries have been used.

4. Fail fast for non-retryable errors

For a non-retryable failure, the application skips the retry loop and goes directly to Failure Handling. The diagram shows 400, 401, 403, and validation errors here. A 400 usually means the request is invalid. A 401 means authentication is missing or invalid. A 403 means the caller is not allowed to perform the action. Retrying the same request would add delay without fixing the cause.

5. Handle failure after all retries are used

If the attempts are exhausted, the flow also goes to Failure Handling. I would use structured logging to capture useful failure details. I would record metrics and alerts so repeated failures become visible. The design also includes a circuit breaker for repeated downstream failures. The application then returns a meaningful error or fallback to the Caller / Upstream Service. If recovery should continue later, the design allows an async handoff or dead-letter queue instead of keeping the synchronous request open.

6. Make retries safe with idempotency

I would finish by discussing idempotency. Idempotency means repeating an operation does not create an unwanted extra effect. The diagram treats GET, PUT, and DELETE as safe retry cases. For POST, I would use an idempotency key before retrying. This matters because a timeout does not prove the External API did nothing. The operation may have completed even when its response was lost. The retry policy therefore improves reliability, but retries must remain safe.

Time & Space Complexity

The benefit of this design is that retry behavior is controlled in one place inside the Java Application. The Retry Executor allows one initial call and up to three retries, with a one-second delay. This can recover from temporary timeouts, 429 responses, or 5xx failures. The downside is extra latency and more requests while the External API may already be unhealthy. The circuit breaker helps reduce that pressure when failures continue. Non-retryable 400, 401, 403, and validation errors fail quickly instead of wasting retries. Idempotency is also important because repeated operations can create duplicate effects. For POST operations, the design uses an idempotency key before retrying. We accept the added retry logic because it improves recovery from short-lived failures.

Why Interviewers Ask This

Interviewers ask this question to test whether a candidate can separate temporary failures from failures that should stop immediately. They want correct request and response flow, bounded retries, delay handling, and a clear stop condition. They also look for practical failure handling such as structured logging, metrics, circuit breakers, and fallback behavior. A strong answer also recognizes that retries can repeat side effects, so idempotency matters. The goal is to show reliable engineering judgment, not simply retry every failure.

Interviewer may ask next
What would you do if the External API kept failing for several minutes while many requests were arriving?

I would use the circuit breaker already shown in Failure Handling. The normal request path stays the same: the Business Service uses the Retry Executor and HTTP Client to call the External API. Short temporary problems can still use the existing one-second delay and three-retry limit. However, repeated downstream failures would cause the circuit breaker to stop sending more calls for a period instead of continually stressing the failing dependency. During that time, the Business Service can return the meaningful error or fallback already shown in the design. Structured logging, metrics, and alerting would make the outage visible to operators. If work should continue later, the existing async handoff or dead-letter queue can be used. The benefit is lower pressure on an unhealthy External API and faster failure for callers. The downside is that some requests may receive fallback or error responses without making another direct attempt while the circuit remains open.

How would you make retries safe for a POST operation that could create duplicate data?

I would use the idempotency key shown in the diagram before enabling retries for that POST operation. A timeout does not prove that the External API failed to process the request. It may have completed the operation while the response was lost. Without protection, the Retry Executor could send the same logical operation again and create a duplicate effect. The idempotency key lets repeated attempts represent the same logical operation instead of separate operations. The rest of the design stays unchanged. The Business Service still sends the call through the Retry Executor and HTTP Client. Retryable failures still wait one second and allow up to three retries. Non-retryable failures still go directly to Failure Handling. The benefit is safer retry behavior for operations with side effects. The downside is extra coordination and state around idempotency. That added complexity is worthwhile when repeating a POST could create duplicate records or actions.

26. How would you translate different manufacturer APIs into one unified answer?API DesignHardApple

Question Details

Explain how you would normalize multiple upstream APIs into a single client-facing contract.

Short Interview Answer (30-60 seconds)

At a high level, I would hide each manufacturer’s API differences behind adapters and expose one stable contract to clients. The request enters through the API Gateway, which handles authentication, rate limiting, and versioning. Inside the Java application, I map the request into a canonical model, route it to the required manufacturer adapters, and call each vendor over HTTPS with mTLS. I normalize their responses, merge the results, and return one unified answer. The benefit is a simple client contract. The trade-off is extra translation and operational complexity.

Detailed Explanation

The problem is that different manufacturers may describe the same product information in different ways. One may use different field names, formats, or values from another. I want clients to avoid understanding those differences. They should send one kind of request and receive one predictable answer. The design therefore converts the client request into one common internal shape, translates that shape for each manufacturer, converts every vendor response back into the common shape, and then builds one final response. The diagram shows this request and response flow from clients through the integration layer and back.

Useful Questions to Ask the Interviewer
  • Do we normally call one manufacturer or fan out to several manufacturers for one request?
  • Which fields must always appear in the unified response?
  • How much partial data can we return when one manufacturer fails?
How would you translate different manufacturer APIs into one unified answer? diagram
How to Explain It in an Interview
1. Start with one client-facing contract

I would first give clients one stable API contract. Client Apps send an HTTPS request with a JWT to the API Gateway. The gateway handles authentication, rate limiting, and API versioning in this design. After validation, it forwards the API request to the Unified Contract API inside the Java Application Boundary. The client does not need to understand each manufacturer’s format. This keeps the client-facing interface stable while vendor-specific details stay behind the integration boundary.

2. Convert the request into a canonical model

The Unified Contract API passes the incoming request into the Canonical Model. A canonical model is one common internal representation used across the integration layer. It defines the standard request and response schema. This is important because Manufacturer API A, B, and C may represent similar information differently. The canonical model lets the remaining Java components work with one consistent shape instead of carrying vendor-specific formats through the whole application.

3. Route the request through manufacturer adapters

The Orchestrator / Manufacturer Router receives the canonical request. It selects the required manufacturer adapter or adapters. The diagram also allows fan-out and collection when more than one manufacturer is needed. Manufacturer Adapter A, B, and C each translate between the canonical model and that manufacturer’s vendor format. This isolates manufacturer-specific rules. A vendor-specific mapping change is handled in its adapter instead of changing the client-facing contract.

4. Call each manufacturer securely

Each adapter sends its vendor request to the matching external Manufacturer API over HTTPS with mTLS. mTLS means both sides use certificates to verify the secure connection. Secrets / Certificates stores API keys, OAuth secrets, and mTLS certificates used by the integration. The vendor request and vendor response are separate flows. Manufacturer API A returns to Adapter A, Manufacturer API B returns to Adapter B, and Manufacturer API C returns to Adapter C.

5. Normalize and combine the responses

The returned vendor payloads move into the Response Normalizer. This component performs field mapping, enum mapping, and unit conversion. That turns different vendor formats into comparable canonical response parts. The Aggregator + Error Mapper then receives those canonical response parts. It merges results, normalizes errors, and supports a partial response when that behavior is appropriate. The Unified Response shown in the diagram contains client-facing fields such as sku, availability, price, currency, and eta.

6. Handle fallback, monitoring, and the final response

The Fallback Policy contains retry, timeout, and circuit-breaker behavior. The Cache can supply hot data or a cached fallback response into that policy. This improves resilience when an upstream manufacturer is slow or unavailable, but cached data may be less fresh. Observability receives logs, metrics, and traces. It helps operators understand failures and latency, but it is not part of the business response path. After aggregation, the single unified response returns to the Unified Contract API. The API Gateway then returns the unified answer toward Client Apps. The main trade-off is a simpler client contract in exchange for more translation, mapping, fallback, security, and operational work inside the integration layer.

Practical Complexity & Trade-offs

The main benefit is isolation. Clients depend on one contract instead of every manufacturer’s format. Each adapter contains vendor-specific translation, so manufacturer changes stay behind the integration boundary. The downside is more mapping code and more cases to test. Normalization must handle fields, enums, and units consistently. Fan-out can increase latency because several upstream calls may be involved. Timeouts, retries, and the circuit breaker improve reliability, but retries can create extra load. The cache can provide fallback data, but that data may be older. mTLS and stored credentials improve security, but certificates and secrets must be managed carefully. Rate limiting and versioning at the API Gateway improve control, while observability adds operational visibility.

Why Interviewers Ask This

Interviewers ask this to see whether you can create a clean boundary around inconsistent external APIs. They want correct request and response modeling, a useful canonical model, adapter-based isolation, and clear ownership of security and reliability concerns. They also evaluate whether you understand normalization, partial failures, caching, rate limiting, versioning, mTLS, secret handling, and observability. Most importantly, they want to hear the trade-off between a simple client contract and a more complex integration layer.

Interviewer may ask next
What would you change if one manufacturer becomes slow or unavailable?

I would keep the same client-facing contract and isolate the problem inside that manufacturer path. The affected Manufacturer Adapter would still use the existing HTTPS and mTLS vendor call, while the Fallback Policy would apply the retry, timeout, and circuit-breaker behavior shown in the diagram. A timeout stops one slow upstream call from waiting indefinitely. Limited retries can help with temporary failures. The circuit breaker can stop repeated calls when that upstream service is unhealthy. If a partial answer is allowed, the Aggregator + Error Mapper can still merge successful manufacturer responses and normalize the failed part. The Cache can provide a cached response when the fallback policy uses it. The Unified Contract API and Unified Response shape stay unchanged. Observability continues collecting logs, metrics, and traces so operators can identify the failing manufacturer. The main downside is that a partial or cached answer may be less complete or less fresh than a fully live response.

How would you add a new manufacturer without breaking existing clients?

I would keep the Unified Contract API and Canonical Model stable unless the business truly needs new client-visible information. I would add another manufacturer adapter beside Adapter A, Adapter B, and Adapter C. The Orchestrator / Manufacturer Router would be updated so it can select that adapter when required. The new adapter would translate the canonical request into the new manufacturer’s format and translate the returned vendor data back toward the canonical representation. Its external call would follow the same secure pattern shown in the diagram, using HTTPS with mTLS and credentials managed through Secrets / Certificates. The Response Normalizer would handle the new field, enum, and unit mappings. The Aggregator + Error Mapper would then combine that normalized data with the existing response parts. Existing clients would continue receiving the same Unified Response fields. The downside is additional adapter code, mapping rules, credentials, monitoring, and testing, but vendor-specific changes remain isolated from clients.

27. Your point of view was challenged or had conflicted interests.BehavioralMediumApple

Question Details

Describe the disagreement, how you responded, and what happened next.

Interview tip:

Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a disagreement where another engineer challenged your technical approach, explain how you listened to their concerns, compared the options using shared project goals, worked toward a decision with the team, and supported the final direction.

Situation

In my last role, I was working on a Java service that needed a change to how it handled a business workflow. I preferred adding the new behavior inside the existing service because it already owned the related logic. Another engineer believed we should create a separate service because they wanted clearer isolation. We both had reasonable concerns, but our preferred solutions were different.

Task

I was responsible for helping the team reach a sound technical decision without turning the disagreement into a personal debate. I also needed to make sure we considered maintainability, operational complexity, delivery risk, and the actual needs of the application.

Action

I first asked the other engineer to explain the problems they expected if we kept the logic in the existing service. I listened carefully and agreed that separation could become valuable if the workflow grew independently. I then explained my concern that creating another service immediately would add deployment, monitoring, failure handling, and communication complexity before we had a clear need for that separation. Instead of continuing with opinions, I suggested that we compare both approaches against the same criteria. We reviewed ownership of the data, expected change patterns, transaction boundaries, failure behavior, and how each option would affect support after release. I also separated my preference from the decision itself. I told the team that I was comfortable changing my position if the separate service solved a real problem that the simpler approach could not solve. After the discussion, we agreed to keep the behavior in the existing Java service but organize the new logic behind a clear interface so it could be separated later if the requirements changed. I documented the reasoning and made sure the other engineer had a chance to review the implementation before we moved forward.

Result

The team reached a decision without damaging the working relationship, and we delivered a solution that matched the current needs without adding unnecessary operational complexity. The other engineer's concerns also improved the design because we created a cleaner boundary inside the service. I learned that when my point of view is challenged, the best response is to move the discussion away from who is right and toward shared criteria, evidence, and the needs of the system.

Why Interviewers Ask This

Interviewers ask this question to understand how a candidate handles disagreement when reasonable people have different interests or technical opinions. A strong answer shows that the candidate can listen, explain concerns clearly, evaluate alternatives objectively, protect working relationships, and support a shared decision even when their original preference does not automatically win.

Interviewer may ask next
What would you have done if the team had chosen the other engineer's approach?

I would have supported the decision once the team had considered the tradeoffs and agreed on the direction. I would have helped make the separate service reliable and would have raised new concerns only if I found evidence that materially changed the decision.

What did you learn from having your point of view challenged?

I learned to separate my technical preference from the outcome the team needs. The disagreement became more productive when I listened first and compared both options using shared criteria instead of trying to defend my original idea.

28. Had to prioritize between two different things and how you decided.BehavioralMediumApple

Question Details

Describe the tradeoff, how you made the decision, and the outcome.

Interview tip:

Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a situation where two important development tasks competed for the same time, how you compared their user impact, urgency, technical risk, and dependencies, how you communicated the tradeoff with the team, and what happened after you chose which work to do first.

Situation

In my last role, I was working on a Java service when two important needs came up at the same time. One was finishing a new feature that another team needed for an upcoming release. The other was fixing an intermittent production issue that was causing some requests to fail. Both mattered, but I could not give both the same attention at the same time.

Task

I needed to decide which work to prioritize while protecting production stability and being clear with the teams affected by the decision. I also wanted to avoid making the choice only based on which request sounded more urgent.

Action

I first gathered enough information to compare the two items using the same criteria. For the production issue, I reviewed application logs, recent errors, and the affected request flow. I confirmed that the failures were still occurring and could affect existing users. For the feature, I checked its release dependency and spoke with the team waiting for it. I learned that delaying it would affect their schedule, but they had a short term workaround. Based on that information, I prioritized the production issue because an active reliability problem had a more immediate user impact and could become harder to diagnose if it continued. I explained my reasoning to both teams instead of simply saying that the feature was delayed. I also reduced the impact on the feature work by documenting where I had stopped and identifying the next development steps before switching tasks. I then focused on isolating the failure path, implemented the fix, tested the affected Java service behavior, and worked with the team to verify that the issue was resolved. Once the service was stable, I returned to the feature work using the notes I had prepared.

Result

The production issue was resolved without losing the context needed to continue the feature work. The team waiting for the feature understood the tradeoff because I communicated the reason and impact early. I learned that when two priorities compete, I make a better decision by comparing user impact, urgency, risk, and available alternatives instead of treating every urgent request as equally important.

Why Interviewers Ask This

Interviewers ask this question to understand how a candidate makes decisions when important work competes for limited time. A strong answer shows that the candidate uses clear priorities, considers user and business impact, communicates tradeoffs, manages risk, and takes ownership of the consequences of the decision.

Interviewer may ask next
Why did you prioritize the production issue instead of the feature?

I chose the production issue because it was already affecting existing users, while the team waiting for the feature had a temporary workaround. That made the production problem more urgent and higher risk. I still considered the feature important, so I communicated the delay early and preserved my development context before switching.

What would you do differently in a similar situation now?

I would use the same decision criteria, but I would make the tradeoff visible even earlier by writing down the impact, urgency, dependencies, and alternatives for both items. That would make the decision easier for everyone to understand and would help the team quickly challenge any assumption I had missed.

29. Tell me about a time when you got a process to change.BehavioralMediumApple

Question Details

Describe the situation, the change you pushed for, and the result.

Interview tip:

Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a process that was creating repeated problems, the change you proposed, how you gathered evidence and handled concerns from the team, why you chose that approach, and how the new process improved the way the team worked.

Situation

In my last role, our team had a manual code review and release process for a Java service. Developers often discovered formatting issues, basic test failures, or build problems only after opening a review or preparing a release. This created repeated work and slowed down feedback.

Task

I wanted to improve the process so that common problems were caught earlier, before they reached manual review. I was not responsible for changing the team process by myself, so I needed to show why the change was useful and get agreement from the other developers.

Action

I first looked at the problems that were repeatedly coming up during reviews and releases. I noticed that many of them could be detected automatically by the build. I proposed adding automated checks for code formatting, static analysis, unit tests, and a clean build before code could move forward. I created a small example in our Java build configuration and showed the team how a developer would receive immediate feedback when a check failed. I explained that my goal was not to add another approval step. The goal was to remove avoidable manual work and let reviewers spend more time on design, correctness, and maintainability. Some developers were concerned that stricter checks could slow them down, so I suggested introducing the checks gradually and fixing existing issues before making every rule required. I asked the team for feedback on rules that caused unnecessary noise and adjusted the configuration based on that feedback. After we agreed on the approach, I documented the new process and helped other developers resolve the first few failures so the change was easy to adopt.

Result

The team adopted the automated checks as part of the normal development process. Basic problems were caught earlier, code reviews became more focused, and releases had fewer avoidable build surprises. I learned that changing a process works better when I show the problem clearly, involve the people affected by the change, and make adoption practical instead of simply asking everyone to follow a new rule.

Why Interviewers Ask This

Interviewers ask this question to understand whether a candidate can recognize an inefficient process, influence others without relying only on authority, and turn an improvement idea into a change that people actually use. A strong answer shows practical judgment, clear communication, collaboration, and ownership of the change.

Interviewer may ask next
How did you handle resistance from developers who thought the new checks would slow them down?

I listened to the specific concerns instead of treating the resistance as a problem. I demonstrated that the checks gave developers feedback before review, and I suggested introducing them gradually rather than making every rule mandatory at once. I also adjusted rules that produced unnecessary noise. That made the process feel like a useful improvement rather than an extra barrier.

What would you do differently if you had to make a similar process change again?

I would involve a few developers earlier when creating the first version of the change. In this case, I built the initial example before gathering broader feedback. It worked, but earlier input could have helped me identify noisy rules sooner and made the team feel involved from the beginning.

30. What are your experience with Java and how does it apply to the position you applied for?BehavioralMediumApple

Question Details

Describe your Java experience and explain how it applies to the role.

Interview tip:

Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a previous Java project, the responsibilities you owned, how you designed and implemented reliable backend features, how you worked with the team, and how that experience prepares you for this Java Developer role.

Situation

In my last role, I worked on a Java backend application that supported important business workflows. The application had several services that handled requests, validated data, applied business rules, and stored information in a relational database. I worked mainly with Java, Spring Boot, REST APIs, SQL, and automated tests.

Task

My responsibility was to build and maintain backend features that were reliable, easy to understand, and safe to change. I also needed to investigate defects, review code, and work with other engineers to make sure new changes fit the existing system without creating problems for other services.

Action

I used Java to organize business logic into clear classes and methods with focused responsibilities. When I built API features, I first understood the request flow and data requirements before writing code. I used Spring Boot for the service layer, added input validation, handled expected errors clearly, and used SQL carefully so database operations were correct and efficient. I wrote unit tests for important business logic and integration tests when behavior depended on the database or another application component. When I found performance or reliability issues, I traced the request through the application, checked logs and database behavior, and fixed the cause instead of only treating the visible symptom. I also participated in code reviews and explained the reasoning behind my changes so the team could identify risks early. These habits helped me write Java code that was easier for other engineers to maintain. They also apply directly to this position because the role requires strong Java fundamentals, dependable software design, debugging, testing, and collaboration with other engineers.

Result

The features I worked on became easier to maintain and changes could be made with more confidence because the code had clearer responsibilities and better test coverage. I also became stronger at connecting Java language knowledge with practical software engineering decisions. That experience prepared me to contribute to this Java Developer position by building reliable code, understanding existing systems, and working closely with the team while continuing to learn.

Why Interviewers Ask This

Interviewers ask this question to understand both the depth of the candidate's Java experience and whether that experience matches the responsibilities of the position. A strong answer shows practical Java knowledge, sound engineering judgment, ownership of real development work, and a clear connection between previous experience and the role being considered.

Interviewer may ask next
Which part of your Java experience would help you most in this role?

My strongest contribution would be building and maintaining reliable backend code. I have experience taking a requirement, understanding how it fits into an existing Java application, implementing the business logic, validating data, testing the behavior, and reviewing the result for maintainability. That complete development process would help me contribute effectively in this role.

What did you learn from working on that Java application?

I learned that strong Java knowledge is only one part of building good software. I also need to understand the full request flow, design code that other engineers can maintain, test important behavior, investigate the real cause of problems, and communicate design decisions clearly. I would continue applying those lessons in a similar Java role.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.

Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.