cswork / libraryOpen practice workspace

Interview guides · Aquinas Consulting

Aquinas Consulting Software Engineer Interview Guide

Prepare for the Aquinas Consulting Software Engineer interview: four reported rounds, Java and back-end questions, coding answers and a 7-day plan.

The 60-second brief

Aquinas Consulting is a consulting firm, and the Software Engineer role candidates describe is client-facing delivery work: designing, building and maintaining software systems that client organisations run on. Reported projects range from building applications meant to scale to fitting newer technologies into systems that already exist, and engineers work alongside product managers and UX designers. Day to day, candidates list writing and shipping application code, taking part in code reviews, and tracking down defects and performance problems. The skills candidates report as required are Java and Python, data structures and algorithms, web basics (HTML, CSS, JavaScript) and both SQL and NoSQL databases, with AWS or Azure, DevOps tooling and mobile development listed as extras. Spring and React come up as example frameworks.

This guide is for candidates interviewing for a Software Engineer position at Aquinas Consulting, especially those whose background is back-end Java. It covers the four reported rounds (Technical Screening, Behavioral Assessment, Coding Challenge, Final Interviews), gives question-specific guidance for every reported question, from abstract classes versus interfaces through REST design, scalability, debugging, linked lists, binary search, dynamic programming and sorting, and lays out a seven-day plan with a concrete output each day.

Candidates report four rounds over roughly three to five weeks: a Technical Screening, a Behavioral Assessment, a Coding Challenge and Final Interviews that can mix further technical and behavioral questions.

Focus areas

  • Java
  • Technical screening
  • Back-end development

Overview

The questions candidates report for this loop are mostly foundations: Java language semantics (abstract classes versus interfaces), object-oriented design, REST API principles, dependency management, and classic algorithm exercises such as reversing a linked list, binary search, a dynamic programming problem and writing a sort from scratch. The topic tags candidates attach are Java, technical screening and back-end development. None of this is exotic, so the bar is precision: the Java 8+ rules for interfaces, the overflow-safe midpoint in binary search, the correct HTTP method semantics, a complexity argument you can defend line by line.

Because the role is consulting delivery work with product managers and UX designers, a large share of the reported questions are about how you work rather than what you know: prioritising under a tight deadline, handling ambiguous requirements, persuading others, working with a difficult teammate, taking feedback. Prepare these as real stories with your own decisions and measurable outcomes, not as statements of principle. Several reported questions sit between the two, such as describing a time you optimised code or a complex problem you resolved, and they need both a technical core and a clear STAR structure.

For the back-end side, prepare in Java first, since that is what candidates tag, and keep Python ready as the second listed language. Know one framework well enough to explain it; Spring is the one candidates mention, and a reported example question asks for its key features, so be able to explain inversion of control, dependency injection, Spring Boot auto-configuration and how a REST controller maps requests. Because SQL and NoSQL both appear in the requirements, your scalability answer should be able to say when each fits and how you would index, cache and replicate.

The plan below spends three days on Java, back-end and scalability material, two on coding exercises, one on debugging and problem-solving cases, and one on behavioral stories plus a full mixed rehearsal for the Final Interviews.

The Aquinas Consulting interview process

Candidates describe four rounds, in this order, over roughly three to five weeks. The descriptions below report what candidates say each stage covers, followed by how to prepare for it.

Technical Screening

Candidates describe the Technical Screening as a first check of the technical skills and knowledge the Software Engineer role needs, and the topic tags they attach to this loop are Java, technical screening and back-end development. The reported technical questions of this type are conceptual: abstract classes versus interfaces, OOP principles in design, REST API principles and dependency management. Approach it by giving short, exact answers first (a definition plus the rule that distinguishes the two options), then one concrete example from code you have written, then the trade-off. Avoid reciting textbook lists without an example; the strongest answers show when you would pick one option over another.

What to demonstrate

  • Java language knowledge, the topic candidates tag for this loop
  • Command of object-oriented design concepts and when to apply them
  • Ability to explain back-end concepts such as API design and dependency management clearly

How to prepare

  • Write a one-page Java cheat sheet: abstract class versus interface (including default, static and private interface methods), overriding versus overloading, equals/hashCode contract, checked versus unchecked exceptions.
  • Rehearse aloud a REST answer that covers resource naming, method semantics and idempotency, status codes, pagination and versioning, using one API you have built as the example.
  • Prepare a Spring explanation: inversion of control, constructor injection, bean scopes, Boot auto-configuration and how a @RestController handles a request.
  • Be able to describe your Maven or Gradle setup: dependency scopes, how you resolve a transitive version conflict, and how you keep builds reproducible.

