Interview guides · Apexon
Apexon Software Engineer Interview Guide
Apexon Software Engineer interview guide: the reported three-round loop, Java 8, Spring Boot, React, SQL and DSA questions, plus a seven-day plan.
The 60-second brief
Candidates describe Apexon as a digital technology services firm, so engineers are usually placed on client projects rather than on one in-house product. The client sectors named in candidate reports are healthcare, financial services, automotive and technology. The stack depends on the project track: Java 8+ with Spring Boot for back-end services, React or Angular for front ends, C#/.NET and ASP.NET Core on some tracks, and integration platforms such as IBM IIB, ITX or MuleSoft on others. Reported work includes building REST services, event-driven designs with Kafka or RabbitMQ, moving legacy back ends to cloud environments (AWS and Azure are named, and one candidate review mentions ECS, S3 and API Gateway) and running CI/CD pipelines. In a typical week you write and test code, review pull requests, join daily stand-ups, design schemas, write integration tests, fix production defects and document what you built, alongside architects, QA engineers and client stakeholders.
This guide is for candidates interviewing for a Software Engineer role at Apexon on a back-end, full-stack or front-end track. It covers the three reported rounds, the nineteen questions candidates report (Java and Spring Boot, microservices and messaging, SQL, JavaScript and React, data structures and live coding, and project-delivery behavioral questions), how to answer them, and a seven-day plan. If your track is .NET or an integration platform, lean on the architecture, SQL and behavioral material and swap the language revision for your own stack.
Candidates report three stages over about 3-5 weeks: a Recruiter Screen, one or two internal Technical Rounds, and a Leadership or Client Interview. Some accounts add an automated test early on and a closing HR conversation about onboarding and the offer.
Focus areas
- Java
- Data Structures & Algorithms (DSA)
- Spring Boot
Overview
The reported questions fall into four groups, and each needs a different kind of preparation. Core Java, Spring Boot and back-end architecture questions ask you to explain mechanisms: how streams and lambdas change collection code, how a Spring Boot application starts and wires beans, how a Saga differs from two-phase commit, how a synchronous REST call differs from a Kafka or AMQP message. JavaScript and React questions mix explanation (event bubbling, hoisting, hooks, Redux Toolkit versus Context) with one build-style task, a filtered and paginated product list in React and TypeScript. The data structures group is live coding plus collection internals. The last group is behavioral and project delivery.
Because candidates report coding on CoderPad or HackerRank, practise in a plain editor with no autocomplete, saying your plan aloud before you type. Candidates describe the algorithm questions as easy to medium, which makes clean code, edge cases and a stated complexity the differentiators rather than exotic algorithms. Candidates also report SQL as common on back-end and full-stack tracks: joins, aggregations, subqueries and finding missing rows.
The final stage is described as a leadership or client conversation about collaboration, Agile practice and security compliance, and for project-based roles a client technical evaluation that can include pair-programming, system design or scenario-based architecture questions. That makes your own project the most reusable preparation asset: one architecture you can draw from memory, your exact contribution, the alternatives you rejected, and two or three incidents and disagreements you can tell in a structured way.
The Apexon interview process
Candidates describe the stages below as the usual order, with the Technical Rounds sometimes split into two internal interviews and a formal HR discussion closing the process in some accounts.
Recruiter Screen
Candidates describe an opening call with a recruiter that confirms your experience, location preferences and baseline technical fit, covering your résumé, key skills and salary expectations. In some locations and campus drives an automated screening test or a short audio or video technical screen is reported before the technical rounds. Treat it as a fit check for the specific project track: the Java, JavaScript or .NET stack you name here sets the questions you meet later.
What to demonstrate
- Whether your experience and location preferences fit the opening
- Baseline technical alignment with the stack on your résumé
- Clarity when summarising your own projects and skills
How to prepare
- Write a short pitch that links your recent work to Java 8+, Spring Boot, React, SQL or whichever track you are applying for, and say which you are strongest in.
- Have your salary expectation, notice period and location constraints ready, and ask what the technical rounds will cover.
- If an automated test is mentioned, rehearse array and string questions such as finding duplicates or run-length encoding in a plain editor.
Technical Rounds
Candidates report one or two internal technical interviews covering core programming languages, coding exercises, data structures and framework-specific questions, with architectural discussion and deep questions on integration technologies for some tracks. Coding is reported on CoderPad or HackerRank, and stack-specific questions are answered in the technology of your application (Java 8+, React or SQL). A live coding stage is where the array, string, recursion and collection questions in this guide are practised; the Spring Boot, microservices and React explanations are practised aloud. Outline your approach before typing and name complexity as you finish.
What to demonstrate
- Working code on a live editor with edge cases handled
- Core Java, OOP and collection internals
- Framework and microservices knowledge: Spring Boot, JPA, REST, Kafka or RabbitMQ
- SQL fluency across joins, aggregations and subqueries
How to prepare
- Write the missing-or-duplicate-element solution, run-length encoding and the anagram check in Java without an IDE, then state time and space complexity.
- Explain aloud how a Spring Boot application starts, what dependency injection wires up, and how Saga and Kafka-based designs differ from synchronous REST.
- Revise equals/hashCode, HashMap collision handling and ArrayList versus LinkedList costs so you can answer without notes.
- Write three SQL queries from scratch: an anti-join for missing rows, a grouped aggregate across a join, and a subquery.
Leadership or Client Interview
Candidates describe a leadership or client interview that is behavioral and architectural, covering collaboration with teams, Agile practice and security compliance. For project-based roles the final stage is reported as a client technical evaluation with the client's engineering lead or technical architect, which can include live pair-programming, system design discussion or scenario-based architecture questions. Ask about project goals and performance constraints before you design anything, and speak in clear, plain English because client-facing work is routine.
What to demonstrate
- Collaboration with teammates and client stakeholders
- Agile working habits under changing requirements
- Architecture reasoning, including security considerations
- Clarity when explaining trade-offs
How to prepare
- Prepare a drawn architecture of your most recent project with your contribution and the technology decisions you owned.
- Write STAR outlines for a production incident, a mid-sprint requirement change and a design disagreement.
- List how authentication, authorisation, secrets and data handling worked in a past project so a security-compliance question has real answers.
- Practise a short pair-programming session: talk through the problem, write a small function, then test it aloud.
Where candidates lose points
Coding on CoderPad in silence, then rewriting after a wrong first approach
Say the input, the brute-force idea and the target complexity before typing. For the missing-or-duplicate-element question, name the index-marking or hash-set approach first and then write it.
Reciting Java 8 or Spring Boot definitions without showing mechanism
Be ready to explain how a stream pipeline is lazy until a terminal operation, how equals and hashCode drive HashMap lookups, and how Spring wires beans at start-up, with a short code or call example each.
Answering Saga versus two-phase commit or REST versus Kafka as a list of definitions
Frame each as a failure scenario: what happens when payment succeeds and shipping fails, how compensating actions work, and what idempotent consumers do on a redelivered message.
Giving a project walkthrough that lists technologies but not your own decisions
Draw the architecture, mark the parts you built, and give one decision with the alternative you rejected and why. Rehearse this once with someone who has not seen the project.
Treating the client round as another technical screen and starting to design immediately
Open with questions about goals, load and constraints, restate them, and bring security and Agile delivery examples ready, since collaboration and compliance are reported topics.
Questions & practice
Explain the differences between deep copy and shallow copy in JavaScript and show how object immutability is m
Medium · JavaScript, React, and Front-End Enginee
Explain the differences between deep copy and shallow copy in JavaScript and show how object immutability is maintained.
Approach
- Shallow copy: spread ({...obj}, [...arr]), Object.assign and Array.prototype.slice copy only the top level, so nested objects and arrays are still shared, and mutating copy.address.city changes the original too.
- Deep copy options and their limits: structuredClone handles Date, Map, Set, typed arrays and circular references, but throws DataCloneError on functions and DOM nodes, and class instances come back as plain objects. JSON.parse(JSON.stringify(x)) drops undefined, functions and symbols, turns Date into a string and NaN or Infinity into null, and throws on cycles and BigInt. lodash cloneDeep is the library choice.
- If asked to write one, recurse over own keys, copy arrays as arrays, and keep a WeakMap from original to copy so cycles and shared references are cloned once. That is O(n) in the number of nodes.
- Immutability: const stops reassignment, not mutation. Object.freeze is shallow, and a write to a frozen object is silently ignored in sloppy mode but throws a TypeError in strict mode, so a deep freeze must recurse. For updates, copy along the changed path ({...state, user: {...state.user, name}}) so untouched branches stay shared. Use map, filter and concat, or ES2023 toSorted, toSpliced and with, instead of push, splice and sort.
- Tie it to React and Redux: change detection compares references, so mutating state in place means no re-render or a stale selector. Immer lets you write mutating code that produces a new immutable object.
Follow-up
- What does structuredClone do with a class instance or an object holding a function?
- Update the quantity of one item inside an array nested in state without mutating anything.
- When is deep cloning a large state object too expensive, and what do you do instead?
Given an array of integers, write an efficient algorithm to find missing or duplicate elements within $O(n)$ t
Medium · Data Structures, Algorithms, and Live Co
Given an array of integers, write an efficient algorithm to find missing or duplicate elements within $O(n)$ time complexity.
Approach
- Pin down the variant first: are values in 1..n or 0..n, is there one missing and one duplicated or several, is the array sorted, and may I modify it? The best solution depends on those answers.
- One number missing from 0..n with n distinct values: subtract the actual sum from n(n+1)/2, using a long to avoid overflow, or XOR every index and value together. Both are O(n) time and O(1) space.
- General case: one pass with a HashSet, where Set.add returning false marks a duplicate, then loop over 1..n to collect values not seen. That is O(n) time and O(n) space, and a boolean[n + 1] does the same with less overhead.
- O(1) extra space when values are in 1..n and mutation is allowed: for each x, negate nums[|x| - 1]; if it is already negative, |x| is a duplicate, and any index still positive at the end gives a missing value (index + 1). Cyclic sort, swapping each value into position value - 1, is also O(n), because every swap puts one element in its final place.
- Edge cases: an empty array, nothing missing, several duplicates, and negative or out-of-range values, which break the index tricks (fall back to a hash set). To find the duplicate in n + 1 values from 1..n without modifying the array, Floyd's cycle detection runs in O(n) time and O(1) space.
Follow-up
- Find both the duplicated and the missing number in one pass with O(1) extra space, without a hash set.
- How would you do this if the numbers arrive as a stream too large to hold in memory?
- Find the duplicate without modifying the array and without extra memory.
How would you implement string manipulation algorithms such as run-length encoding or checking if two strings
Medium · Data Structures, Algorithms, and Live Co
How would you implement string manipulation algorithms such as run-length encoding or checking if two strings are valid anagrams?
Approach
- Run-length encoding in one pass: keep the current character and a run counter, and when the character changes or the input ends, append the character and the count to a StringBuilder. That is O(n), whereas repeated String concatenation in a loop can degrade to O(n^2).
- Handle the encoding edge cases: an empty string, a single character, runs of 10 or more (the decoder must read multi-digit counts), and input that already contains digits, which makes the format ambiguous unless you escape them or encode counts differently. Ask whether to return the original when the encoding is not shorter ("aabcccccaaa" becomes "a2b1c5a3").
- To decode, read a character, parse the full number that follows it, and append the character that many times. Validate malformed input such as a count with no character before it.
- Anagrams: return false if the lengths differ. For lowercase ASCII, use an int[26]: increment for each character of s, decrement for t, then check that every slot is zero. That is O(n) time and O(1) space. Sorting both strings is O(n log n). For general Unicode, count in a HashMap over s.codePoints(), because a Java char is a UTF-16 unit and cannot represent every character.
- Clarify normalisation before coding: is the comparison case-sensitive, and do spaces and punctuation count? Extension: to group words into anagram sets, key a HashMap<String, List<String>> by the sorted word (O(n · k log k)) or by a 26-count signature (O(n · k)).
Follow-up
- Group a list of words into anagram sets. What do you use as the key?
- Encode in place in a char array when the encoded form is never longer than the input.
- Why is concatenating Strings in a loop slow in Java, and what does StringBuilder change?
Explain the difference between ArrayList and LinkedList under the hood, detailing the time complexity for look
Medium · Data Structures, Algorithms, and Live Co
Explain the difference between ArrayList and LinkedList under the hood, detailing the time complexity for lookup, insertion, and deletion operations.
Approach
- ArrayList is backed by an Object[]. get(i) is O(1). add at the end is amortised O(1): when the array is full it grows to about 1.5 times its size (oldCapacity + (oldCapacity >> 1)) and copies the elements. Inserting or removing at index i is O(n - i) because System.arraycopy shifts the tail, and contains is O(n).
- LinkedList is doubly linked. get(i) is O(n): it walks from whichever end is nearer, so up to n/2 steps. Adding or removing at the head or tail is O(1). Inserting at a position is O(1) only once you hold the node, for example through ListIterator.add or remove; reaching it is still O(n). It also implements Deque.
- Memory and cache behaviour: every LinkedList node is a separate object with a header and three references (item, prev, next), while ArrayList stores one reference per element in a contiguous array that the CPU cache handles well. In practice ArrayList often wins even for middle insertions at moderate sizes, and ArrayDeque beats LinkedList as a queue or stack.
- Common pitfalls: neither class is synchronised. Their iterators are fail-fast and throw ConcurrentModificationException if the list is structurally changed outside the iterator, so use Iterator.remove or removeIf. On a List<Integer>, remove(1) removes index 1 while remove(Integer.valueOf(1)) removes the value.
- Summarise the choice: use ArrayList for random access and append-heavy work, LinkedList only when you insert or remove through an iterator you already hold, and ArrayDeque for FIFO or LIFO access.
Follow-up
- What does amortised O(1) mean for ArrayList.add, and why does pre-sizing the list help?
- Which collection would you use for a queue, and why not LinkedList?
- How do you safely remove elements from a list while iterating over it?
Write a recursive algorithm to solve a tree or array traversal problem, and explain how you would optimize it
Medium · Data Structures, Algorithms, and Live Co
Write a recursive algorithm to solve a tree or array traversal problem, and explain how you would optimize it using dynamic programming or memoization.
Approach
- Pick one concrete problem and write it. On a tree, take maximum depth or root-to-leaf path sum: the base case returns 0 for null, then the function recurses left and right. It runs in O(n) time and O(h) stack space, and a skewed tree makes h equal to n, which risks a StackOverflowError in Java (convert to an explicit stack or BFS).
- On an array, take climbing stairs or house robber: naive recursion f(i) = f(i - 1) + f(i - 2) recomputes the same subproblems and grows exponentially (about 1.6^n calls).
- Memoise top-down: cache f(i) in an array or HashMap keyed by the state, so each state is solved once. Time becomes O(number of states × work per state), which is O(n) here, with O(n) space.
- Convert to bottom-up tabulation: fill dp[i] from smaller indices in order, then cut space to O(1) by keeping only the last two values. Bottom-up also removes the recursion depth limit.
- Say when DP applies: it needs overlapping subproblems and optimal substructure. Plain tree traversals such as max depth visit each node once, so memoisation adds nothing. For a tree problem with choices (house robber on a tree), return a pair (best if robbed, best if skipped) from each node instead of keeping a memo map. Before coding, define the state, the recurrence, the base cases and the evaluation order.
Follow-up
- Rewrite your recursive solution iteratively. What does that change in space use?
- For counting grid paths, what is the memo key and what is the complexity?
- How do you recover the actual choices (the path), not just the optimal value?
How do custom object comparisons work in data structures like HashMap or HashSet, and what happens when two di
Medium · Data Structures, Algorithms, and Live Co
How do custom object comparisons work in data structures like HashMap or HashSet, and what happens when two distinct keys produce a hash collision?
Approach
- Walk through a HashMap lookup: compute key.hashCode(), spread it with h ^ (h >>> 16), take the bucket index as (n - 1) & hash (the table size n is a power of two), then within the bucket compare the hashes and then call == or equals(). HashSet is a HashMap whose elements are the keys.
- State the equals and hashCode contract: equal objects must return equal hash codes, and unequal objects may collide. Override both over the same fields (Objects.equals, Objects.hash, or a record). If you override only equals, two equal keys land in different buckets, so the set holds duplicates and get returns null.
- Collision handling in Java 8+: entries in one bucket form a linked list. When a bucket grows beyond 8 entries (TREEIFY_THRESHOLD) and the table has at least 64 buckets, it becomes a red-black tree, so a bad bucket costs O(log n) instead of O(n). If the table is smaller, the map resizes instead, and a tree shrinking to 6 entries turns back into a list.
- Resizing: the default capacity is 16 and the load factor 0.75. When size exceeds capacity × load factor, the table doubles and each bucket splits into entries that stay at index i and entries that move to i + oldCapacity. Each resize is O(n) but puts stay amortised O(1), and pre-sizing avoids repeated resizes when the count is known.
- Mutable keys: if a field used in hashCode changes after insertion, the entry sits in the wrong bucket and can no longer be found, so prefer immutable keys such as String or records. TreeMap and TreeSet use compareTo or a Comparator instead of equals, so that comparison should be consistent with equals.
Follow-up
- How does HashMap handle a null key, and why does ConcurrentHashMap reject it?
- What goes wrong when two threads put into a plain HashMap at the same time?
- How do you write equals for a subclass that adds a field without breaking symmetry?
Explain the key differences between SQL and NoSQL databases, and write a query to find missing elements or cal
Medium · Core Java, Spring Boot, and Back-End Arc
Explain the key differences between SQL and NoSQL databases, and write a query to find missing elements or calculate aggregations across joined tables.
Approach
- Contrast the models: relational databases have a fixed schema, joins, ACID transactions and SQL. NoSQL covers document (MongoDB), key-value (Redis, DynamoDB), wide-column (Cassandra) and graph (Neo4j) stores, usually modelled around access patterns with denormalised data, partitioned horizontally, and often with tunable or eventual consistency. Choose by query patterns, relationships and consistency needs.
- Find missing rows with an anti-join: SELECT c.id FROM customers c LEFT JOIN orders o ON o.customer_id = c.id WHERE o.id IS NULL, or use NOT EXISTS. Avoid NOT IN against a column that can hold NULL: one NULL in the subquery makes the predicate unknown for every row, so nothing comes back.
- Aggregate across joins from the right grain: SELECT c.id, c.name, COUNT(o.id) AS order_count, COALESCE(SUM(o.amount), 0) AS revenue FROM customers c LEFT JOIN orders o ON o.customer_id = c.id GROUP BY c.id, c.name. COUNT(o.id) gives 0 for customers with no orders, where COUNT(*) would give 1.
- Guard against fan-out: joining two one-to-many tables (orders and payments) to the same parent multiplies rows and inflates sums, so aggregate each in its own CTE first and then join the results. Put filters on the right-hand table in the ON clause, because in WHERE they quietly turn the LEFT JOIN into an inner join. Use HAVING for conditions on aggregates.
- For gaps in a numeric sequence, use LEAD(id) OVER (ORDER BY id) and keep rows where the next id minus id is greater than 1, or in PostgreSQL LEFT JOIN against generate_series(min, max). Say which index supports each query (orders.customer_id for the join).
Follow-up
- Why does NOT IN return no rows when the subquery contains a NULL?
- How would you return the top two orders per customer by amount?
- Which index makes the anti-join fast on a large orders table, and how would you confirm it is used?
Write the update path that detects a concurrent edit
Medium · optimistic concurrency · transactional outbox · audit history
resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.
Approach
- One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
- Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
- State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
- Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.
Follow-up
- A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
- Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?
What are the major feature additions in Java 8, and how do functional interfaces, Lambda expressions, and the
Medium · Core Java, Spring Boot, and Back-End Arc
What are the major feature additions in Java 8, and how do functional interfaces, Lambda expressions, and the Streams API alter collection processing?
Approach
- List the Java 8 additions by name: lambda expressions, functional interfaces with java.util.function (Function, Predicate, Supplier, Consumer, BiFunction), method references, default and static interface methods, the Streams API, Optional, the java.time date API and CompletableFuture.
- Define a functional interface as one with exactly one abstract method (default methods and public Object methods do not count); a lambda is an instance of it, and any local variable it captures must be effectively final.
- Explain the stream pipeline: a source, lazy intermediate operations (filter, map, flatMap, sorted, distinct) and one terminal operation (collect, reduce, forEach, count). Nothing runs until the terminal call, short-circuiting operations like findFirst, anyMatch and limit stop early, the source is never modified, and a stream can be consumed only once.
- Show the change on a real task: a loop that groups orders by customer becomes orders.stream().filter(o -> o.getTotal() > 100).collect(Collectors.groupingBy(Order::getCustomerId, Collectors.counting())). The code says what to compute rather than how to iterate.
- Give the trade-offs: parallelStream runs on the common ForkJoinPool and only pays off for large, CPU-bound, easily split sources such as arrays or ArrayList, not LinkedList or blocking I/O. Side effects inside lambdas break that, and Collectors.toMap throws IllegalStateException on duplicate keys unless you pass a merge function.
Follow-up
- What is the difference between map and flatMap, and when do you need flatMap?
- Why does a pipeline that ends in peek and has no terminal operation print nothing?
- Where is Optional a good return type, and where is it misused?
Explain the internal execution flow of a Spring Boot application, including auto-configuration, dependency inj
Medium · Core Java, Spring Boot, and Back-End Arc
Explain the internal execution flow of a Spring Boot application, including auto-configuration, dependency injection, and annotations like @RestController and @Autowired.
Approach
- Trace startup from main: SpringApplication.run picks the application type (a servlet web app when Spring MVC is on the classpath), prepares the Environment from application.properties or yml and the active profiles, creates and refreshes the ApplicationContext, starts the embedded server (Tomcat by default) and then calls any CommandLineRunner or ApplicationRunner beans.
- Break @SpringBootApplication into @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan. The scan starts at the package of the main class, so beans outside that package tree are not found.
- Explain auto-configuration: Boot 2.7+ lists candidate classes in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (spring.factories before that), and each applies only if its conditions pass (@ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty). Defining your own DataSource bean makes the default back off, and running with --debug prints the conditions report.
- Cover dependency injection: beans are singletons by default, @Autowired resolves by type, and @Qualifier or @Primary settles ambiguity. Prefer constructor injection (final fields, easy to test, and since Spring 4.3 a single constructor needs no annotation). BeanPostProcessors wrap beans in proxies, which is how @Transactional and @Async work.
- Follow one request: DispatcherServlet asks the HandlerMapping for the matching controller method, binds @PathVariable and @RequestBody arguments, calls the method and, because @RestController is @Controller plus @ResponseBody, serialises the return value through an HttpMessageConverter (Jackson for JSON).
Follow-up
- Why does @Transactional do nothing when one method calls another method in the same class?
- How do you exclude or override an auto-configuration you do not want?
- What happens at startup when two beans depend on each other through their constructors?
How do you handle transaction management across distributed microservices using patterns like Saga or two-phas
Medium · Core Java, Spring Boot, and Back-End Arc
How do you handle transaction management across distributed microservices using patterns like Saga or two-phase commit?
Approach
- Start with the constraint: each service owns its own database, so a local @Transactional cannot span services, and calling a REST API and then writing locally is a dual write that can half-succeed.
- Explain two-phase commit: a coordinator asks each participant to prepare (vote and hold locks), then sends commit or abort. It gives atomicity, but it blocks: if the coordinator fails after prepare, participants sit in doubt holding locks. Every resource must support XA, which rules out most brokers, REST services and many NoSQL stores.
- Explain a saga as a chain of local transactions, each with a compensating action. Example: create the order as PENDING, charge the payment, then reserve stock; if the reservation fails, refund and then cancel the order. Choreography passes events between services, while orchestration uses a central state machine that is easier to trace when there are many steps.
- Make it reliable: a transactional outbox writes the event in the same local transaction as the business row, and a relay publishes it. Consumers are idempotent (they dedupe on a message or business key) because delivery is at-least-once. Compensations must be idempotent and retryable, and steps that cannot be undone (sending an email) go last.
- State the trade-off: sagas give eventual consistency with no isolation, so other requests can see intermediate states. Counter that with status flags (semantic locks such as PENDING). Two-phase commit fits a few XA-capable resources that need strict atomicity, and sagas fit long-running business flows across services.
Follow-up
- What do you do when a compensating transaction itself fails?
- How do you stop another request from selling stock that a saga has reserved but not yet confirmed?
- With six services in the flow, would you use choreography or orchestration, and why?
What are the differences between synchronous REST API endpoints and asynchronous event-driven messaging system
Medium · Core Java, Spring Boot, and Back-End Arc
What are the differences between synchronous REST API endpoints and asynchronous event-driven messaging systems using Apache Kafka or AMQP?
Approach
- Synchronous REST: the caller blocks for the answer and gets errors straight away as status codes. Both sides must be up at the same moment, and latency and failure add up along a chain of calls. It fits queries and any step where the caller needs the result before it can continue.
- Asynchronous messaging: the producer publishes and moves on, and the broker buffers messages. That separates the services' availability, absorbs bursts and lets new consumers subscribe without changing the producer. The costs are eventual consistency, harder tracing (carry a correlation id) and the need to report results some other way.
- Kafka versus AMQP brokers such as RabbitMQ: Kafka is a partitioned, replicated append-only log. Messages are kept for a retention period, consumers track offsets and can replay, ordering holds only within a partition (so key by entity id), and each partition goes to one consumer in a group. RabbitMQ routes through exchanges (direct, topic, fanout, headers) to queues and removes a message once it is acknowledged.
- Delivery semantics: assume at-least-once, so consumers must be idempotent. Send poison messages to a dead-letter queue after bounded retries. Kafka's idempotent producer and transactions give exactly-once only for read-process-write flows that stay inside Kafka.
- Give a concrete split: POST /orders validates the request, stores the order, returns 202 Accepted with an order id and publishes OrderPlaced. Inventory, notification and analytics consume that event, while order status is still read synchronously over REST.
Follow-up
- How do you guarantee that one customer's events are processed in order?
- A consumer crashes after processing a message but before committing its offset. What happens, and how do you make that safe?
- How does the client find out whether its asynchronous request succeeded?
How does event bubbling and delegation work in modern JavaScript, and what are the key differences between var
Medium · JavaScript, React, and Front-End Enginee
How does event bubbling and delegation work in modern JavaScript, and what are the key differences between var, let, and const regarding hoisting?
Approach
- Describe the three event phases: capture (from window down to the target), target, then bubble (back up). addEventListener(type, handler, { capture: true }) listens during capture. event.target is the element where the event started and event.currentTarget is the element whose listener is running.
- Separate stopPropagation (halts further propagation) from preventDefault (cancels the browser's default action, such as following a link or submitting a form). Note that focus, blur, mouseenter and mouseleave do not bubble; focusin and focusout do.
- Delegation: put one listener on a stable parent and resolve the item with event.target.closest('li[data-id]'), checking that the parent contains the match. It covers children added later and avoids one listener per row, but it breaks if a child calls stopPropagation.
- Hoisting: var is function-scoped, hoisted and initialised to undefined, can be redeclared, and at the top level of a script becomes a property of window. let and const are block-scoped and hoisted without being initialised, so reading them before the declaration throws a ReferenceError (the temporal dead zone). const needs an initialiser and blocks reassignment, not mutation of the object.
- Show the classic bug: for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)) prints 3, 3, 3 because every callback shares one binding, while let creates a fresh binding per iteration and prints 0, 1, 2. Function declarations are fully hoisted, but a function expression assigned to var is undefined until assigned, so calling it early throws a TypeError.
Follow-up
- What does typeof return for a let variable read inside its temporal dead zone, and for a name that was never declared?
- When would you attach a listener in the capture phase instead of the bubble phase?
- How would you delegate clicks for a list whose items contain nested buttons and icons?
Describe the component lifecycle in React, and demonstrate how hooks like useEffect, useMemo, and `useCall
Medium · JavaScript, React, and Front-End Enginee
Describe the component lifecycle in React, and demonstrate how hooks like useEffect, useMemo, and useCallback optimize rendering performance.
Approach
- Map the lifecycle: mount (class: constructor, render, componentDidMount), update (render, componentDidUpdate) and unmount (componentWillUnmount). In a function component, render is the function call. useEffect with [] runs after mount and its cleanup runs on unmount. With dependencies it runs again after any render where a dependency changed (compared with Object.is), and the previous cleanup runs first.
- Place useEffect correctly: it is for syncing with outside systems (fetching, subscriptions, timers), not an optimisation. It runs after paint, while useLayoutEffect runs before paint for DOM measurements. A missing dependency gives stale closures, and setting state that is also a dependency causes a render loop.
- Explain what causes a re-render: a state change, a parent re-render or a context value change. React.memo skips re-rendering a child when its props are shallowly equal.
- useMemo caches a computed value until its dependencies change, which helps for an expensive derivation or for keeping an object prop's identity stable. useCallback(fn, deps) is useMemo(() => fn, deps) and keeps a function's identity stable, which only matters when the receiver is a React.memo child or an effect that depends on the function.
- Measure before memoising: the React DevTools Profiler shows which components re-render and why, and memoisation has its own comparison and memory cost. Also note that React 18 Strict Mode mounts, unmounts and remounts in development, so effects run twice and their cleanup must be correct.
Follow-up
- Why does an effect run twice in development, and what does that tell you about its cleanup?
- If a fetch in useEffect depends on an id that changes quickly, how do you stop an old response overwriting a newer one?
- Give a case where adding useMemo makes things slower or changes nothing.
How do you implement state management using Redux Toolkit or the Context API, and when should you choose one o
Medium · JavaScript, React, and Front-End Enginee
How do you implement state management using Redux Toolkit or the Context API, and when should you choose one over the other?
Approach
- Context is a way to pass a value down the tree, not a state manager in itself. Pair it with useState or useReducer in a provider. Every consumer re-renders when the provider's value changes by reference, and there are no selectors, so it suits values that change rarely: theme, locale, the signed-in user.
- Redux Toolkit setup: configureStore (DevTools and thunk middleware are on by default), createSlice for a cartSlice with addItem and removeItem reducers written in mutating style (Immer turns that into immutable updates), the Provider at the root, then useDispatch(addItem(product)) and useSelector(state => state.cart.items).
- Explain why Redux scales for frequently changing state: useSelector re-renders a component only when its selected value changes (strict === by default). createAsyncThunk models pending, fulfilled and rejected states, and RTK Query handles caching and refetching of server data.
- State the choice: Context for low-frequency shared values and small apps. Redux Toolkit when many components read and write shared state that changes often, when transitions are complex, or when middleware and time-travel debugging help. Data fetched from the server is usually better in RTK Query or React Query than in either.
- If you stay on Context, cut re-renders by splitting it into several contexts, memoising the provider value with useMemo, and putting state and dispatch in separate contexts so components that only dispatch do not re-render.
Follow-up
- Why does a component re-render when an unrelated field in the same context changes?
- A useSelector that returns a filtered array re-renders on every store update. How do you fix it?
- Where do API calls and their loading and error states live in a Redux Toolkit app?
Walk through how you would construct a functional dynamic user interface to fetch product data from a RESTful
Medium · JavaScript, React, and Front-End Enginee
Walk through how you would construct a functional dynamic user interface to fetch product data from a RESTful API and display it with filtering and pagination in React and TypeScript.
Approach
- Define the types first: interface Product { id: string; name: string; category: string; price: number } and a generic Page<T> { items: T[]; total: number; page: number; pageSize: number }. State the assumption that the API filters and paginates on the server through query parameters.
- Model state explicitly: filters (category, search text, price range), the current page, and the request as a discriminated union of idle, loading, success (with data) and error (with message). Mirror filters and page in the URL query string so refresh, sharing and the back button work.
- Fetch in a custom useProducts(filters, page) hook. Call fetch inside useEffect with an AbortController that is aborted in the cleanup, so a stale response cannot overwrite a newer one. Debounce the search input, reset to page 1 when a filter changes, and check res.ok, because fetch does not reject on 4xx or 5xx responses.
- Render every state: a loading skeleton, an error with a retry button, an empty-results message, and a list keyed by product.id. Compute totalPages as Math.ceil(total / pageSize) and disable Previous and Next at the bounds.
- Name the trade-offs: client-side filtering is fine for a small catalogue fetched once, and server-side scales. Cursor pagination stays stable under inserts, while offset pagination is simpler and allows jumping to a page. React Query or RTK Query adds caching and keeps the previous page on screen while the next one loads. Test with React Testing Library, mocking the API with MSW.
Follow-up
- How do you make sure a slow response for an old filter never replaces the results for the current one?
- How would you change this to infinite scroll?
- Where should the filter state live so a page refresh keeps it?
Walk me through the technical architecture of your current or most recent project, detailing your specific con
Medium · Behavioral and Project Delivery
Walk me through the technical architecture of your current or most recent project, detailing your specific contributions and technology decisions.
Approach
- Open with one sentence on what the system does and who uses it, then walk one real request end to end: client, gateway or load balancer, the services involved, the data stores and any asynchronous consumers. Name the actual frameworks and databases instead of saying "a microservice".
- Separate what the team built from what you built: "I wrote the payment reconciliation service", "I designed the order schema". Keep it consistent with your CV, because every component you claim invites a deeper question.
- Pick two decisions you made or influenced and give the alternative you rejected and the reason, for example a Kafka topic instead of a synchronous call for notifications, or PostgreSQL instead of a document store because the data was relational. Give one number where you have it: request volume, data size, or a latency or cost change.
- Cover the operational side briefly: how it is built and deployed (containers, Kubernetes, the CI pipeline in Jenkins or GitLab), how failures are seen (logs, metrics, tracing), and how authentication and secrets are handled.
- Close with one limitation or incident and what you would change now. If the client's details are confidential, describe the domain and scale in general terms and keep the technical substance.
Follow-up
- What happens to a user request when your main downstream dependency is down?
- Why that database, and what would make you change it?
- What would break first if traffic grew tenfold, and what would you change?
One customer endpoint stalls deliveries to every other destination
Medium · bulkhead · timeouts · saturation
The egress service delivers about 1.5k webhooks/second across 40,000 destinations, with a per-destination concurrency cap of 4 and a 10-second connect-plus-read timeout. Throughput falls to 300/second, queue depth climbs, and p99 delivery latency for unaffected destinations goes from 200 ms to minutes, while the error rate barely moves. One tenant holds 900 destination rows whose URLs share a hostname that now answers in 9.5 seconds. Explain the mechanism with the arithmetic, then give the containment in the order you would apply it.
Approach
- Look at saturation before errors. A flat error rate with collapsing throughput says nothing is failing, things are waiting, so the first signal to pull is in-flight request count or pool wait time rather than the error counter. This is the distinction that decides the whole investigation.
- Group in-flight work by resolved host, not by destination id. The cap is keyed per destination row, so 900 rows sharing one hostname buy 3,600 concurrent slots against a single host, each held for 9.5 seconds. The bulkhead was never a bulkhead for that host, and grouping by the wrong dimension is why the dashboard looked healthy.
- Do the arithmetic in both directions. Required concurrency is arrival rate times latency, so 1.5k/second at 200 ms needs about 300 in flight, which is entirely consumed by 3,600 slow slots; conversely whatever concurrency is left sustains rate equals concurrency divided by 9.5 seconds, which is the 300/second you are seeing. Matching both numbers is what promotes this from a plausible story to the mechanism.
- Explain why the circuit breaker never helped. It opens on consecutive failures, and a 9.5-second response inside a 10-second timeout is a success. Slow is not failing, so an error-rate breaker cannot see this; you need a slow-call ratio, a deadline propagated from the caller's remaining budget, or a concurrency limiter.
Follow-up
- The host recovers to 80 ms. How long does the queue take to drain, and what does the drain do to the recovered host?
- Where should the 10-second timeout number actually come from?
Suggested practice checklist
A seven-day plan built on the three reported rounds and the four question groups candidates describe, moving from the project walkthrough through Java, coding, Spring and messaging, SQL and React to a behavioral rehearsal.
Day 1 — Map the loop and your project
- Write the reported sequence (Recruiter Screen, Technical Rounds, Leadership or Client Interview) and mark which stack on your résumé each stage is likely to test.
- Draw the architecture of your most recent project and list your contributions and technology decisions.
Deliverable: A loop map and a one-page architecture diagram with your contributions and three technology decisions marked.
Day 2 — Java 8 and collection internals
- Rewrite five loop-based methods as stream pipelines using filter, map, collect and groupingBy, and note where lazy evaluation matters.
- Write a class with equals and hashCode, put instances in a HashSet, and break the contract on purpose to see the failure.
- Explain ArrayList and LinkedList costs for lookup, insertion and deletion, including why LinkedList rarely wins in practice.
Deliverable: Stream rewrites, a working equals/hashCode demo and a written comparison table of ArrayList and LinkedList.
Day 3 — Live coding in a plain editor
- Solve missing and duplicate elements in an array in O(n) with both a hash set and an in-place marking approach.
- Write run-length encoding and an anagram check, covering empty input and mixed case.
- Write one recursive traversal, then add memoization and state the new complexity.
Deliverable: Three solutions written without autocomplete, each with stated time and space complexity and an edge-case list.
Day 4 — Spring Boot, microservices and messaging
- Explain aloud what happens from the main method to a handled @RestController request, covering auto-configuration and dependency injection.
- Sketch a Saga for an order across three services with compensating steps, and note where two-phase commit would block.
- Compare a synchronous REST call with a Kafka consumer, covering ordering, retries and idempotency.
Deliverable: A spoken explanation of each topic and one Saga diagram with failure paths.
Day 5 — SQL and databases
- Write queries for missing rows with LEFT JOIN and IS NULL, and a grouped aggregate across a join that does not double count.
- Write a subquery version of each and compare it with the join form.
- Prepare a short answer on SQL versus NoSQL for a project you know, then work the concurrent-edit practice question.
Deliverable: A saved set of SQL queries with explanations, and a SQL versus NoSQL answer tied to a real project.
Day 6 — JavaScript, React and TypeScript
- Explain event bubbling and delegation with a small example, and the hoisting differences between var, let and const.
- Build a product list that fetches from a REST endpoint with loading and error states, filtering and pagination in React and TypeScript.
- Explain deep versus shallow copy, immutability patterns, and when Redux Toolkit beats Context.
Deliverable: A working React and TypeScript list component and spoken answers on hooks, state management and copying.
Day 7 — Behavioral and client-round rehearsal
- Write STAR outlines for the production incident, the shifting-requirements and the design-disagreement prompts.
- Rehearse the architecture walkthrough from Day 1 aloud, including security points.
- Run a mock client round: clarify goals first, then talk through a small design.
Deliverable: Three STAR outlines, a rehearsed walkthrough and one recorded mock client conversation.
Project delivery and client stories
The final stage is reported as behavioral and architectural, covering teamwork, Agile practice and security compliance, and for project roles it takes place with client engineers. Prepare stories where you made a decision, stayed clear with stakeholders and can name what you would change.
- Walk me through the technical architecture of your current or most recent project, detailing your specific contributions and technology decisions.
- Tell me about a time you explained a technical trade-off to a non-technical or client stakeholder.
Describe a challenging production bug or bottleneck you encountered in a recent enterprise project and how you
Medium · Behavioral and Project Delivery
Describe a challenging production bug or bottleneck you encountered in a recent enterprise project and how you diagnosed and resolved it.
Approach
- Choose an incident you personally diagnosed in an enterprise project, and open with the symptom and how it surfaced, such as an alert, a slow endpoint or a client complaint, plus who was affected.
- Walk through your diagnostic sequence in order: recent deploys, logs and traces, metrics, then the specific tool, for example a thread dump, GC log, slow-query log or EXPLAIN plan, and the hypothesis each step ruled in or out.
- State the root cause precisely, for example a missing index, N+1 queries from lazy loading, an exhausted connection pool or an unbounded cache, and what you changed.
- Prove the fix with a before-and-after measurement, and close with the prevention you added: a regression test, an alert or a runbook entry.
- The mistake that sinks it is a vague story with no numbers, or one where the team fixed it and your part is unclear. Use I for your actions.
Follow-up
- How did you confirm the fix worked rather than traffic simply dropping?
- What would you have monitored to catch this earlier?
- How did you communicate the incident to the client or stakeholders while it was open?
How do you manage shifting project requirements or tight client deadlines within an Agile/Scrum development en
Medium · Behavioral and Project Delivery
How do you manage shifting project requirements or tight client deadlines within an Agile/Scrum development environment?
Approach
- Pick one concrete story: a requirement changed mid-sprint or a client moved a date. Say what changed, when you learned it and what was already built.
- Describe how you sized the impact: re-estimated the remaining work, listed what depended on it, and brought the product owner or client options such as trimming scope, shipping a thin slice behind a feature flag, or moving the date.
- Show how you kept quality: tests and review stayed in place, and risk was recorded openly rather than absorbed silently.
- Cover the Agile mechanics you used, such as backlog refinement, sprint review and a retrospective action that reduced repeat changes.
- The mistake that sinks it is a heroics story where you worked longer and said nothing about the scope trade-off, or one that blames the client.
Follow-up
- What did you drop or defer, and who agreed to it?
- How would you handle it if the client insisted on all the scope by the same date?
- What did you change afterwards so late changes hurt less?
How do you handle technical disagreements with senior developers or client engineering stakeholders regarding
Medium · Behavioral and Project Delivery
How do you handle technical disagreements with senior developers or client engineering stakeholders regarding solution design?
Approach
- Set up the disagreement fairly: the design decision, the other person's position and why it was reasonable, then your position.
- Show you used evidence rather than seniority: a small prototype, a benchmark, a table of trade-offs covering cost, risk and maintenance, or a written decision record.
- With a client stakeholder, describe asking about their goals and constraints first, since the disagreement often disappears once the real constraint is clear.
- State how it was decided and what you did next: if you lost, you carried out the decision fully and flagged the risk you still saw; if you won, you gave credit and checked the result.
- The mistake that sinks it is a story where you were simply right, or one where you quietly gave in and never raised the risk.
Follow-up
- What if the senior developer still disagreed after seeing your data?
- Tell me about a time you were wrong in a disagreement like this.
- How would you handle it differently if the client's contract limited the design options?