Interview guides · American Express
American Express Software Engineer Interview Guide
American Express Software Engineer interview guide: five reported rounds, Java and Spring Boot, DSA, SQL, Kafka, behavioral prep and a 7-day plan.
The 60-second brief
American Express runs card payments, digital banking, merchant services and risk analytics, and candidates also mention lifestyle products such as Resy and Tock. The Software Engineer role is described as backend-heavy enterprise work: microservices and REST APIs, cloud deployments, batch processing, event streaming with Kafka, and refactoring older systems into smaller services. The stack named for the role includes Java with Spring Boot, Python, Go, and JavaScript or React for front-end work, with relational stores (Postgres, MySQL, DB2), NoSQL stores (Redis, Couchbase, MongoDB), Docker, Kubernetes, and Jenkins or GitHub Actions for delivery.
This guide is for candidates interviewing for a Software Engineer position at American Express, especially backend and full-stack tracks that use Java and Spring Boot. It covers the five reported stages, the reported coding, core Java, database, messaging and design questions, how to defend the projects on your resume, behavioral stories for the manager conversation, and a 7-day schedule that sequences all of it.
Candidates report five stages over roughly 4-6 weeks: an online application, a recruiter screen, an online coding challenge, two to three technical rounds, and a final managerial round.
Focus areas
- Data Structures & Algorithms (DSA)
- Problem Solving
- Coding Challenges (Algorithmic)
Overview
The reported questions fall into four families. The first is data structures and algorithms: 3Sum, longest palindromic substring, merge sort with its complexity, cycle detection and reversal in a linked list, shortest path on a grid, and sliding window problems. Candidates describe the difficulty as Easy to Medium, centred on arrays, strings, hash maps, linked lists, trees and BFS/DFS, with readable code mattering more than dynamic programming tricks. Dynamic programming, tries and heaps are listed as less common.
The second family is core Java and Spring Boot: abstract class versus interface, garbage collection and memory leaks, Java 8 streams and lambdas, thread safety and synchronization, dependency injection and bean lifecycles, and Spring Boot Actuator. The third is databases and design: ACID versus eventual consistency, a scalable REST microservice, a SQL query with joins, GROUP BY and window functions, Kafka consumer groups and partitions, and caching strategies. Reported scenarios include a notification service for payment alerts and tuning a slow query over large transaction tables. Prepare these as explanations you can give aloud with a concrete example, not as definitions.
The fourth family is your own experience. Candidates report detailed questioning on resume projects: why a particular database or framework was chosen, how the service was monitored and deployed, and how a performance problem was resolved. The last stage is a managerial conversation on behavioral questions and cultural fit. Anything on your resume, such as Redis, Kafka or Kubernetes, can be asked about at the level of internals, so list your projects early and only keep technologies you can explain.
The American Express interview process
These are the five stages candidates describe, in the order they report them; the exact mix of coding challenge, digital interview and technical rounds varies by hiring route.
Online Application
Candidates describe this as submitting an application online, which starts the process. There is nothing to perform here, but the resume you submit is what later stages are reported to dig into, so treat it as preparation material. Candidates report detailed questions on every language, tool and project listed, including why a database or framework was chosen.
What to demonstrate
- Submitting the application online
- The resume content that later project questions are reported to draw on
How to prepare
- List each project on your resume with its stack, your own contribution, one design decision and one production problem.
- Remove or be ready to defend every technology you list, for example Redis, Kafka, Docker or Kubernetes, down to how it works internally.
Recruiter Screen
Candidates report an initial screening, either by a recruiter or through a digital assessment, covering basic competencies. Approach it as a conversation about your background and fit with the stack the role lists: Java with Spring Boot, Python, Go or React, relational and NoSQL databases.
What to demonstrate
- Basic competencies, screened by a recruiter or a digital assessment
- Fit between your background and the role's listed languages and frameworks
How to prepare
- Prepare a short, concrete summary of your strongest backend project and the languages you used on it.
- Ask the recruiter what the next stages are and whether the coding step is a timed online challenge or a digital interview.
Online Coding Challenge
Candidates report either an online coding challenge or a HireVue digital interview that mixes behavioral questions with coding tasks. The coding questions practised for this kind of stage are the array, string, graph and window problems from the reported list, such as shortest path on a grid and the sliding window on a contiguous subarray. You work without a live conversation partner, so your solution must be correct and handle edge cases on its own.
What to demonstrate
- Working solutions to coding tasks without live help
- Short behavioral answers when the digital interview format is used
How to prepare
- Solve grid shortest path with BFS and the sliding window problem on a timer, then test with empty, single-element and all-equal inputs.
- Prepare a one-minute STAR answer for the bug or outage question in case behavioral prompts appear here.
Technical Rounds
Candidates report two to three technical interviews that combine live coding with theoretical questions. The reported material includes algorithm problems (3Sum, longest palindromic substring, merge sort, linked-list cycles and reversal), core Java and Spring Boot concepts, SQL, and design topics such as Kafka, caching and REST microservices. Candidates also report debugging your own code live, tracing it and checking boundary conditions when a case fails, so practise narrating that process.
What to demonstrate
- Live coding that works and is cleanly structured
- Java, Spring Boot and database fundamentals explained correctly
- Design reasoning for microservices, messaging and caching
How to prepare
- Rehearse each reported coding problem aloud: brute force, optimisation, complexity, then edge cases before you write code.
- Prepare crisp explanations for abstract class versus interface, garbage collection, thread safety, bean lifecycles, ACID versus eventual consistency and Kafka partitions.
- Write a window-function SQL query over a transactions table and explain the index it would use.
Managerial Round
Candidates report a final interview with a manager that centres on behavioral questions and cultural fit. The reported behavioral prompts cover a production bug or outage, disagreements with teammates or architects, a tight deadline with quality, and changing scope mid-sprint. Candidates also mention American Express Leadership Behaviors in connection with behavioral rounds. Answer with STAR stories in which you made the decisions.
What to demonstrate
- Behavioral answers about collaboration, deadlines, conflict and scope change
- Cultural fit, as the round is described by candidates
How to prepare
- Write four STAR stories, one per reported behavioral prompt, each with a measured result and a lesson.
- Rehearse the resume-based questions: architecture of the main service you supported, why you picked a database, and a performance degradation you resolved.
Where candidates lose points
Writing 3Sum that returns duplicate triplets or runs in cubic time
Sort first, fix one element, use two pointers on the rest, and skip equal values for all three positions. State the O(n^2) time and test with all zeros, no solution and negative-only arrays.
Defining Java terms without internals, for example saying 'use synchronized' for thread safety or 'the GC cleans up memory' for leaks
Practise explaining what is shared, what can race, and the alternatives (locks, atomics, concurrent collections). For leaks, name how a live reference such as a static map or listener keeps memory reachable, and how heap dumps help find it.
Listing Kafka, Redis or Couchbase on the resume without being able to explain partitions, offsets, eviction or consistency
For each listed technology, prepare how it works, why you chose it and one failure mode. Remove anything you cannot defend.
Aggregating after a one-to-many join in SQL so sums are inflated, or ignoring which index the query uses
State the row grain before joining, aggregate in a subquery or CTE when needed, and explain the window function's partition and order. Say how you would read the query plan for a slow query.
Behavioral answers that say 'we' throughout, with no decision of yours and no result
Write each STAR story with your own action, a measured result and a lesson. Rehearse them aloud so the situation takes two sentences.
Questions & practice
Given an array of integers, write an algorithm to identify all unique triplets that sum to zero (3Sum problem)
Medium · Algorithmic Problem Solving & Data Struc
Given an array of integers, write an algorithm to identify all unique triplets that sum to zero (3Sum problem).
Approach
- Sort the array first (O(n log n)), then fix each index i and run two pointers lo = i+1 and hi = n-1 looking for nums[lo] + nums[hi] == -nums[i]. Total O(n^2) time against O(n^3) for the triple loop, with O(1) extra space beyond the output and the sort.
- Get uniqueness from the sort instead of a set of tuples: skip i when nums[i] == nums[i-1], and after recording a hit advance lo and retreat hi past equal values before continuing.
- Break out early once nums[i] > 0, because every remaining value is positive and no triplet can reach zero.
- Edge cases to test aloud: fewer than three elements, all zeros ([0,0,0,0] gives exactly one triplet), no solution, and large values where the Java int sum can overflow (add into a long).
Follow-up
- How would you solve 3Sum Closest, returning the sum nearest to a target?
- Generalise to kSum: what does the recursion look like, and why is the cost O(n^(k-1))?
- What changes if you may not reorder the input array?
Implement a function to find the longest palindromic substring within a given string.
Medium · Algorithmic Problem Solving & Data Struc
Approach
- State the brute force: check all O(n^2) substrings, each in O(n), for O(n^3) total. Then improve on it.
- Expand around centres: there are 2n-1 centres (each character for odd lengths and each gap for even lengths). Expand while s[l] == s[r] and track the best start and length. That gives O(n^2) time and O(1) space, and it is the version to code.
- Alternative: a DP table where dp[i][j] = s[i] == s[j] && (j - i < 2 || dp[i+1][j-1]), filled by increasing length. It is also O(n^2) time but uses O(n^2) memory, so explain why you prefer expansion.
- Mention Manacher's algorithm as the O(n) option without coding it unless asked.
- Edge cases: empty string, a single character, even-length answers like "abba", and all-identical strings, which are the worst case for expansion. In Java, substring(start, start + len) excludes the end index.
Follow-up
- How would you count all palindromic substrings instead of returning the longest?
- How does the longest palindromic subsequence differ, and what is its DP?
- What if the check must ignore case and non-alphanumeric characters?
Write an efficient implementation of Merge Sort and analyze its time and space complexity.
Medium · Algorithmic Problem Solving & Data Struc
Approach
- Top-down: split at mid = lo + (hi - lo) / 2 (this avoids int overflow), sort each half recursively, then merge with two pointers into a buffer and copy back.
- Allocate the auxiliary array once and pass it down rather than allocating in every merge. Time is O(n log n) in the best, average and worst case (log n levels, O(n) merge work per level). Space is O(n) auxiliary plus O(log n) recursion stack.
- Keep it stable by taking from the left half on ties (<=). Stability is what lets a sort by amount preserve an earlier sort by date.
- Practical optimisations: use insertion sort for small subarrays, and skip the merge when a[mid] <= a[mid+1], which makes already-sorted input O(n). The bottom-up iterative version avoids recursion entirely.
- Contrast with quicksort, which sorts in place but is O(n^2) in the worst case and is not stable. Java's Arrays.sort uses dual-pivot quicksort for primitives and TimSort, a merge-sort hybrid, for objects.
Follow-up
- How do you merge sort a singly linked list, and why does it need no auxiliary array?
- How would you sort a file too large for memory (sorted runs plus a k-way merge with a min-heap)?
- How can the merge step count inversions in an array?
How do you detect and handle cycles in a linked list? Write code to reverse a singly linked list.
Medium · Algorithmic Problem Solving & Data Struc
Approach
- Cycle detection with Floyd's algorithm: slow moves one step and fast moves two. If they meet there is a cycle. Loop while fast != null && fast.next != null. That is O(n) time and O(1) space, against O(n) space for a HashSet of visited nodes.
- Find where the cycle starts: after the meeting, reset one pointer to head and move both one step at a time; they meet at the entry node. To handle the cycle, walk to the node whose next is the entry and set its next to null. Get the cycle length by counting steps around the loop from the meeting point.
- Iterative reversal: prev = null, curr = head; on each step save next = curr.next, set curr.next = prev, then prev = curr and curr = next; return prev. O(n) time, O(1) space.
- The recursive reversal is O(n) stack and can throw StackOverflowError on long lists in Java. Reversing a list that has a cycle never terminates, so detect the cycle first.
- Test empty, single-node and two-node lists, plus a cycle that loops back to the head itself.
Follow-up
- Reverse only the nodes between positions m and n, or reverse in groups of k.
- Check whether a linked list is a palindrome in O(1) extra space.
- Prove why resetting one pointer to head finds the cycle entry.
Given a matrix or grid, find the shortest path from a source cell to a destination cell using graph traversal
Medium · Algorithmic Problem Solving & Data Struc
Given a matrix or grid, find the shortest path from a source cell to a destination cell using graph traversal algorithms.
Approach
- Treat each open cell as a node with 4-directional neighbours (8 if diagonals are allowed). On an unweighted grid, BFS from the source gives the shortest path: the first time the destination is reached, its distance is minimal. Time and space are O(rows x cols).
- Mark a cell visited when you enqueue it, not when you dequeue it. Otherwise the same cell enters the queue many times. Store distance in a dist array, or process the queue level by level.
- To return the path itself, record each cell's parent and walk back from the destination.
- Weighted cells need Dijkstra with a priority queue (O(E log V)). Use 0-1 BFS with a deque when weights are only 0 or 1, and A* with a Manhattan-distance heuristic for unit-cost 4-way moves. DFS does not find shortest paths.
- Edge cases: source or destination blocked, source equal to destination (distance 0), an unreachable target (return -1), and bounds checks on every neighbour.
Follow-up
- What if you may remove up to k obstacles? (BFS over states (row, col, removalsLeft).)
- How do you find each cell's distance to the nearest of several sources (multi-source BFS)?
- When does bidirectional BFS help on a large grid?
Explain how you would implement a sliding window approach to solve a contiguous subarray optimization problem.
Medium · Algorithmic Problem Solving & Data Struc
Approach
- Recognise the pattern: a contiguous subarray or substring, plus a condition that changes monotonically as the window grows or shrinks. Keep left and right pointers. Each index enters and leaves the window at most once, so the total is O(n) against O(n^2) for checking every subarray.
- Fixed-size window of k: add a[right], subtract a[right-k], and update the best. One example is the maximum total over k consecutive transactions.
- Variable-size window: extend right, and while the window breaks the constraint, shrink from left; then update the answer. Examples are the longest substring without repeating characters (hash map of counts or last index) and the shortest subarray with sum >= target when all values are non-negative.
- Negative values break the shrink logic. Switch to prefix sums with a hash map (count of subarrays summing to k) or Kadane's algorithm (maximum subarray sum).
- For a maximum inside a moving window, keep a monotonic deque of indices so each step is amortised O(1). For a time-based window over transactions, evict from the front by timestamp instead of by count.
Follow-up
- Track the maximum transaction amount over the last T seconds of a stream: what structure, and what complexity?
- Why does the shrink step fail with negative numbers, and what replaces it?
- How would you handle a window over data arriving out of timestamp order?
How does garbage collection work in Java? What are memory leaks, and how do you diagnose them?
Medium · Core Java, OOPs & Frameworks
Approach
- The collector traces reachability from GC roots (thread stack locals, static fields, JNI references). Anything unreachable is eligible for collection, and reference cycles are collected because Java does not use reference counting.
- Generational heap: new objects go to Eden, minor GCs copy survivors between survivor spaces, and long-lived objects are promoted to the old generation. G1 has been the default collector since Java 9; ZGC and Shenandoah target very short pauses, and Parallel GC targets throughput.
- A Java memory leak is an object that is still reachable but no longer needed. Common causes are static maps that only grow, caches with no eviction, listeners that are never unregistered, ThreadLocals left set on pooled threads, and unclosed resources.
- Diagnosis: watch old-gen usage after full GCs trend upward (GC logs via -Xlog:gc*, JFR, heap metrics). Capture a heap dump with jcmd <pid> GC.heap_dump or -XX:+HeapDumpOnOutOfMemoryError, then open it in Eclipse MAT and follow the dominator tree and the path to GC roots for the largest retained sizes.
- Fixes: bounded caches with size or time eviction, ThreadLocal.remove() in a finally block, try-with-resources, and WeakHashMap only where key-based weak references fit.
Follow-up
- How does OutOfMemoryError: Java heap space differ from Metaspace or GC overhead limit exceeded?
- Explain strong, soft, weak and phantom references.
- Old-gen usage climbs after every full GC in production. What do you capture first?
Explain thread safety, race conditions, and synchronization in multi-threaded Java applications.
Medium · Core Java, OOPs & Frameworks
Approach
- A race condition is any result that depends on thread interleaving. count++ is a read-modify-write and not atomic, and check-then-act (if (!map.containsKey(k)) map.put(k, v)) breaks the same way.
- synchronized gives mutual exclusion plus a happens-before edge for visibility. volatile gives visibility and ordering but does not make compound actions atomic. AtomicLong or LongAdder use CAS for counters. ReentrantLock adds tryLock, timeouts and fairness. ConcurrentHashMap's compute, merge and putIfAbsent are atomic per key.
- Better still, design out shared mutable state: immutable objects with final fields, thread confinement, and stateless services. Spring singleton beans serve every request thread, so they must not keep per-request data in fields.
- Deadlock needs mutual exclusion, hold-and-wait, no preemption and circular wait. Prevent it with a global lock order or tryLock with a timeout. Diagnose it with jstack or jcmd Thread.print, which report deadlocked threads.
- Run work through an ExecutorService with a bounded pool and queue instead of creating raw threads, and shut it down cleanly.
Follow-up
- When is volatile enough and when do you need synchronized?
- Make a lazily initialised singleton thread-safe: double-checked locking with volatile, or the holder idiom.
- ConcurrentHashMap or Collections.synchronizedMap: what is the difference under contention?
Write a SQL query utilizing joins, group by, and window functions to aggregate transaction records across mult
Medium · Databases, System Design & Architecture
Write a SQL query utilizing joins, group by, and window functions to aggregate transaction records across multiple tables.
Approach
- Fix the grain before writing. With transactions(txn_id, account_id, amount, txn_ts, status) and accounts(account_id, customer_id), first aggregate inside a CTE: SELECT a.customer_id, DATE_TRUNC('month', t.txn_ts) AS mon, SUM(t.amount) AS total FROM transactions t JOIN accounts a ON a.account_id = t.account_id WHERE t.status = 'SETTLED' GROUP BY 1, 2.
- Window over the aggregate: SUM(total) OVER (PARTITION BY customer_id ORDER BY mon) for a running total, total - LAG(total) OVER (PARTITION BY customer_id ORDER BY mon) for month-over-month change, and RANK() OVER (PARTITION BY mon ORDER BY total DESC) for ranking. Windows evaluate after GROUP BY, so filter on rank in an outer query, never in WHERE.
- Guard against fan-out: joining two one-to-many tables (transactions and refunds) to the same account before aggregating multiplies the sums, so aggregate each side first. Use LEFT JOIN plus COALESCE(SUM(...), 0) to keep customers with no activity, and put conditions on the right-hand table in ON, not WHERE.
- Know the window details: ROW_NUMBER, RANK and DENSE_RANK differ on ties. With ORDER BY, the default frame is RANGE UNBOUNDED PRECEDING, which lumps tied rows together; write ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW for a strict running sum.
- Performance: index transactions(account_id, txn_ts), filter dates as a range (txn_ts >= :start AND txn_ts < :end) rather than wrapping the column in a function, and read EXPLAIN ANALYZE output.
Follow-up
- Return the top three customers by spend for each month.
- Compute a 7-day rolling sum per account, including days with no transactions.
- The join across two high-volume transaction tables is slow. Walk through how you would speed it up.
Keep soft-deleted accounts from blocking re-registration
Easy · soft delete · unique constraints · engine differences
app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.
Approach
- Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
- Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
- For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
- State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.
Follow-up
- A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
- How do you honour an erasure request while resource_revision.actor_user_id still references this table?
Explain the structural differences between an abstract class and an interface in Java, and describe when you w
Medium · Core Java, OOPs & Frameworks
Explain the structural differences between an abstract class and an interface in Java, and describe when you would use each.
Approach
- Abstract class: can hold instance fields and constructors, any access modifier, and a mix of abstract and concrete methods. A class can extend only one.
- Interface: fields are implicitly public static final. Methods are implicitly public abstract unless declared default or static (Java 8) or private (Java 9). An interface has no constructors or instance state, and a class can implement many interfaces.
- Use an interface for a capability or contract shared by unrelated types, for example a PaymentGateway with several implementations that Spring injects and tests mock. Use an abstract class when closely related subclasses share state and a template-method skeleton.
- Diamond rule: if two interfaces supply the same default method, the implementing class must override it and can delegate with InterfaceName.super.method().
- Related points: a functional interface has exactly one abstract method, and a sealed interface (Java 17) restricts which classes may implement it.
Follow-up
- Can an abstract class have no abstract methods, and can you instantiate it?
- Why can an interface not have a constructor or instance fields?
- Since Java 8 added default methods, when would you still choose an abstract class?
What are Java 8 Streams, lambdas, and functional interfaces? Provide a practical example of stream processing.
Medium · Core Java, OOPs & Frameworks
Approach
- A functional interface has exactly one abstract method (default and static methods are allowed); @FunctionalInterface makes the compiler enforce that. The core types in java.util.function are Function<T,R>, Predicate<T>, Supplier<T>, Consumer<T> and BiFunction. A lambda or method reference such as Txn::getAmount implements one, and any local variable it captures must be effectively final.
- A stream pipeline is a source, then lazy intermediate operations (filter, map, flatMap, sorted, distinct), then one terminal operation (collect, reduce, count, forEach) that triggers execution. A stream cannot be reused after its terminal operation.
- Worked example: txns.stream().filter(t -> t.status() == APPROVED).collect(groupingBy(Txn::merchantId, summingLong(Txn::amountCents))) totals spend per merchant. Keep money as long cents or BigDecimal, never double.
- Pitfalls: mutating shared state inside forEach or map; Collectors.toMap throws IllegalStateException on duplicate keys unless you pass a merge function; parallelStream runs on the common ForkJoinPool and pays off only for large CPU-bound work.
- Optional: return it instead of null, and use orElse, orElseThrow or map rather than an unchecked get().
Follow-up
- What is the difference between map and flatMap? Give an example.
- How would you show that intermediate operations are lazy?
- When would a parallel stream make a service slower?
What is Dependency Injection (IoC) in Spring Boot, and how does the Spring container manage bean lifecycles?
Medium · Core Java, OOPs & Frameworks
Approach
- IoC means the container builds and wires objects and a class only declares what it needs. Prefer constructor injection: fields can be final, a missing bean fails at startup, and tests can pass mocks without Spring. Since Spring 4.3 a single constructor needs no @Autowired.
- Beans come from component scanning (@Component, @Service, @Repository, @Controller), from @Bean methods in @Configuration classes, and from Spring Boot auto-configuration guarded by conditions such as @ConditionalOnMissingBean.
- Lifecycle: instantiate, inject dependencies, Aware callbacks, BeanPostProcessor before-initialisation, @PostConstruct / afterPropertiesSet / init-method, BeanPostProcessor after-initialisation (where AOP proxies for @Transactional are created), use, then @PreDestroy / destroy at shutdown.
- Scopes: singleton (the default, one per container), prototype (a new instance per lookup, and the container does not call its destroy callbacks), and request and session in web apps. To inject a shorter-lived bean into a singleton, use ObjectProvider or a scoped proxy.
- Ambiguity and cycles: resolve several beans of one type with @Primary or @Qualifier. Constructor-injection cycles fail at startup, and Spring Boot 2.6+ rejects circular references by default.
Follow-up
- What do @Component, @Service and @Repository change? (@Repository adds persistence exception translation.)
- Why does @Transactional not apply when a bean calls its own method?
- What happens when a prototype bean is injected into a singleton?
What are Spring Boot Actuators, and how are they utilized for service health monitoring in production?
Medium · Core Java, OOPs & Frameworks
Approach
- Add spring-boot-starter-actuator to get endpoints under /actuator: health, info, metrics, loggers (change log levels at runtime), env, beans, mappings, threaddump, heapdump, and prometheus once micrometer-registry-prometheus is on the classpath.
- Recent Spring Boot versions expose only health over HTTP by default. Open more with management.endpoints.web.exposure.include, put sensitive endpoints (env, heapdump) behind Spring Security, or move them to a separate management.server.port.
- The health endpoint aggregates HealthIndicators (database, Redis, disk space). Write a custom indicator by implementing HealthIndicator and returning Health.up() or Health.down().withDetail(...); control how much detail is shown with management.endpoint.health.show-details.
- On Kubernetes, map probes to /actuator/health/liveness and /actuator/health/readiness. Keep downstream checks out of liveness, or a database outage restarts every pod; put a dependency in readiness only if the instance truly cannot serve without it.
- Metrics through Micrometer: http.server.requests timers (latency percentiles, error counts by status), JVM memory and GC, and custom counters, scraped into Grafana or Datadog dashboards and alerts.
Follow-up
- Write a health indicator for a downstream payment API. What should DOWN mean?
- How would you add a custom endpoint with @Endpoint and @ReadOperation?
- Which actuator endpoints would you never expose publicly, and why?
Explain ACID properties in relational databases (RDBMS) and how they differ from eventual consistency in NoSQL
Medium · Databases, System Design & Architecture
Explain ACID properties in relational databases (RDBMS) and how they differ from eventual consistency in NoSQL stores like Couchbase or Redis.
Approach
- Atomicity: all or nothing, through the write-ahead log or undo. Consistency: constraints, foreign keys and checks hold after every commit. Isolation: concurrent transactions behave as the isolation level promises (read committed, repeatable read, serializable). Durability: a committed change survives a crash because the log is flushed at commit.
- Tie it to an example: a transfer debits one account and credits another in a single transaction. Prevent lost updates with SELECT ... FOR UPDATE or an optimistic version column, and name the anomalies each isolation level allows (dirty read, non-repeatable read, phantom).
- Eventual consistency: replicas converge once writes stop, but a read can be stale in the meantime. It trades read freshness for availability and latency, which is the CAP/PACELC argument.
- Be precise about the stores named. Redis runs each command atomically, and MULTI/EXEC executes a queued batch without rollback; replication is asynchronous, so a failover can lose acknowledged writes. Couchbase makes single-document operations atomic and has supported multi-document ACID transactions since 6.5, while cross-cluster replication is eventually consistent.
- Placement: balances and ledgers belong in the relational store. Sessions, caches, counters and read-heavy profiles fit key-value stores. Use an outbox or idempotency keys when one flow writes to both.
Follow-up
- Which isolation level would you use for a balance update, and what anomaly are you guarding against?
- How do you keep a Redis cache from serving a stale balance after a Postgres update?
- What is a saga, and when would you use one instead of a distributed transaction?
How do you design and structure a scalable RESTful API microservice to handle high concurrent traffic?
Medium · Databases, System Design & Architecture
Approach
- Pin down the numbers first: read/write ratio, peak requests per second, latency target, and which operations must be strongly consistent. Then design resources as nouns with correct verbs and status codes, cursor pagination, and a version in the path or a header.
- Keep instances stateless behind a load balancer so they scale horizontally. Validate tokens at the gateway or in a filter, keep no session state in memory, and use a pooled DB connection (HikariCP is the Spring Boot default) sized against the database's limit.
- Data path: add indexes for the hot queries, send reads to replicas where staleness is acceptable, put a cache-aside Redis layer in front of hot reads, and push slow side effects (notifications, analytics) onto Kafka instead of doing them in the request.
- Resilience: set a timeout on every outbound call, retry with exponential backoff and jitter only for idempotent operations, and add a circuit breaker and bulkhead (Resilience4j). Rate-limit with a token bucket that returns 429, and require an idempotency key on POSTs that move money.
- Operations: return a consistent error body through @ControllerAdvice, add health probes, track p99 latency and error-rate metrics, log with a correlation ID, and use distributed tracing across services.
Follow-up
- How do you make a payment POST safe to retry?
- A downstream dependency's latency jumps to several seconds. What happens to your thread pool, and how do you contain it?
- How would you change a response field without breaking existing clients?
Explain event-driven architecture using Apache Kafka. How do consumer groups, partitions, and topics operate?
Medium · Databases, System Design & Architecture
Approach
- A topic is a named log split into partitions. Each partition is an ordered, append-only sequence with offsets, replicated across brokers (a leader plus followers, with an in-sync replica set). Consuming does not delete messages; retention is by time or size, or by log compaction.
- Producers: the record key is hashed to choose a partition, so events with the same key (an account ID, say) stay in order. Ordering holds only within a partition, never across the whole topic. Use acks=all with min.insync.replicas for durability; enable.idempotence prevents duplicates from retries and is on by default since Kafka 3.0.
- Consumer groups: each partition goes to exactly one consumer in a group, so parallelism is capped at the partition count and extra consumers sit idle. Separate groups each receive every message, which gives you fan-out to independent services. A rebalance happens when members join or leave.
- Offsets are committed to __consumer_offsets. Committing after processing gives at-least-once delivery, so handlers must be idempotent (dedupe on event ID); committing before processing gives at-most-once. Transactions give exactly-once only for Kafka-to-Kafka pipelines.
- Design concerns: use a transactional outbox to avoid the DB-plus-Kafka dual write, send poison messages to retry and dead-letter topics, and manage schema evolution through a schema registry (Avro or Protobuf).
Follow-up
- Does Kafka keep order across partitions? How do you guarantee per-account ordering?
- What happens to key-to-partition mapping when you add partitions to a live topic?
- Consumer lag keeps growing. What do you check, and what can you change?
How do you approach caching strategies in distributed systems to mitigate database latency?
Medium · Databases, System Design & Architecture
Approach
- Decide what is worth caching: read-heavy data that can tolerate some staleness, measured by hit ratio and by DB load saved. Do not cache values that must be exact at decision time, such as an available balance used to authorise a payment.
- Patterns: cache-aside (on a miss, load from the DB and set with a TTL; on a write, update the DB and then delete the key), read-through, write-through (synchronous write to cache and DB, so the cache stays warm but writes are slower), and write-behind (asynchronous DB write, which risks data loss).
- Consistency: deleting after a write can still race with a concurrent miss that re-caches the old value. Narrow that window with short TTLs, versioned keys, or invalidation driven by change events on Kafka.
- Failure modes: a stampede on a hot key's expiry (fix with single-flight or a lock and early refresh), penetration by keys that do not exist (cache the empty result briefly or use a Bloom filter), and mass expiry (add jitter to TTLs). If Redis is down, fall back to the DB behind a circuit breaker and rate limit.
- Layers and limits: a small in-process cache (Caffeine) in front of Redis, an eviction policy (Redis allkeys-lru or allkeys-lfu), memory sizing, and sharding through Redis Cluster's 16384 hash slots.
Follow-up
- How do you keep the cache consistent with the database on updates?
- One key takes most of the traffic. How do you stop it overloading a single Redis node?
- Redis becomes unavailable for ten minutes. What does your service do?
Every query on one table stalls for forty seconds mid-deploy
Medium · lock contention · ddl · migrations
During a release on PostgreSQL, every query touching resource times out for about 40 seconds and then recovers with no intervention. The release ran one migration, ALTER TABLE resource ADD COLUMN archived_reason TEXT, and the migration log shows it completing in 6 ms. Unrelated tables showed no change in error rate. Explain how a 6 ms statement caused a 40-second stall, give the ordered checks you would run on a live system to confirm it, and give the migration procedure that prevents a repeat.
Approach
- Separate the statement's duration from the lock's duration. ADD COLUMN with no default is a catalogue-only change and genuinely runs in milliseconds, but it requires ACCESS EXCLUSIVE, and it cannot acquire that until every transaction already touching the table has finished.
- Account for the queueing, which is the part that surprises people. A lock request that is waiting blocks later requests for conflicting modes behind it rather than letting them overtake, so one long-open transaction holds the DDL and the DDL holds all the traffic. The stall length is set by the longest open transaction, not by the size of the change.
- Confirm on a live system in this order: pg_stat_activity for that table ordered by xact_start, looking for the oldest transaction and specifically for state = idle in transaction; then pg_locks where granted = false to find the waiter; then join them on pid to name blocker and blocked. pg_blocking_pids() does that join for you and is the fastest single call.
- Prevent rather than merely time it better. Set lock_timeout to a second or two on the migration session so the DDL abandons the queue after a bounded wait and is retried, instead of holding it for as long as the oldest transaction lives. Be exact about what that buys: queries arriving during the wait still queue behind the pending ACCESS EXCLUSIVE request, so each attempt costs them up to one lock_timeout of added latency. The outage goes from 40 seconds to about one second per attempt, not to zero. Also run migrations away from deploy-time peaks, and put a statement timeout and an idle-in-transaction timeout on the analytics role that opens the long transactions.
Follow-up
- The same release also wants NOT NULL on that column. What is the sequence that gets there without a long lock?
- Your lock_timeout retry fails ten times in a row because the analytics transaction is always open. What do you change?
Suggested practice checklist
The plan follows the reported loop: resume and loop mapping first, then algorithms, core Java and Spring Boot, databases and design, behavioral stories, and a final mixed rehearsal. Each day produces something you can check.
Day 1 — Map the loop and inventory your resume
- Write the five reported stages and note where each reported question family could be practised.
- For every project on your resume, write the stack, your contribution, one design decision and one production problem.
- Mark each technology you cannot explain internally and schedule it into the days ahead.
Deliverable: A one-page loop map and a project inventory with weak technologies marked.
Day 2 — Arrays, strings and sliding window
- Solve 3Sum with sorting and two pointers, including duplicate skipping, and state its complexity.
- Solve longest palindromic substring by expanding around centers, then compare it with the dynamic programming version.
- Write the sliding window pattern for a contiguous subarray optimisation and for maximum transaction amounts within a moving time frame, using a deque.
Deliverable: Three working solutions with complexity notes and an edge-case list for each.
Day 3 — Linked lists, merge sort and grid graphs
- Write cycle detection with fast and slow pointers and reverse a singly linked list iteratively.
- Implement merge sort and explain its time and space complexity and why it is stable.
- Solve shortest path on a grid with BFS, handling blocked cells, unreachable targets and a start equal to the target.
Deliverable: Three solutions tested on empty, single-node and unreachable inputs.
Day 4 — Core Java and Spring Boot
- Write short spoken answers on abstract class versus interface, and on garbage collection and finding memory leaks.
- Write a stream pipeline example and a race condition fix using synchronized, atomics or a concurrent collection.
- Explain dependency injection, bean lifecycles and what Spring Boot Actuator exposes for health monitoring.
Deliverable: Six recorded or written explanations, each with a concrete code example.
Day 5 — Databases, messaging and system design
- Write a SQL query joining several transaction tables with GROUP BY and a window function, and explain its index use.
- Compare ACID with eventual consistency and design a scalable REST microservice, covering statelessness, caching and rate limits.
- Explain Kafka topics, partitions, consumer groups and ordering, and outline a cache-aside strategy with eviction.
Deliverable: One SQL query with notes, one service sketch and one-page notes on Kafka and caching.
Day 6 — Behavioral stories and resume defence
- Write four STAR stories: a production bug, a technical disagreement, a deadline with quality, and a scope change.
- Prepare answers on your main service's architecture and why you chose its database.
Deliverable: Four STAR stories with measured results and a written project walkthrough.
Day 7 — Full rehearsal
- Do a timed mock: one coding problem, one Java question, one SQL or design question.
- Practise explaining your approach aloud, tracing a failing case, and checking boundary conditions.
- Review the weak items from day one and fix the largest gap.
Deliverable: A mock-run log listing what went wrong and one fix for each item.
Behavioural preparation
Candidates report that the final stage is a manager conversation on behavioral questions and cultural fit, and that the digital interview step can include behavioral questions too. For a backend engineering role the reported prompts concern production incidents, disagreements over technical choices, deadlines versus quality, and changing requirements. Prepare each as a STAR story where you made the decisions and can state a result.
- Walk me through the architecture of the primary microservice you supported in your last role.
- Describe a scenario where a production service you managed experienced performance degradation, and explain how you resolved it.
Describe a challenging technical bug or outage you faced in a recent project and the specific steps you took t
Medium · Behavioral & Past Experience
Describe a challenging technical bug or outage you faced in a recent project and the specific steps you took to diagnose and resolve it.
Approach
- Choose an incident where you owned the diagnosis. In one or two sentences give the situation: what service, what user or business impact, how it was detected (alert, dashboard, customer report).
- Show the order of your reasoning: stop the damage first (rollback, feature flag, restart, scale-out), then narrow the cause by comparing recent deploys and config changes, reading logs and metrics, reproducing in a lower environment, and forming then testing one hypothesis at a time.
- Name the root cause precisely, for example a connection pool exhausted by a leaked connection, an unbounded cache, a slow query after a data growth step, or a race condition. Say how you confirmed it rather than assuming.
- Close with the result and prevention: the fix, a regression test, an alert or dashboard you added, and what changed in the process. Give a measured outcome such as error rate back to baseline or latency before and after.
Follow-up
- How did you decide between rolling back and fixing forward?
- What would have let you detect this earlier?
- How did you confirm the fix addressed the root cause and not just the symptom?
How do you resolve disagreements with team members or senior architects regarding tech stack selection or impl
Medium · Behavioral & Past Experience
How do you resolve disagreements with team members or senior architects regarding tech stack selection or implementation details?
Approach
- Pick a real disagreement about a technical choice, such as a database, framework or service boundary, where the other person was a teammate or a more senior architect. State both positions fairly, in a way the other person would accept as accurate.
- Describe how you moved the discussion from opinion to evidence: agreeing on criteria first (latency, operability, team skills, cost, migration risk), then a small benchmark, spike or review of existing code, and writing the trade-offs in a short design note.
- Show how it ended: either your option was adopted with a measurable reason, or the other view won and you committed fully and helped deliver it. Both endings are fine if your conduct is clear.
- Avoid a win-lose story. The strongest version includes something you learned from the other side or changed in your own proposal.
Follow-up
- What if the architect still disagreed after seeing your data?
- What did you concede, and why?
- How did you keep the working relationship afterwards?
Tell me about a time you had to meet a tight deadline while maintaining high code quality and test coverage.
Medium · Behavioral & Past Experience
Approach
- Set the context in two sentences: the deadline, what was at stake, and what quality meant concretely (tests, review, monitoring), not generalities.
- Explain the trade-off decisions you made: cutting scope with the product owner instead of cutting tests, prioritising tests on the critical paths such as money movement or data integrity, using feature flags or incremental releases, and keeping the CI pipeline as a hard gate.
- Give evidence: coverage on the changed code, defects found after release, on-time delivery. Name what you deliberately deferred and how you recorded it as a follow-up.
- Avoid claiming there was no cost. A credible story names the shortcut you rejected and the one you accepted.
Follow-up
- What did you drop from scope, and who agreed to that?
- How did you decide which code needed tests first?
- Would you handle the deadline differently now?
How do you prioritize tasks and handle scope changes when requirements evolve mid-sprint?
Medium · Behavioral & Past Experience
Approach
- Use a specific sprint where requirements changed after commitment. Describe the change, who raised it and what else was in flight.
- Show your prioritisation method: assess impact, urgency, dependencies and risk, and ask the product owner what the change replaces rather than just adding it. Re-estimate openly and show the trade-off in the backlog.
- Describe communication: telling stakeholders what moves out of the sprint, updating the team and recording the decision. Add how you protected quality while the plan shifted.
- Close with the outcome and what you changed in the process afterwards, such as earlier refinement or a buffer. Do not present either always saying yes or always refusing as the answer.
Follow-up
- What if both items were urgent and the stakeholders disagreed?
- How did you decide what to move out of the sprint?
- What did you change in planning afterwards?