Behavioral Assessment

Candidates describe the Behavioral Assessment as an evaluation of interpersonal skills and how well you collaborate inside a team. The reported behavioral questions cover working with a difficult team member, keeping a team motivated during a hard project, persuading others to adopt an idea, handling feedback, and the role you usually take in team projects. Approach it with a bank of five or six real stories from your own work, each with a clear situation, the specific decision you made, what it cost or risked, and an outcome you can state with a number or a concrete result. Speak in the first person singular about your own actions; a story told entirely as what the team did leaves your contribution unclear.

What to demonstrate

  • Interpersonal skills and collaboration within a team, as candidates describe the stage
  • Specific, first-person accounts of past decisions and their outcomes
  • How you handle conflict, disagreement and feedback

How to prepare

  • Write a story bank of six STAR stories and map each to at least one reported behavioral question (difficult teammate, motivation, persuasion, feedback, team role, ambiguity).
  • For every story, write the measurable result and one thing you would do differently, so the reflection is ready when asked.
  • Rehearse each story aloud and cut the situation to two sentences so most of the answer is about your actions.

Coding Challenge

Candidates describe the Coding Challenge as a practical demonstration of coding skill and algorithm understanding. The reported coding questions are classic exercises: reverse a linked list, implement binary search, explain the time complexity of your solution, solve a problem with dynamic programming, and implement a sorting algorithm from scratch. This is the stage type where those questions are practised. Approach each problem by restating inputs and edge cases, stating the approach and its complexity before writing code, writing the code cleanly in Java (or Python), then walking through a small test by hand, including empty input and one-element input.

What to demonstrate

  • Working, correct code for standard data-structure and algorithm problems
  • Choice of algorithm and a defensible time and space complexity analysis
  • Handling of edge cases such as empty input, duplicates and boundary indices

How to prepare

  • Implement iterative and recursive linked list reversal in Java and test on empty, one-node and two-node lists.
  • Write binary search plus its lower-bound variant from memory with an overflow-safe midpoint, and test targets below, above and inside the array.
  • Solve coin change and longest common subsequence bottom-up, stating state, recurrence, base case and complexity for each.
  • Implement merge sort and quicksort from scratch and be ready to compare stability, extra memory and worst case.

Final Interviews

Candidates describe the Final Interviews as broader conversations that may add further technical and behavioral questions on top of what earlier stages covered. Treat it as a mixed round: you may need to move from a scalability or debugging discussion to a behavioral story without warm-up. The reported questions that fit this kind of mixed conversation include ensuring the scalability of a web application, debugging a failing application, prioritising under tight deadlines and describing a complex problem you resolved. Approach it by preparing structured frameworks for the open-ended technical questions and making sure your behavioral stories do not repeat ones you already told earlier in the loop.

What to demonstrate

  • Breadth across technical and behavioral topics in one conversation
  • Structured reasoning on open-ended problems such as scalability and debugging
  • Consistency between your earlier answers and your deeper explanations

How to prepare

  • Rehearse a scalability walkthrough in order: measure, stateless app tier with load balancing, caching, database indexing and replicas, asynchronous queues, then partitioning.
  • Rehearse a debugging sequence on a real incident you handled: reproduce, scope, find what changed, bisect, fix, add a regression test.
  • Keep two spare behavioral stories you have not used in earlier rounds.
  • Prepare questions for your interviewers about client projects, team setup and how engineers work with product managers and UX designers.

Where candidates lose points

Answering the abstract class versus interface question with pre-Java-8 rules, such as 'interfaces cannot have method bodies'.

Learn the current rules: default and static interface methods (Java 8), private interface methods (Java 9). Then make the real distinction about instance state, constructors and single versus multiple inheritance, and give a design example for each.

Losing the rest of the list when reversing a linked list, or writing binary search that loops forever or skips the last element.

Save curr.next before rewiring, and pick one binary search form (closed [lo, hi] or half-open [lo, hi)) and keep its loop condition and updates consistent. Trace every solution by hand on empty, one-element and two-element inputs before saying you are done.

Jumping straight to microservices or sharding when asked how to ensure a web application scales.

Start from measurement and the bottleneck, then move through stateless app servers, caching, database indexing, read replicas and queues before partitioning. Name the trade-off at each step (cache staleness, replica lag).

Giving behavioral answers in the team's voice with no personal decision and no measurable result, for example an optimisation story with no before and after numbers.

For every story in your bank, write the decision you personally made, the measured outcome, and one thing you would change. Practise saying 'I' and naming the number.

Stating a time complexity without justifying it, or ignoring space and hidden costs such as list.contains inside a loop.

