The reported questions fall into four groups. Coding covers a function that counts spaces in a string, traversing multidimensional arrays, a string pattern-matching problem and a search algorithm. Language and framework questions cover RxJS subscriptions in Angular, Java garbage collection, asynchronous error handling in TypeScript, and reading a snippet for wrong output or concurrency bottlenecks. Design questions cover a SaaS product you built, booking confirmation across vendor APIs, high-level versus low-level design, and database partitioning and replication. Behavioral questions round it out. Most of these reward a prepared structure, so build one for each group instead of memorizing answers.
Candidates describe the work as full-stack: Angular and TypeScript on the frontend, Java services behind them, relational or non-relational stores, and calls out to third-party travel APIs. The responsibilities listed include code review, automated tests, monitoring and troubleshooting production incidents. That makes the production side of your experience worth rehearsing. Be ready to say how you detected a problem, how you narrowed it down and what you changed afterwards, because several reported questions (garbage collection and memory use, concurrency bottlenecks, a production concurrency bug) are debugging stories in disguise.
The design prompts share one theme: a system that depends on other systems. Real-time booking confirmation across several vendor APIs means timeouts, retries, duplicate requests and partial success. A cache of itinerary records means a data structure and an invalidation policy. A growing transactional database means a partition key, replication lag and failover. Practise answering each of these by naming the failure first and the component second.
Candidates also report that loops vary by geography and seniority, and that senior roles lean harder on architecture and leadership scenarios. If you are interviewing above entry level, give design and behavioral preparation at least as much time as algorithm practice. The plan below spreads the week across coding, runtime depth, two design days and a behavioral day.
Online Assessment
reportedCandidates report a Codility or similar online coding test that comes first and gates the live stages. The reports do not give a duration or a question list for this stage, so prepare for the format: writing code that runs, in your strongest language, without an interviewer to ask for hints. The coding questions candidates report across the loop (counting spaces in a string, traversing multidimensional arrays, string pattern matching, a search algorithm) are a sensible practice set for any coding stage like this one.
What to demonstrate
- Producing working code in a test environment rather than pseudocode
- Handling edge cases yourself, such as empty input, a single element or very large input
- Choosing a sensible time and space complexity for string, array and search problems
How to prepare
- Solve string, array and search problems in Java or TypeScript in a plain editor with no autocomplete, and run your own tests before you stop.
- Before coding each problem, write the edge cases as a short list (empty, null, one element, duplicates, whitespace other than a space) and test each one.
- Practise the reported coding questions end to end, including the multidimensional array traversal and the string pattern-matching problem, and state the complexity of each solution.
Live Technical Rounds
reportedCandidates report live coding sessions and deep dives into your resume after the online assessment. The language and framework questions reported across the loop (RxJS subscriptions in Angular, Java garbage collection and memory troubleshooting, asynchronous work and errors in TypeScript, reading a snippet for concurrency problems) are the kind of depth a resume deep dive on this stack can reach. Approach it in two parts: code aloud while keeping your reasoning audible, and be able to defend every project on your resume down to your own contribution and the trade-offs you chose.
What to demonstrate
- Working code written live, with the reasoning explained as you go
- Hands-on depth in the stack you list, such as Java memory behavior or Angular and RxJS
- Ability to explain your own past projects: what you built, what you chose and why
How to prepare
- Pick three projects from your resume and write for each the architecture, your exact contribution, one scaling bottleneck and one production problem you fixed.
- Be able to explain, with a concrete example, how you stop an RxJS subscription from leaking in an Angular component and how you would investigate rising heap usage in a Java service.
- Rehearse coding while talking: restate the problem, name the brute-force version, pick a structure, write it, then walk a test case through it by hand.
Managerial Discussions
reportedCandidates report managerial discussions that focus on behavioral questions, plus system design for more senior roles. Reported behavioral questions include conflicts in a cross-functional team, shifting requirements or tight deadlines, a professional weakness and why you want to build a career at this company. The reported design questions are the SaaS product you have built, real-time booking confirmation across third-party vendor APIs, HLD versus LLD for a new microservice, and partitioning and replication for a growing transactional database. Prepare stories you can tell in a structured way and one design framework you can reuse.
What to demonstrate
- Structured behavioral answers that show your own decisions and results
- For senior roles, design reasoning that names components, failure modes and trade-offs
- Clear explanation of technical choices to people outside engineering
How to prepare
- Write five stories (a disagreement, a changing requirement, a production incident, a weakness you are fixing, a project you led) and rehearse each aloud in STAR order.
- Practise the booking-confirmation design with a fixed outline: requirements, request flow, idempotency, vendor timeouts and retries, partial failure, data model, monitoring.
- Prepare a specific, factual answer to the question about why you want to build your career at this company, tied to the work the role describes.
- Prepare a pair of design talking points for database growth: how you would pick a partition key, and what replication lag does to reads after a write.
PracHub editorial advice for the preparation topics above.
Writing the string-counting function immediately without clarifying what counts as a space
Ask whether tabs, newlines and Unicode whitespace count, what a null or empty string returns, and how large the input can be. Then write the loop, add a test for each case, and say the complexity (one pass, constant extra memory).
Answering the RxJS question with only 'unsubscribe in ngOnDestroy'
Explain why subscriptions leak (a long-lived source keeps a reference to your callback), then give the options: the async pipe, takeUntilDestroyed or a destroy subject with takeUntil, finite sources that complete, and switchMap instead of nested subscribes. Say which you would choose and why.
Listing Java garbage collectors without a troubleshooting sequence for a memory problem in a busy service
Cover generations and reachability briefly, then walk the investigation: confirm whether the heap or something else is growing, read GC logs for frequency and pause time, take a heap dump or class histogram, find what retains the objects, and verify the fix under load.
Designing booking confirmation as if every vendor call succeeds
Design for failure first: idempotency keys so a retry does not double-book, per-vendor timeouts and bounded retries, a durable record of each attempt, a defined state for partial confirmation, and compensation or manual follow-up when only some vendors confirm.
Describing resume projects in terms of the team, so your own contribution is unclear
For each project on your resume, write what you personally designed, decided or fixed, the alternative you rejected and the result. Practise saying I for your work and naming teammates for theirs.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Write a function to count the number of spaces in a given string.
Write a function to count the number of spaces in a given string.
Approach
- Pin down what counts as a space before coding: only ' ' (U+0020), or any whitespace. In Java, Character.isWhitespace(c) is true for tab and newline but false for the non-breaking space U+00A0, while Character.isSpaceChar(c) is true for U+00A0. In JavaScript, /\s/ matches U+00A0 too.
- Write one pass over the characters with a counter: for (int i = 0; i < s.length(); i++) if (s.charAt(i) == ' ') count++. That is O(n) time and O(1) extra space. A space sits in the BMP, so scanning UTF-16 chars cannot split a surrogate pair into a false match.
- Handle null and empty input explicitly: return 0 for an empty string, and decide whether null throws or returns 0. Test leading, trailing and consecutive spaces, plus a string made only of spaces.
- Avoid the split trick in Java: "a ".split(" ") drops trailing empty strings and returns a single element, so split(" ").length - 1 undercounts. In JavaScript, "a ".split(" ") keeps them and returns three elements, so the same trick gives 2 there. Name this difference rather than relying on it.
- For very large input, stream it through a buffered reader in fixed-size chunks so memory stays bounded. Counting splits cleanly across chunks or threads because the per-chunk counts simply add up.
Follow-up
- How would you count words instead of spaces, so a run of several spaces counts as one separator?
- The input is a multi-gigabyte log file. How do you count without loading it, and would parallelising it help?
- How does your answer change if tabs, newlines and Unicode spaces must all count?
Discuss how garbage collection functions in Java and how you would troubleshoot memory usage in a high-through
Discuss how garbage collection functions in Java and how you would troubleshoot memory usage in a high-throughput backend service.
Approach
- Explain reachability: the JVM traces live objects from GC roots (thread stacks, static fields, JNI references), so reference cycles are collected. It does not use reference counting. Heap memory is generational: most objects die young in eden, survivors are copied between survivor spaces, and long-lived objects are promoted to the old generation.
- Name the collectors and their trade-offs. G1 has been the default since JDK 9: it is region-based and targets a pause goal set by -XX:MaxGCPauseMillis (default 200 ms). Parallel GC favours throughput. ZGC and Shenandoah do most of their work concurrently for very short pauses, and ZGC is generational by default from JDK 23.
- Separate a leak from allocation churn using GC logs (-Xlog:gc*). Old-generation occupancy after each collection climbing steadily points to a leak. A high allocation rate with frequent young GCs but a flat post-GC baseline points to churn, which you fix by allocating less, not with a bigger heap.
- Gather evidence with jcmd GC.class_histogram and jcmd GC.heap_dump, or -XX:+HeapDumpOnOutOfMemoryError. Open the dump in Eclipse MAT and follow the dominator tree to the largest retained sizes. Use JFR or async-profiler in allocation mode to find hot allocation sites.
- Check the usual causes: unbounded caches or static maps (bound them, for example with a Caffeine maximumSize), ThreadLocals on pooled threads, listeners that are never removed, and class-loader leaks filling Metaspace. If process RSS exceeds -Xmx, the memory is native (direct buffers, Metaspace, thread stacks): enable -XX:NativeMemoryTracking=summary and run jcmd VM.native_memory. In containers, size the heap with -XX:MaxRAMPercentage.
Follow-up
- The pod is OOM-killed but the heap never reaches -Xmx. Where is the memory going, and how do you prove it?
- How would you choose between G1 and ZGC for a latency-sensitive API?
- What does 'OutOfMemoryError: Metaspace' tell you, compared with 'Java heap space'?
Walk through a code snippet to identify output discrepancies or concurrency bottlenecks.
Walk through a code snippet to identify output discrepancies or concurrency bottlenecks.
Approach
- Trace the snippet by hand with concrete inputs and write expected output next to actual output, line by line, before you name a fix. Say what the runtime does at each step instead of what the code appears to intend.
- Check the usual causes of wrong output: integer division and overflow (int wraps silently in Java), floating-point comparison, off-by-one loop bounds, and == versus equals(). Integer caching makes == true for boxed values from -128 to 127 and false outside that range. In JavaScript, var in a loop shares one binding across closures where let does not, and Promise callbacks (microtasks) run before setTimeout callbacks.
- For concurrency, list every piece of shared mutable state and who writes it. count++ is a read-modify-write and is not atomic. volatile gives visibility but not atomicity. Check-then-act sequences on a HashMap race, so use ConcurrentHashMap.computeIfAbsent or merge, and AtomicInteger or LongAdder for counters.
- For bottlenecks, look for coarse locks: a synchronized method that does I/O or a remote call while holding the monitor, one global lock guarding unrelated data, or blocking calls inside an async pipeline. Narrow the critical section, stripe the lock, or switch to immutable snapshots.
- Confirm the diagnosis with a thread dump (jcmd Thread.print or jstack). Many threads BLOCKED on the same monitor means contention. A 'Found one Java-level deadlock' section shows inconsistent lock ordering. Prove the fix with a repeated stress test or jcstress, because one passing run proves nothing for a race.
Follow-up
- Is calling get() and then put() on a ConcurrentHashMap atomic? What would you use instead?
- The thread dump shows a deadlock between two locks. How do you fix it without one global lock?
- How would you show that your fix removed the race and did not just make it rarer?
Implement a solution to manipulate and traverse multidimensional arrays efficiently.
Implement a solution to manipulate and traverse multidimensional arrays efficiently.
Approach
- Clarify the shape first: rectangular or jagged rows, m x n or square, whether in-place modification is allowed, and what 'traverse' means (spiral, diagonal, flood fill, search). Then state the target, which is usually O(m*n) time for one full pass.
- Know the standard in-place moves. Spiral order shrinks four boundaries (top, bottom, left, right) and checks top <= bottom and left <= right before each side, so a single row or column is not repeated. To rotate an n x n matrix 90 degrees clockwise, transpose it and then reverse each row: O(n^2) time, O(1) extra space.
- For search: in a matrix sorted along rows and columns, start at the top-right and step left or down for O(m+n). If the whole matrix is sorted row after row, binary search over indices 0..mn-1 and map index k to (k / n, k % n) for O(log(mn)).
- For repeated range sums, build a 2-D prefix array P[i+1][j+1] = a[i][j] + P[i][j+1] + P[i+1][j] - P[i][j]. Each rectangle query then takes O(1) by inclusion-exclusion. For grid problems (islands, flood fill), use BFS or DFS with a direction-delta array and a visited marker.
- Mention memory layout: a Java int[][] is an array of row references, so keep the column index in the inner loop to stay cache-friendly. Test an empty matrix, a single row, a single column and jagged rows.
Follow-up
- How do you rotate a non-square m x n matrix, and why can't it be done in place in the same array?
- The matrix is huge and mostly zeros. How would you store and traverse it?
- How would you rotate counter-clockwise, or by 180 degrees, reusing the same building blocks?
Solve a medium-difficulty string manipulation problem involving pattern matching.
Solve a medium-difficulty string manipulation problem involving pattern matching.
Approach
- Pin down the variant before coding: substring search, anagram or permutation occurrences, wildcard matching with ? and *, a regex subset with . and *, or a word-pattern bijection. Also ask about case sensitivity, character set and whether matches may overlap.
- For an anagram or permutation of p inside s, slide a fixed window of length |p| and keep a 26-slot (or map) count difference plus the number of mismatched slots. Each step updates two counts, so the whole scan is O(n).
- For exact substring search, naive comparison is O(n*m) in the worst case. KMP builds the prefix function (for each position, the length of the longest proper prefix of the pattern that is also a suffix ending there) and searches in O(n+m). Rabin-Karp uses a rolling hash, expected O(n+m), and must verify characters on a hash hit because of collisions.
- For wildcard matching, let dp[i][j] mean the first i characters of s match the first j of p. A '' gives dp[i][j] = dp[i][j-1] || dp[i-1][j]. A '?' or an equal character gives dp[i-1][j-1]. dp[0][j] is true only when p[0..j) is all stars. This is O(nm) time and can use O(m) space with one rolling row.
- For a word-pattern bijection, keep two hash maps (char to word and word to char), so neither 'abba' to 'dog dog dog dog' nor 'ab' to 'dog dog' passes. Test an empty pattern, an empty text, a pattern longer than the text and overlapping matches such as 'aa' in 'aaaa'.
Follow-up
- How does '' in a regex differ from '' in wildcard matching, and how does the recurrence change?
- The text arrives as a stream you cannot rewind. Which of your approaches still works?
- How would you return every match position, including overlapping ones?
Given a dataset, write a searching algorithm optimized for minimal time complexity.
Given a dataset, write a searching algorithm optimized for minimal time complexity.
Approach
- Ask what the data guarantees: sorted or not, static or changing, one query or many, and exact match, range or nearest value. Unsorted data with a single query cannot beat a linear O(n) scan, because every element might be the answer.
- For many exact lookups, either build a hash set once (O(n) build, O(1) expected per lookup, but no ordering or ranges) or sort once in O(n log n) and binary search in O(log n) per query. Choose by whether range or nearest queries are needed.
- Write the lower-bound form of binary search to avoid off-by-one bugs: lo = 0, hi = n; while lo < hi, take mid = lo + (hi - lo) / 2 (which avoids int overflow); if a[mid] < target then lo = mid + 1, else hi = mid. It returns the first index whose value is >= target, which also gives first and last occurrences when there are duplicates.
- Know the library behaviour: Java Arrays.binarySearch returns -(insertionPoint) - 1 when the key is missing and makes no promise about which duplicate it finds. For data that changes, a balanced BST such as Java TreeMap gives O(log n) insert, delete, floorKey and ceilingKey.
- Extend binary search to a rotated sorted array (decide which half is sorted at each step) and to 'binary search on the answer' over any monotonic predicate. Interpolation search averages O(log log n) on uniformly distributed keys but degrades to O(n), so state that condition if you mention it.
Follow-up
- The array has duplicates. Return the first and last positions of the target in O(log n).
- Values are inserted and deleted between queries. What structure do you switch to, and what does each operation cost?
- The dataset no longer fits in memory. How does the search change?
Explain how you would structure a custom data structure to optimize retrieval times for cached travel itinerar
Explain how you would structure a custom data structure to optimize retrieval times for cached travel itinerary records.
Approach
- Clarify the access pattern: lookup by itinerary id, by traveler, by date range, or all of them. Also ask about the read/write ratio, how stale a record may be, and whether the bound is by entry count or by memory.
- Build the core as an LRU cache: a hash map from itinerary id to a node in a doubly linked list, with the most recently used node at the head. get and put are O(1), and eviction removes the tail. In Java, LinkedHashMap with accessOrder = true and an overridden removeEldestEntry gives the same behaviour.
- Add secondary indexes only for the queries that need them. A map from traveler id to a set of itinerary ids serves 'all trips for this traveler'. A TreeMap keyed by departure date gives O(log n + k) range queries. Every eviction and update must also clean up these indexes, or they become a leak.
- Handle freshness: store an expiry timestamp per entry and check it lazily on read. Add a min-heap or timer wheel if expired entries must be removed proactively, and invalidate or version entries when a booking changes. Compare LRU with LFU: plain LRU is evicted by one large scan, while a W-TinyLFU policy such as Caffeine's resists that.
- Address concurrency: a single LRU list needs a lock on every get because reads reorder it. Use striped or segmented caches, or a library such as Caffeine, which buffers reads to avoid contention. Across several instances, a shared cache such as Redis with per-key TTLs, read through cache-aside, avoids each node holding a different stale copy.
Follow-up
- How do you make this cache thread-safe without one lock serialising every read?
- A booking is changed by another service. How does your cache find out, and what does a reader see in the meantime?
- How would you bound the cache by memory used rather than by number of records?
Explain why the owner filter ignores the listing index
The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.
Approach
- Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
- For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
- Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
- Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.
Follow-up
- 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
- A dashboard runs this for 40 owners in one page load. What changes about the design?
Denormalise tenant onto revisions and backfill it live
resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.
Approach
- Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
- Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
- Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
- Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.
Follow-up
- The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
- How do you verify the backfill actually finished, given rows are still being inserted while it runs?
Explain the architecture of a SaaS product you have built, highlighting critical features and scaling bottlene
Explain the architecture of a SaaS product you have built, highlighting critical features and scaling bottlenecks.
Approach
- Open with one sentence on what the product did and who used it. Then walk the request path in order: client, load balancer or gateway, services, data stores, queues and third-party integrations. Say plainly which parts you built or owned and which you only used.
- Name the multi-tenancy model and its trade-off: a shared schema with a tenant_id column (cheapest, but needs strict row filtering), schema-per-tenant, or database-per-tenant (strongest isolation, highest operating cost). Explain how tenant identity flows from authentication down to every query.
- Pick two or three features that shaped the design, such as SSO through SAML or OIDC, role-based access, audit logging, billing or a third-party integration, and say what each one forced in the architecture.
- For each scaling bottleneck, give the symptom, the measurement and the fix. Examples are database connection exhaustion fixed with a pooler, a noisy tenant fixed with per-tenant rate limits or a dedicated queue, N+1 queries fixed with batching, and slow synchronous vendor calls moved behind a queue. State the before-and-after numbers you actually measured.
- Close with what you would change now and why, such as a component you would split out, merge back or replace. This shows you understand the trade-offs rather than only the diagram.
Follow-up
- One tenant generates most of the load. How do you stop it from degrading everyone else?
- How did you deploy schema changes without downtime?
- Which component breaks first at ten times the current traffic, and how do you know?
Design a high-level system for real-time booking confirmation across multiple third-party vendor APIs.
Design a high-level system for real-time booking confirmation across multiple third-party vendor APIs.
Approach
- Scope it first: which vendors (air, hotel, car), whether a trip combines several of them, the latency the user waits for, and what 'confirmed' means. Then sketch the path: booking API, booking service, orchestrator, one adapter per vendor, a bookings database, an event queue and a notification channel (WebSocket, SSE or polling).
- Make writes safe to retry. The client sends an idempotency key, and the booking row is created in a PENDING state under it, so a retried request returns the same booking. Drive each booking through an explicit state machine (PENDING, CONFIRMED, FAILED, CANCELLED) and use the transactional outbox pattern so state changes and events cannot diverge.
- Coordinate multi-vendor trips as a saga: confirm each leg in turn and, if one fails, run compensating actions (cancel the held hotel when the flight fails), since no distributed transaction spans the vendors. Normalise every vendor's request and response format inside its adapter.
- Isolate each vendor: set per-vendor timeouts, retry with exponential backoff and jitter only when the vendor call is idempotent, add a circuit breaker per vendor, keep separate connection or thread pools (bulkheads), and apply token-bucket limits within each vendor's rate quota. Treat a timeout as an unknown outcome and reconcile it by querying the vendor's status, because a blind retry can double-book.
- If a vendor is slow, return 'pending' to the user and push the final result when it arrives. Use a correlation id across all vendor calls, add metrics on per-vendor latency and error rate, and run a periodic reconciliation job that compares local state with vendor records.
Follow-up
- The vendor call times out but the vendor actually created the reservation. How do you avoid a duplicate booking?
- The flight confirms but the hotel fails. What does the user see, and what does the system do?
- A vendor cuts your rate limit during a traffic spike. How does the design degrade?
Discuss how you approach High-Level Design (HLD) versus Low-Level Design (LLD) when building a new microservic
Discuss how you approach High-Level Design (HLD) versus Low-Level Design (LLD) when building a new microservice.
Approach
- Frame HLD as the decisions that are expensive to reverse: the service's responsibility and boundary, the data it alone owns, the interfaces it exposes (REST, gRPC or events), synchronous versus asynchronous dependencies, and non-functional targets such as latency, throughput, availability and security.
- Frame LLD as the decisions inside the boundary: the domain model and module structure, interfaces and patterns (for example a strategy or adapter per external provider and a repository layer), the database schema and indexes, API contracts with error codes, validation, idempotency, concurrency control and the test plan.
- Explain the order: settle the HLD first, because boundaries and data ownership drive everything else, then go deep in LLD on the riskiest parts. Record significant trade-offs in short architecture decision records.
- Cover cross-service concerns at the HLD level: consistency without distributed transactions (sagas, outbox), how a downstream failure is contained (timeouts, circuit breakers, fallbacks), API versioning, and observability (structured logs, metrics, traces with a correlation id).
- Ground it in one service you built: show its HLD decisions, then one LLD detail such as a table design or a class interface, and explain how one shaped the other. Mention when you would keep the code as a module in an existing service instead of creating a new microservice.
Follow-up
- When would you decide not to create a new microservice at all?
- How do you change this service's API without breaking existing consumers?
- Walk through the low-level design of the one component you consider riskiest.
How would you handle database partitioning and replication for a rapidly growing transactional database?
How would you handle database partitioning and replication for a rapidly growing transactional database?
Approach
- Measure before choosing: find out whether the pressure comes from read QPS, write QPS, storage growth or a few hot tables. Then apply the cheap fixes first: query and index tuning, connection pooling (for example PgBouncer), vertical scaling and archiving cold data.
- For read pressure, add read replicas. Replication is usually asynchronous, so replicas lag, and a user who just booked may not see the booking on a replica. Handle this with read-your-writes routing: send that user's reads to the primary for a short window after a write, or compare replication positions.
- Use partitioning within one node for large time-based tables. In PostgreSQL, declarative range partitioning by month on a booking date enables partition pruning and makes retention a cheap DETACH or DROP instead of a mass DELETE. A unique constraint on a partitioned table must include the partition key columns.
- Shard across nodes when one primary cannot absorb the writes. Choose a shard key such as customer or tenant id so most transactions stay on one shard. Hash sharding spreads load evenly, while range sharding on a monotonically increasing key creates a hot shard. Cross-shard joins and transactions become expensive, and global IDs need a scheme such as Snowflake-style IDs or UUIDv7. Mapping many logical shards to fewer physical nodes makes resharding easier.
- Choose the replication mode by recovery objective: synchronous replication to at least one standby means a committed transaction survives losing the primary, at the cost of write latency. Use automated failover (for example Patroni) with fencing to avoid split-brain. To migrate live data, backfill through CDC or dual writes, verify counts and checksums, then cut over.
Follow-up
- Your shard key makes an important reporting query cross-shard. How do you serve it?
- A traveler books and the confirmation page, read from a replica, shows nothing. How do you fix it?
- How do you move from 4 shards to 16 without downtime?
Explain how RxJS observables work in Angular and how you manage subscriptions to prevent memory leaks.
Explain how RxJS observables work in Angular and how you manage subscriptions to prevent memory leaks.
Approach
- Explain the model: an Observable is lazy and does nothing until subscribe() runs it. HttpClient observables are cold, emit one response and complete. A Subject or BehaviorSubject in a service is hot, shared and never completes on its own, and so are interval and fromEvent.
- Explain the leak: a subscription to a long-lived source holds a closure that references the component. After the component is destroyed, the callback keeps running and the component cannot be garbage collected. Each revisit of the route adds another live subscription.
- Default to the async pipe in templates, because Angular subscribes and unsubscribes with the view. In code, use takeUntilDestroyed() from @angular/core/rxjs-interop (Angular 16+), which needs an injection context or an explicit DestroyRef. On older versions, use takeUntil(this.destroy$) and call destroy$.next() and destroy$.complete() in ngOnDestroy.
- Put takeUntil last in the pipe. If it sits before a switchMap or mergeMap, the inner subscription can outlive the teardown. Use take(1) or first() for one-shot reads. With shareReplay, set refCount: true, or the shared source stays subscribed after every subscriber leaves.
- Choose flattening operators deliberately: switchMap cancels the previous inner request (typeahead search), concatMap preserves order, mergeMap runs concurrently and exhaustMap ignores triggers while busy (double-click submit). To confirm a leak, compare Chrome heap snapshots across repeated navigation and look for detached component instances.
Follow-up
- An HttpClient call completes by itself. Can it still cause a bug if the component is destroyed before the response arrives?
- When would you pick switchMap over mergeMap or exhaustMap, and what goes wrong with the wrong one?
- How would you find which subscription is leaking in a large app?
The week follows the three reported stages: coding fundamentals first, then Java, Angular and TypeScript depth, then two system design days, then the behavioral material. Each day ends with something you can check, such as tested code, a diagram or written stories.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Map the loop and inventory your projects
- Write the three reported stages (Online Assessment, Live Technical Rounds, Managerial Discussions) and next to each the question types that fit it.
- Choose three projects from your resume. For each, write the architecture, your own contribution, one scaling bottleneck and one production problem you fixed.
- Write a spoken answer to the question about explaining the architecture of a SaaS product you have built, using your strongest project.
Deliverable: A one-page loop map, three project summaries, and a written outline of your SaaS architecture answer.
Practice prompt ↗02Strings, arrays and pattern matching
- Write the function that counts spaces in a string in your main language, handling null, empty, tabs and Unicode whitespace, and add tests for each case.
- Implement multidimensional array traversals (row by row, by column, diagonally and in a spiral), checking the index arithmetic with a non-square matrix.
- Solve two string pattern-matching problems, first the brute-force way, then with a sliding window or a prefix-function approach, and note the complexity of each.
Deliverable: Tested solutions for the spaces function, the array traversals and two pattern-matching problems, each with its complexity written next to it.
Practice prompt ↗Practice prompt ↗Practice prompt ↗03Search and the itinerary cache
- Implement binary search and its variants (first occurrence, insertion point, search in a rotated array) and list when a hash lookup beats it.
- Build an LRU cache with a hash map and a doubly linked list, giving O(1) get and put, then add optional time-to-live expiry.
- Write a short answer on how you would structure cached itinerary records: the lookup key, eviction, and when an entry becomes stale.
Deliverable: Working binary search variants and an LRU cache with tests, plus a written answer on structuring the cache with complexity noted.
Practice prompt ↗Practice prompt ↗04Java, Angular and TypeScript depth
- Write a one-page explanation of Java garbage collection (generations, reachability, common collectors) and a step-by-step plan for investigating growing memory in a busy service.
- Build a small Angular component that leaks a subscription, show the leak, then fix it with the async pipe and with takeUntilDestroyed.
- Write TypeScript examples of Promise.all versus Promise.allSettled, an AbortController cancellation and a typed Result for expected failures.
- Take a short Java snippet that shares a counter across threads, predict its output, then fix the race and describe the bottleneck your fix introduces.
Deliverable: A GC and memory investigation page, a before-and-after Angular component, a TypeScript async examples file and an annotated concurrency snippet.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗05Design: booking confirmation and HLD versus LLD
- Design real-time booking confirmation across several vendor APIs: draw the request flow, state the data model, and add idempotency keys, timeouts, retries and a state for partial success.
- List the failure modes (vendor down, slow response, duplicate request, one vendor confirms while another fails) and the action for each.
- For one microservice from that design, produce both the high-level view (boundaries, APIs, data stores) and the low-level view (key classes or interfaces, request and response contracts).
Deliverable: One annotated diagram, a table of failure modes with responses, and a high-level plus low-level sketch for a single service.
Practice prompt ↗Practice prompt ↗06Database growth and indexing
- Write an answer for partitioning and replication of a fast-growing transactional database: the partition key you would choose, the queries it makes expensive, and how you handle replication lag and failover.
- Work through the SQL practice item on why the owner filter ignores the listing index, and write down the rule you learned about index use.
- Work through the SQL practice item on denormalizing a tenant onto revisions and backfilling it live, and note how the backfill avoids blocking writes.
Deliverable: A written partitioning and replication answer plus two short notes, one per SQL practice item, on indexing and live backfill.
Practice prompt ↗Practice prompt ↗Practice prompt ↗07Behavioral stories and a full mock
- Write STAR outlines for the five reported behavioral questions: introducing yourself, a cross-functional disagreement, shifting requirements or deadlines, your weakness and why this company.
- Say each aloud and cut anything that is not your own decision or result.
- Run a full mock: one coding problem, one design prompt and the stories, then list the three weakest moments and fix them.
Deliverable: Five rehearsed STAR outlines and a mock-session note listing three weak spots with a fix for each.
Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Practice prompt ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
Candidates report that the managerial stage focuses on behavioral questions, with system design added for more senior roles. The reported prompts cover how you work with product, QA and architects, how you handle change and pressure, self-awareness, and why you want this employer. Prepare stories where you made the decision, quantify the outcome and describe your own part.
How do you handle asynchronous operations and error boundaries in TypeScript?
How do you handle asynchronous operations and error boundaries in TypeScript?
Approach
- Start with the model. A promise settles once, and an async function never throws synchronously: it returns a rejected promise. Use try/catch around an await where you can recover or add context, and let the rejection propagate where you cannot. Make sure every promise is awaited, returned or explicitly handled, because a promise nobody handles becomes an unhandled rejection.
- Cover concurrency: Promise.all rejects on the first failure but does not cancel the other work, Promise.allSettled returns every outcome when partial results are acceptable, and Promise.race plus a timeout bounds a slow call. To cancel real work, pass an AbortSignal from an AbortController into fetch rather than just ignoring the result.
- Type the errors. In strict mode the catch variable is unknown, so narrow it with instanceof or a type guard. For expected failures such as validation or a vendor timeout, return a discriminated union like { ok: true, value } or { ok: false, error } so callers must handle both cases, and keep throw for the unexpected.
- Clarify the term: error boundary is a React idea. In an Angular codebase the equivalents are catchError inside a stream that returns a fallback so the stream stays alive, an HttpInterceptor for HTTP failures, and a global ErrorHandler for anything uncaught that logs and shows a safe message.
- Mention retries last: use bounded retries with backoff only for idempotent calls, and avoid forEach(async ...), which discards the promises and hides failures. A lint rule such as no-floating-promises catches the common mistakes.
Follow-up
- When would you pick Promise.allSettled over Promise.all, for example when calling several external services?
- What happens to a rejected promise that nobody awaits, and how would you catch that in production?
- How would you cancel an in-flight request when a component is destroyed?
Tell me about yourself and walk through your past professional experience relevant to this role.
Tell me about yourself and walk through your past professional experience relevant to this role.
Approach
- Use a three-part shape: present (your current role and the kind of work you own), past (two or three experiences that led here, each with one outcome), and future (why this role is the next step). Cover what you built and for whom, not a list of job titles.
- Choose the experiences that match the stack and work the role describes: Java or TypeScript services, Angular or another frontend framework, integrations with external APIs, and production ownership such as monitoring, tests and incidents. Give each a concrete result, such as a latency drop, an incident you resolved or a system you migrated, and say which part was yours.
- End by handing over a thread. Name the one project you would be glad to go deeper on, because candidates report resume deep dives in the live technical stage and this is how you steer toward your strongest material.
- The mistake that hurts most is a chronological recitation of every job with no emphasis, or answers that say we throughout and never show what you did. Rehearse it aloud until it sounds like a conversation, not a script.
Follow-up
- Which of those projects would you like to walk me through in more detail?
- What was your specific contribution, as opposed to the team's?
- What made you decide to move on from your last role?
How do you approach and resolve technical disagreements or conflicts within a cross-functional engineering tea
How do you approach and resolve technical disagreements or conflicts within a cross-functional engineering team?
Approach
- Choose a real disagreement about a technical decision with someone outside your own function, such as a product manager pushing a scope, a QA engineer disputing a release, or an architect preferring a different design. Describe it in two sentences, then spend most of the time on how you handled it.
- State the other person's position in terms they would accept, including the constraint behind it (a date, a risk, a customer promise). Then show how you moved from opinions to evidence: a prototype, a benchmark, a load test, a list of failure modes, or a small experiment behind a flag.
- Say how the decision was made and by whom, and what you did if it went against you. A strong ending shows the outcome and the working relationship afterwards, and what you changed in how you raise disagreements.
- The mistake that sinks this answer is a story where you were simply right and the other side was wrong, or one where you avoided the conflict altogether. Pick a case where you changed your mind partly, or where you accepted a decision and still delivered it well.
Follow-up
- What would you have done if the decision had gone against you?
- How do you handle disagreeing with someone more senior than you?
- Was there a point where you realized the other person was right about something?
Describe a time when you had to manage shifting project requirements or tight delivery deadlines.
Describe a time when you had to manage shifting project requirements or tight delivery deadlines.
Approach
- Describe what changed and how you found out: a requirement revised mid-build, a dependency that slipped, or a fixed date that did not move. Keep the situation short, then show your sequence of decisions.
- Show how you re-planned. Good material includes splitting the work into what must ship and what can follow, agreeing the cut list explicitly with the product manager, delivering a thin end-to-end slice first, and using feature flags or a staged rollout so incomplete work stays out of the way.
- Cover how you kept quality up under the squeeze: which tests you refused to skip, what you checked in review, and how you watched the release. Say what you told stakeholders and when, because early, specific communication is the point of the story.
- Give the result with numbers if you have them (what shipped, what moved, what the follow-up cost), and name the lesson. A story of unplanned overtime with no prioritization and no communication is the common failure.
Follow-up
- What exactly did you cut, and who agreed to it?
- How did you keep the quality of what you shipped acceptable?
- What would you do early next time so the same pressure does not build up?
What is your professional weakness, and what active steps are you taking to improve upon it?
What is your professional weakness, and what active steps are you taking to improve upon it?
Approach
- Pick a real weakness that is relevant to engineering work but does not undercut the basics of the job, for example a habit of over-engineering first versions, reluctance to delegate review, optimistic estimates, or being slow to write documentation. Avoid a disguised strength such as working too hard.
- Anchor it in one specific incident where it cost something, so the answer is a story instead of a label. Say what the effect was on the team or the delivery.
- Then describe the mechanism you set up, since the question asks for active steps: a time-boxed spike before committing to a design, a short design note reviewed before coding, adding a buffer based on your last three estimates, or a documentation checklist in your pull request template.
- Close with evidence that it is working, such as the last time you caught it early, feedback from a teammate or a changed result. The mistake to avoid is naming a weakness with no steps, or naming something so serious that it contradicts the work you are applying for.
Follow-up
- How would a teammate describe this weakness?
- When did it last show up, and what did you do?
- How do you know your approach is working?
Why do you want to build your career at American Express Global Business Travel specifically?
Why do you want to build your career at American Express Global Business Travel specifically?
Approach
- Build the answer from facts you can state. The role is described as software for corporate travel booking and expense management, using Java, TypeScript and Angular, with integrations to third-party travel APIs. Read the company's public materials before the interview and add one or two specifics you actually found, so the answer is yours and not generic.
- Connect those facts to your own experience: transaction-heavy systems, reliability work, integrating external services, or frontend work for business users. Say what you want to learn or build next and why this kind of work fits.
- Mention what you would like to contribute early, such as improving test coverage or handling vendor failures gracefully. Do not guess at internal team details or make claims you cannot support.
- The mistake is praising the brand name or saying you want a large company. A better answer would stop making sense if you swapped in a different employer.
Follow-up
- What do you know about the technology stack for the teams you would join?
- What kind of problems would you most like to work on in your first months?
- Which other opportunities are you considering, and how does this one compare?
- 01
Tell me about yourself and walk through your past professional experience relevant to this role.
- 02
How do you approach and resolve technical disagreements or conflicts within a cross-functional engineering team?
- 03
Describe a time when you had to manage shifting project requirements or tight delivery deadlines.
- 04
What is your professional weakness, and what active steps are you taking to improve upon it?
- 05
Why do you want to build your career at American Express Global Business Travel specifically?
- 06
Show how you debugged a complex concurrency issue in a production backend service.
How many rounds are there and how long does the process take?
Candidates report 3 rounds over about 3-5 weeks: an online assessment, live technical rounds, then managerial discussions. Reports also say loops vary by geography and seniority, so ask your recruiter what your own schedule looks like.
PracHub Software Engineer practice ↗What is the online assessment?
Candidates report a Codility or similar online coding test before the live stages. No question list or duration is reported, so practise solving string, array and search problems in your main language, running your own tests, and checking edge cases like empty input.
PracHub Software Engineer practice ↗Which languages and frameworks should I prepare?
The role lists Java and TypeScript, with Angular on the frontend, and says the stack varies by team. Cloud platforms (AWS, Azure), Docker, Kubernetes, event-driven architecture and CI/CD pipelines are listed as nice-to-haves. Be strongest in the language you will code in and ready to discuss RxJS subscriptions, Java memory behavior and TypeScript async handling.
PracHub Software Engineer practice ↗What system design questions are reported?
Candidates report four: the architecture of a SaaS product you have built, real-time booking confirmation across third-party vendor APIs, HLD versus LLD for a new microservice, and database partitioning and replication. System design is mainly reported for more senior roles, where candidates also say architecture and leadership get more weight.
PracHub Software Engineer practice ↗How should I prepare for the resume deep dive?
Candidates report that the live technical stage includes deep dives into your resume. For each project, be ready to state what you built, your own contribution, the alternatives you considered, the main scaling problem and one production issue you resolved. Cut anything on your resume you cannot discuss in detail.
PracHub Software Engineer practice ↗How should I structure the behavioral answers?
Use situation, task, action and result, but spend most of the time on the action: what you decided and why. Prepare one story each for a cross-functional disagreement, shifting requirements or a tight deadline, and a weakness you are working on. Add a specific answer for why you want to work at this company.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01American Express Global Business Travel Software Engineer candidate reports ↗
Company-reported rounds, questions and FAQ.
candidate · Accessed 2026-09-22 - 02PracHub Software Engineer practice ↗
PracHub practice material, not company-reported.
platform · Accessed 2026-09-22 - 03PracHub preparation framework ↗
PracHub preparation guidance.
platform · Accessed 2026-09-22