Explain the count line by line, give both time and space, and flag library calls that are not constant time. Practise this on each coding exercise in the plan.

Questions & practice

Write a function to reverse a linked list.

Medium · Coding / Algorithms

Approach

  1. Iterative solution with three pointers: prev = null, curr = head; loop while curr != null: save next = curr.next, set curr.next = prev, advance prev = curr, curr = next; return prev. O(n) time, O(1) extra space.
  2. Mention the recursive version: reverse the rest, then head.next.next = head and head.next = null. It is O(n) time but O(n) stack depth, which can overflow the Java stack on very long lists, so prefer iteration in production.
  3. Walk the edge cases aloud: empty list returns null, a single node returns itself, and a two-node list is the smallest case that proves the pointer order is right.
  4. Explain the bug to avoid: overwriting curr.next before saving it loses the rest of the list.

Follow-up

  • Reverse only the nodes between positions m and n.
  • Reverse the list in groups of k nodes.
  • How would you check whether a linked list is a palindrome in O(1) extra space?

How would you implement a binary search algorithm?

Medium · Coding / Algorithms

Approach

  1. State the precondition: the array is sorted. Use lo = 0, hi = n - 1, loop while lo <= hi, mid = lo + (hi - lo) / 2 (avoids int overflow from (lo + hi) / 2 on large arrays), compare and move lo = mid + 1 or hi = mid - 1; return -1 if not found.
  2. Say the invariant: if the target exists it is always within [lo, hi]. Every off-by-one bug breaks that invariant, so check each update against it.
  3. Give the lower-bound variant: half-open [lo, hi) with hi = n, while lo < hi, if a[mid] < target lo = mid + 1 else hi = mid; returns the first index with a[mid] >= target, which handles duplicates and insertion points.
  4. Complexity: O(log n) time, O(1) space iterative. Test: empty array, one element, target smaller than all, larger than all, and duplicates.

Follow-up

  • Find the first and last occurrence of a target in a sorted array with duplicates.
  • Search for a target in a rotated sorted array.
  • Use binary search on the answer, e.g. the minimum ship capacity to deliver all packages within D days.

Explain the time complexity of your solution.

Medium · Coding / Algorithms

Approach

  1. Define the input sizes first (n elements, m queries) and count the dominant operations: sequential steps add, nested loops multiply, a loop that halves the range is O(log n).
  2. For recursion, write the recurrence: T(n) = 2T(n/2) + O(n) is O(n log n) by the master theorem; naive Fibonacci T(n) = T(n-1) + T(n-2) is exponential, which is why memoisation matters.
  3. Give space complexity too: auxiliary structures plus recursion stack depth.
  4. Call out hidden costs in Java: list.contains is O(n), string concatenation in a loop can be O(n^2) (use StringBuilder), HashMap operations are O(1) average but degrade in the worst case (Java 8+ treeifies long buckets to O(log n)), ArrayList.add is amortised O(1).
  5. State best, average and worst cases when they differ, as with quicksort.

Follow-up

  • Can you improve the time complexity, and what would it cost in space?
  • What is the complexity if the input is already sorted?
  • What does amortised O(1) mean for ArrayList.add?

Solve a problem using dynamic programming.

Medium · Coding / Algorithms

Approach

  1. Pick a classic problem and say why DP applies: optimal substructure plus overlapping subproblems. Coin change (minimum coins for an amount) is a clean choice.
  2. Define state, recurrence and base case explicitly: dp[a] = minimum coins to make amount a; dp[0] = 0; dp[a] = min over coins c <= a of dp[a - c] + 1; unreachable amounts stay at infinity and return -1.
  3. Choose top-down (recursion plus memo, easy to write) or bottom-up (a table filled in increasing order, no stack risk) and say why. Coin change is O(amount x coins) time and O(amount) space.
  4. Mention space optimisation for two-dimensional DPs (e.g. longest common subsequence in O(n x m) time reduced to one rolling row) and how to reconstruct the actual solution by storing choices.

Follow-up

  • Count the number of ways to make the amount instead. Why does the loop order (coins outer versus amount outer) change the answer?
  • Reconstruct which coins were used, not just how many.
  • When does a greedy approach give the right answer for coin change, and when does it fail?

Can you implement a sorting algorithm from scratch?

Medium · Coding / Algorithms

Approach

  1. Pick merge sort or quicksort and say why. Merge sort: stable, O(n log n) in all cases, O(n) extra memory. Quicksort: in place, O(n log n) average but O(n^2) worst case on bad pivots, not stable.
  2. Write it cleanly: merge sort as split, recurse, merge with two indices (use <= to keep stability); quicksort with a random or median-of-three pivot to avoid the sorted-input worst case.
  3. Handle the edge cases: empty array, one element, already sorted input, many duplicates (Lomuto partition degrades with heavy duplicates; three-way partitioning fixes it).
  4. Compare with others: insertion sort is O(n^2) but fast on small or nearly sorted data, which is why hybrid sorts use it for small runs; heap sort is O(n log n) in place but not stable; Java sorts primitives with dual-pivot quicksort and objects with stable TimSort.

Follow-up

  • How would you sort a file too large to fit in memory?
  • When does stability matter in a real application?
  • How would you sort a million integers that all lie between 0 and 1,000?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

A tenant on the standard plan may hold at most 50 resources with status='active'. The create handler runs SELECT count(*) FROM resource WHERE tenant_id = $1 AND status = 'active', compares to 50, then inserts. Two creates arrive 3 ms apart on different instances and the tenant lands at 51. Name the anomaly, say whether PostgreSQL 16 READ COMMITTED or REPEATABLE READ prevents it and why, then give an implementation that holds the cap at READ COMMITTED with the exact statements. Finally, say what changes when the cap is 'at most one running export per tenant' on job_run.

Approach

  1. Name it: write skew. The two transactions read an overlapping set and write disjoint rows, so there is no row-level conflict for the engine to detect and each commit is individually legal.
  2. Rule out the levels precisely. READ COMMITTED takes a fresh snapshot per statement and takes no lock on the counted rows, so both see 49. PostgreSQL's REPEATABLE READ is snapshot isolation: it removes non-repeatable reads and phantoms within the snapshot but still admits write skew, because the anomaly is not a re-read of a changed row, it is a read of a set that a concurrent transaction invalidates. Only SERIALIZABLE closes it, by tracking the read dependency and aborting one transaction with SQLSTATE 40001 — a guarantee that exists only if the application re-runs the whole transaction from the read.
  3. Convert the set predicate into a single-row conflict: keep tenant.active_resource_count and run UPDATE tenant SET active_resource_count = active_resource_count + 1 WHERE tenant_id = $1 AND active_resource_count < 50 in the same transaction as the INSERT. Zero affected rows is the cap, returned as 409. The row lock serialises the decision at any isolation level, and contention is bounded to one tenant's row — which is also the fair-scheduling unit, unlike a global counter that would convoy every tenant behind one row.
  4. State the cost you just took on: a counter is a second source of truth that can drift, so every path that changes status must adjust it inside the same transaction, and a periodic reconciliation has to exist, with resource_revision as the authority for what the count should have been.

Follow-up

  • A resource moves from archived back to active. Which statements change, and what breaks if the counter update and the status change land in different transactions?
  • The cap becomes plan-dependent and a plan can change mid-month. Where does the number 50 live, and who reads it?

Find version gaps and relay lag with window functions

Medium · window functions · event tables · frame semantics

outbox_event holds event_id, aggregate_type, aggregate_id, aggregate_version, event_type, payload, status ('pending','published','dead'), attempts, created_at, published_at. A projection is missing rows and you must decide whether the relay skipped events or the consumer dropped them. Write three queries over the last seven days: one listing every aggregate_id whose published aggregate_version sequence has a hole, one giving per-day counts with a running total, and one returning the newest published event per aggregate. For each, say where the window function is evaluated relative to WHERE and LIMIT. PostgreSQL 16.

Approach

  1. Gaps: compute lead(aggregate_version) OVER (PARTITION BY aggregate_id ORDER BY aggregate_version) in a subquery, then filter next_version <> aggregate_version + 1 in the outer query. Window functions are evaluated after WHERE, GROUP BY and HAVING and before the outer ORDER BY and LIMIT, so the predicate cannot sit in the same WHERE clause and PostgreSQL 16 has no QUALIFY.
  2. Say what the seven-day filter does to the answer: it truncates every partition, so the first row per aggregate has no predecessor inside the window and a hole spanning the boundary is invisible. Widen the window, or join to resource.version as the authority for the true maximum.
  3. Running total: SELECT date_trunc('day', created_at) AS d, count() AS n, sum(count()) OVER (ORDER BY date_trunc('day', created_at) ROWS UNBOUNDED PRECEDING). An aggregate inside a window call is legal because grouping runs before windowing. The grouping key is unique per row here so ROWS and RANGE agree, but write the frame anyway — over ungrouped rows with tied timestamps the default RANGE frame pulls in every peer row and the total jumps.
  4. Newest per aggregate: DISTINCT ON (aggregate_id) ... ORDER BY aggregate_id, aggregate_version DESC is the cheap PostgreSQL-only form when an index matches that order; row_number() OVER (PARTITION BY aggregate_id ORDER BY aggregate_version DESC) = 1 is the portable form and needs a subquery for the same evaluation-order reason as the gap query.

Follow-up

  • Relay failover redelivers events. Does a duplicate break the gap query, and how would you detect one from this table alone?
  • Turn the gap check into a continuous monitor rather than a query someone runs after an incident. What does it watch?

What is the difference between an abstract class and an interface in Java?

Medium · Technical / Domain Questions

Approach

  1. Lead with the structural difference: an abstract class can hold instance state, constructors and members of any access level; an interface holds no instance state (only public static final constants), and a class can implement many interfaces but extend only one class.
  2. Update the rules past Java 7: since Java 8 interfaces can have default and static methods, and since Java 9 private methods, so 'interfaces have no method bodies' is no longer true. The real remaining difference is state and constructors.
  3. Give the design rule: use an interface for a capability shared by unrelated types (Comparable, Runnable, a Repository contract); use an abstract class for a family that shares state and partial behaviour, e.g. a template-method base like AbstractList.
  4. Mention API evolution: adding an abstract method to an interface breaks every implementer unless you add it as a default; a functional interface (single abstract method) can be a lambda target, an abstract class cannot.
  5. Close with the diamond case: if two interfaces supply the same default method, the implementing class must override it and can call InterfaceA.super.method().

Follow-up

  • What happens when a class implements two interfaces with the same default method signature?
  • Why can an interface not have a constructor, and how would you share initialisation logic instead?
  • When would you choose composition over either an abstract class or an interface?

Can you explain the concepts of OOP and how they are applied in software design?

Medium · Technical / Domain Questions

Approach

  1. Name the four concepts and give each a one-line definition plus a code example from one domain, e.g. a payment module: encapsulation (balance is private, changed only through methods that enforce rules), abstraction (a PaymentProcessor interface hides provider details), inheritance, and polymorphism (the caller invokes process() and the runtime type decides).
  2. Separate compile-time polymorphism (overloading, resolved by the compiler from argument types) from runtime polymorphism (overriding, resolved by dynamic dispatch on the object's actual class).
  3. Show design judgment: prefer composition over deep inheritance trees, which create fragile base classes; tie in SOLID, especially single responsibility, Liskov substitution and dependency inversion.
  4. Use the Liskov example: a Square extending Rectangle breaks callers that set width and height independently, which shows inheritance must model behaviour, not just an 'is-a' sentence.

Follow-up

  • Give an example where inheritance made a codebase harder to change. How would you refactor it?
  • How does Java decide which overridden method runs at runtime?
  • How does dependency injection relate to dependency inversion?

What are the principles of RESTful API design?

Medium · Technical / Domain Questions

Approach

  1. Resources as nouns with stable URIs (/orders/{id}/items), and HTTP methods used for their semantics: GET safe and idempotent, PUT idempotent full replace, PATCH partial update, DELETE idempotent, POST creates and is not idempotent by default.
  2. Statelessness: each request carries what the server needs (e.g. a token), which allows any instance behind a load balancer to serve it; plus cacheability through Cache-Control and ETag with conditional requests.
  3. Correct status codes: 200, 201 with a Location header, 204, 400 versus 422 for validation, 401 versus 403, 404, 409 for conflicts, 429 for rate limits, 5xx for server faults, and a consistent JSON error body.
  4. Practical concerns: pagination (cursor-based for large or changing sets, offset for simple cases), filtering and sorting via query parameters, versioning (/v1/ path or header), and idempotency keys so a retried POST cannot create a duplicate.
  5. If asked about implementation, map it to Spring: @RestController, @GetMapping/@PostMapping, ResponseEntity for status codes, @ControllerAdvice for uniform error handling.

Follow-up

  • What is the difference between PUT and PATCH, and when is each idempotent?
  • How would you make a payment-creating POST safe for clients to retry after a timeout?
  • How would you introduce a breaking change without breaking existing clients?

How do you manage dependencies in a software project?

Medium · Technical / Domain Questions

Approach

  1. Cover build-level dependencies first: Maven or Gradle in Java (scopes such as compile, test, provided; a BOM or dependencyManagement block to pin versions across modules), pip or Poetry with a lock file in Python, package-lock.json for JavaScript.
  2. Reproducibility: pin versions and commit lock files so every build and environment resolves the same tree; avoid floating version ranges in production code.
  3. Conflicts: inspect the tree with mvn dependency:tree or gradle dependencyInsight, resolve with an explicit version or an exclusion, and run the test suite after any change to a transitive dependency.
  4. Security and upkeep: automated update PRs (e.g. Dependabot), vulnerability scanning (OWASP Dependency-Check, npm audit), and upgrading major versions one at a time behind tests.
  5. If the interviewer means code-level dependencies, switch to dependency injection: constructor injection through Spring's container makes components testable with mocks and keeps coupling to interfaces.

Follow-up

  • Two libraries need incompatible versions of the same transitive dependency. What do you do?
  • How would you upgrade a major framework version across a multi-module project safely?
  • Why is constructor injection usually preferred over field injection?

Describe a complex problem you faced in a previous project and how you resolved it.

Medium · Problem-solving / Case Studies

Approach

  1. Pick a problem that was hard for a stated reason: several interacting causes, unclear ownership across teams, or a constraint such as no downtime. A simple bug fixed in an afternoon does not answer the question.
  2. Use STAR, but give most of the time to your reasoning: how you broke the problem down, what evidence you gathered, the two or three options you considered and why you chose one.
  3. State your own part in the first person; if it was a team effort, say exactly which piece you owned.
  4. End with a measurable result (incident count, latency, delivery date met) and one lesson you now apply.

Follow-up

  • What would you do differently if you faced the same problem again?
  • Which option did you reject, and what would have happened if you had chosen it?
  • How did you keep stakeholders informed while the problem was unresolved?

If given a project with tight deadlines, how would you prioritize tasks?

Medium · Problem-solving / Case Studies

Approach

  1. Clarify which of date, scope and quality is fixed; with a tight deadline the date usually is, so scope becomes the lever.
  2. List the tasks and rank them by impact and risk (MoSCoW or an impact-versus-effort grid), identify the critical path and dependencies on other people, and start the riskiest unknowns first so surprises surface early.
  3. Protect quality on the core path (tests on the parts that would hurt the client most if wrong) and cut or defer nice-to-haves instead.
  4. Communicate the trade-offs early to the product manager or client with a concrete option set, then track progress daily and re-plan if the critical path slips.
  5. Back it with a short real example: what you cut, what you shipped, and whether the date held.

Follow-up

  • A stakeholder insists every feature is a must-have. How do you respond?
  • Halfway through you realise you will miss the date. What do you do and when?
  • Would you ever cut testing to hit a deadline? Where would you draw the line?

What steps would you take to ensure the scalability of a web application?

Medium · Problem-solving / Case Studies

Approach

  1. Measure before changing anything: load test, track p95/p99 latency, throughput and error rate, and find the actual bottleneck (CPU, database, network, a downstream call).
  2. Make the application tier stateless (sessions in a shared store or tokens) so it scales horizontally behind a load balancer with autoscaling, e.g. on AWS or Azure.
  3. Add caching in layers: a CDN for static assets, HTTP cache headers, and an in-memory cache such as Redis for hot reads, with a stated invalidation strategy and acceptable staleness.
  4. Scale the data layer in order: indexes and query fixes, connection pooling, read replicas (accepting replica lag), then partitioning or sharding; choose SQL or NoSQL by access pattern and consistency needs.
  5. Move slow work off the request path with a message queue and background workers, add rate limiting, and keep monitoring so the next bottleneck is visible.

Follow-up

  • What breaks first when traffic grows ten times overnight?
  • How do you handle a single hot key that overwhelms one cache or database node?
  • When would you pick a NoSQL store over a relational database for part of this system?

How would you approach debugging a failing application?

Medium · Problem-solving / Case Studies

Approach

  1. Start by scoping the failure: crash, wrong output, or slowness; all users or a subset; since when. Read the stack trace, logs and error-rate metrics before forming a theory.
  2. Ask what changed: a deploy, configuration, dependency version, data shape or traffic level. If a recent deploy lines up, roll back or use git bisect to locate the commit that introduced it.
  3. Reproduce in the smallest environment possible, then test one hypothesis at a time with a change that rules options in or out either way.
  4. Use the right Java tool for the symptom: a thread dump (jstack) for hangs or deadlocks, a heap dump and GC logs for OutOfMemoryError or memory growth, a profiler for CPU spikes, slow query logs for database latency.
  5. Finish properly: fix the cause, not just the trigger; add a regression test and better logging or alerts; write a short incident note.

Follow-up

  • The bug only appears in production and you cannot reproduce it locally. What next?
  • How would you debug an intermittent failure that happens once a day?
  • How do you tell a real cause from a change that merely coincided with the failure?

Suggested practice checklist

Each day covers one cluster of the reported questions, mapped to the round where that question type comes up, and produces a written or spoken output you can review before the next stage.

Day 1 — Java and OOP foundations for the Technical Screening

  • Write a one-page Java reference covering abstract classes versus interfaces (with Java 8 and 9 interface features), overriding versus overloading, and the equals/hashCode contract.
  • Code a small example of the four OOP concepts in one domain (e.g. a PaymentProcessor interface with two implementations) and note one place composition beats inheritance.
  • Answer both reported Java and OOP questions aloud and record yourself; cut anything that is a definition without an example.

Deliverable: A Java reference page, a small OOP code sample, and two recorded answers on interfaces and OOP.

Day 2 — Back-end: REST APIs, Spring and dependencies

  • Design the REST endpoints for one resource you know (paths, methods, status codes, pagination, error body, versioning) and add an idempotency key to the create call.
  • Write a Spring outline: IoC and constructor injection, bean scopes, Boot auto-configuration and the request path through a @RestController.
  • Run mvn dependency:tree or gradle dependencies on a project you own, find one transitive conflict or outdated library, and write how you would resolve it.

Deliverable: An endpoint table for one resource, a Spring explanation page, and a written dependency-conflict resolution.

Day 3 — Scalability and databases

  • Write a scalability walkthrough for a web application in order: measure, stateless tier, caching, database indexing and replicas, queues, partitioning, with one trade-off per step.
  • Solve the two SQL practice problems on concurrency caps and window-function gap detection to refresh SQL beyond simple joins.
  • Write a short comparison of when you would use a relational store versus a NoSQL store for parts of the same application.

Deliverable: A one-page scalability walkthrough, two solved SQL problems, and a SQL versus NoSQL comparison note.

Day 4 — Coding: linked lists, binary search and complexity

  • Implement linked list reversal iteratively and recursively in Java, and the group-of-k follow-up, testing empty, one-node and two-node lists.
  • Implement binary search and the lower-bound variant from memory, then solve the rotated-array follow-up.
  • For each solution, write the time and space complexity with a one-line justification.

Deliverable: Tested Java implementations of reversal, binary search and lower bound, each annotated with its complexity.

Day 5 — Coding: dynamic programming and sorting

  • Solve coin change (minimum coins and number of ways) and longest common subsequence bottom-up, writing state, recurrence and base case first.
  • Implement merge sort and quicksort from scratch, then test on sorted, reverse-sorted and all-duplicate arrays.
  • Write a comparison table of merge, quick, heap, insertion and counting sort: stability, extra memory, worst case.

Deliverable: Three DP solutions with recurrences, two tested sort implementations, and a sorting comparison table.

Day 6 — Debugging and problem-solving cases

  • Write your debugging sequence using a real incident you handled, including which Java tool (thread dump, heap dump, profiler) fits which symptom.
  • Prepare STAR stories for the code optimisation question and the complex-problem question, each with a before and after number.
  • Rehearse the tight-deadline prioritisation answer with a real example of what you cut and why.

Deliverable: A written debugging sequence and three rehearsed problem-solving stories with measurable outcomes.

Day 7 — Behavioral bank and a full mixed rehearsal

  • Finish a six-story bank covering a difficult teammate, motivation, persuasion, feedback, your team role and ambiguous requirements.
  • Run a mock final with a friend: one Java concept question, one coding problem, the scalability question and two behavioral stories back to back.
  • Write the questions you will ask about client projects and how engineers work with product managers and UX designers.

Deliverable: A six-story behavioral bank, notes from one mixed mock interview, and a list of questions for your interviewers.

Behavioural preparation

Candidates describe the Behavioral Assessment as a look at interpersonal skills and teamwork, and the Final Interviews can add more behavioral questions. For a consulting Software Engineer who works with product managers and UX designers, prepare stories that show your own decisions in conflict, persuasion, feedback and unclear requirements, each with a concrete result.

Describe a time you optimized a piece of code. What was the impact?

Medium · Technical / Domain Questions

Approach

  1. Structure it as STAR with numbers: the symptom (a slow endpoint, a batch job missing its window), the baseline you measured (p95 latency, runtime, CPU, query count), and why it mattered to users or the client.
  2. Show the diagnosis step: the profiler or tool you used (a Java profiler such as JFR or async-profiler, query logs, EXPLAIN plans) and the hotspot it revealed, e.g. an O(n^2) nested loop, an N+1 query pattern, or missing database index.
  3. Describe the change and its trade-off: replaced a list scan with a HashMap lookup, batched queries, added an index or a cache, and what it cost in memory, write speed or code complexity.
  4. Give the measured impact with the same benchmark before and after, and how you made sure nothing regressed (tests, staged rollout). A story without a before/after number is the mistake that sinks this question.

Follow-up

  • How did you know your change caused the improvement and not something else?
  • What did the optimisation cost in readability, memory or maintenance?
  • Was there a simpler fix you considered and rejected?

How do you handle ambiguous requirements in a project?

Medium · Problem-solving / Case Studies

Approach

  1. Tell a real story where the requirement was genuinely unclear, such as a client request with two plausible interpretations.
  2. Show the steps you took: list what was unknown, go to the person who owns the outcome (product manager, client contact) with specific questions, write your assumptions down and get them confirmed.
  3. Reduce risk with a small slice or prototype to get feedback early, and keep the uncertain part of the design easy to change.
  4. Close with the result and what the clarification saved. The mistakes that sink this answer are waiting passively for a perfect spec or building on silent assumptions.

Follow-up

  • What did you do when the stakeholder could not answer your questions either?
  • Tell me about a time an assumption you made turned out wrong.
  • How do you decide when to stop clarifying and start building?

Describe a situation where you had to work with a difficult team member. How did you handle it?

Medium · Behavioral / Leadership

Approach

  1. Describe the behaviour, not the personality: missed reviews, dismissive comments in code review, blocking decisions. Keep the colleague anonymous and fair.
  2. Show what you did first: a private conversation to understand their constraints or concerns, rather than going straight to a manager.
  3. Show the working agreement you reached (clear ownership, review norms, a design doc both signed off) and how the collaboration changed.
  4. Give the outcome and what you changed in your own behaviour. A story where the other person is purely the villain and you changed nothing is the classic failure.

Follow-up

  • What would you have done if the conversation had not helped?
  • What did you learn about your own working style from it?
  • How did the rest of the team experience the situation?

How do you keep your team motivated during challenging projects?

Medium · Behavioral / Leadership

Approach

  1. Be honest about your position: as an individual contributor you influence motivation through actions, not authority. Name a project that was hard for a specific reason (long crunch, repeated scope changes, unclear value).
  2. Describe concrete actions: breaking work into visible milestones, connecting the work to the client outcome, unblocking teammates, recognising others' work publicly, raising scope concerns with the lead.
  3. Give evidence it worked: delivery held, fewer late surprises, a teammate who stayed engaged.
  4. Avoid platitudes such as team lunches as the whole answer; the story must show something you did that changed how the work went.

Follow-up

  • What did you do when motivation dropped because of something outside your control?
  • How do you notice that a teammate is struggling?
  • What would you do differently as the formal lead?

Give an example of a time you had to persuade others to adopt your idea.

Medium · Behavioral / Leadership

Approach

  1. Pick an idea with real resistance: a new library, a refactor, a testing practice, a different architecture. State the objection precisely.
  2. Show how you persuaded with evidence: a small prototype, a benchmark, a cost comparison, or a trial on one module, and how you addressed each stakeholder's specific concern.
  3. Include the compromise or what you conceded; persuasion that ends in a better joint plan is a stronger story than winning outright.
  4. Give the outcome with a result, and mention if part of the idea was rejected and why that was reasonable.

Follow-up

  • Tell me about a time you failed to persuade people. What did you learn?
  • How did you handle the person who still disagreed afterwards?
  • How do you decide whether an idea is worth pushing for?

How do you handle feedback from peers or supervisors?

Medium · Behavioral / Leadership

Approach

  1. Use a specific piece of critical feedback you received, for example code reviews pointing out pull requests that were too large, or a manager noting unclear status updates.
  2. Show your response: you asked clarifying questions instead of defending, agreed on what good would look like, and changed a concrete habit.
  3. Give evidence of the change: smaller PRs merged faster, fewer review rounds, clearer updates acknowledged by the client.
  4. Optionally add how you give feedback to peers. A generic 'I welcome all feedback' with no example, or feedback that was really praise, sinks the answer.

Follow-up

  • Tell me about feedback you disagreed with. What did you do?
  • How do you give difficult feedback to a peer in code review?
  • What is the most useful feedback you have received?

What role do you typically take on in team projects?

Medium · Behavioral / Leadership

Approach

  1. Name the role you actually tend to take (for example the person who writes the technical design and breaks it into tasks, or the one who owns integration and testing) and back it with one concrete project.
  2. Show range: a second example where the team needed something different and you adapted, such as stepping back to support a newer colleague.
  3. Connect it to cross-functional work, since candidates describe the role as working alongside product managers and UX designers: how you translate between their needs and the technical plan.
  4. Avoid claiming 'natural leader' without evidence, or 'whatever is needed' without any specifics.

Follow-up

  • When has your usual role not been what the team needed?
  • How do you work with a product manager when you disagree on scope?
  • Which role do you find hardest, and why?