cswork / libraryOpen practice workspace

Interview guides · American Wholesale Energy

American Wholesale Energy Software Engineer Interview Guide

American Wholesale Energy Software Engineer interview guide: reported Java, class-design and DOM questions, SQL and REST API prep, and a 7-day plan.

The 60-second brief

American Wholesale Energy runs retail and wholesale operations, and candidates describe the Software Engineer role as the one that keeps the digital side of that business working. The systems named for the role manage product data, site performance and the customer-facing digital experience. Day to day, that means building new site functionality, tuning how existing APIs are called, taking part in code reviews, and following work from requirements through deployment into production support. Candidates describe the engineer working alongside QA leads, product owners, senior architects and infrastructure specialists, in systems where older and newer components sit side by side.

This guide is for candidates interviewing for the Software Engineer role at American Wholesale Energy. It covers the four questions candidates report (Java equality, exception handling and resource cleanup, anti-patterns in a class structure, and the parent-child relationship in a DOM tree), the skills the role lists (Java, SQL, REST APIs, writing and debugging algorithms) and the resume deep-dive candidates describe.

Candidates describe an online assessment followed by several technical and behavioral interviews, with panels in some loops. No round names or order are reported, so this guide treats the loop as one preparation block built around the four reported questions.

Focus areas

  • Based on Software Engineer interviews across companies
  • Problem Solving
  • System Design

Overview

The reported questions are fundamentals, not puzzles. Three of the four sit in Java and class design: how == differs from .equals(), how to handle exceptions and release resources such as database statements, and how to spot anti-patterns in a class you are shown and improve them. The fourth asks how parent and child nodes relate in a DOM tree, which is front-end knowledge rather than Java, so the loop is not purely back-end. Each of these has a short textbook answer and a deeper answer with edge cases, and the deeper answer is the one to rehearse.

The requirements candidates describe for the role are Java, SQL, REST APIs and skill at writing and debugging complex algorithms, with CI/CD, quality engineering and high-traffic site experience as extras. The evaluation areas candidates describe include code review (bugs, security problems such as SQL injection, maintainability), choosing data structures, language best practices, concurrency, memory management, API integration, and testing strategy. A code-review style answer has to connect to that list: when you see a class that builds SQL by string concatenation, say injection and name the bind-parameter fix.

Candidates also report that every project on your resume can be examined in detail, including why you made a design choice and how you handled a constraint. Reserve as much time for explaining your own work as for Java drills. Pick the three or four projects you can defend end to end and prepare the alternatives you rejected.

Candidates report being told to lay out a plan before coding, name edge cases and state time and space complexity. They are also warned that platform practice alone leaves gaps, since follow-ups can move to why the code is written that way. Practise answering aloud, since the reported format mixes coding assessments, conceptual discussion and project conversation.

Where to focus your preparation

The reported loop is described only in general terms, so the single step below is a preparation block that covers everything candidates report rather than one named round.

Preparation focus

Candidates describe an initial online assessment, then several technical and behavioral interviews, sometimes with a panel and sometimes remote or in person depending on office and team. They describe a mix of coding assessments, conceptual deep dives and discussion of past projects. No round names, order or lengths are reported, so ask your recruiter which stages you will face and whether you will write code. Use this block to prepare all four reported questions, the code-review skills behind them and a defensible account of your own projects.

What to demonstrate

  • Java fundamentals: object equality, exception handling and releasing resources such as database statements
  • Reading code critically: spotting bugs, SQL injection risks and anti-patterns, then proposing concrete improvements
  • Structured problem solving: stating a plan, edge cases and time and space complexity before and while writing code
  • Your own projects: the reasons behind your design choices and how you handled technical constraints

How to prepare

  • Write short Java snippets that show == against .equals() for Strings, boxed Integers and a class with and without an overridden equals/hashCode, and explain the output
  • Write a JDBC method that runs a transaction: Connection in an outer try-with-resources, a PreparedStatement inside, rollback in an inner catch and auto-commit restored in finally
  • Take a product-data service class with SQL built by concatenation, an inline REST call and swallowed exceptions, and rewrite it, saying which issue is most serious and why
  • For each resume project, write down the design choices, the alternatives you rejected and the constraint that decided between them

Where candidates lose points

Answering the == question with only 'references versus content' and nothing else

Add the cases that make the answer checkable: new String("a") == "a" is false while an interned literal is true; autoboxed or valueOf Integers are guaranteed to share an instance only inside -128 to 127 (the upper bound can be raised with -XX:AutoBoxCacheMax), so outside that range == usually returns false; and a class that overrides equals must override hashCode consistently or HashMap and HashSet misbehave.

Explaining resource cleanup with a catch that prints the stack trace and a manual finally that calls close()

Use try-with-resources, catch specific exceptions, wrap with the cause, and state the suppressed-exception behaviour. Rehearse one JDBC transaction with the Connection in an outer try-with-resources and rollback in an inner catch, so the answer covers cleanup, rollback and injection in one pass.

Naming anti-patterns in the class you are shown but proposing no concrete change, or rewriting the whole class

Rank what you see by risk first (injection, leaked connections, an inline REST call with no timeout, swallowed exceptions, shared mutable state), then give the smallest change that fixes each and say how a test would confirm behaviour is unchanged. Treat style complaints such as naming and class size as lower priority.

Treating the DOM question as trivia and mixing up childNodes and children, or forgetting that whitespace between tags creates text nodes

Be able to state that every attached node except the document has exactly one parent, that siblings share a parent, that childNodes is a live collection that includes text and comment nodes while children holds elements only, and sketch a small tree to show how traversal and event bubbling follow parent links.

Listing technologies on your resume that you cannot explain at depth

Cut or rehearse every item. For each project be ready to say what you built, why you chose that design over the alternatives, what constraint pushed you, and what you would change now, because candidates report that any listed project can be examined in detail.

Questions & practice

Diff a projection against the primary without per-row point reads

Hard · reconciliation · range hashing · throttling

The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.

Approach

  1. Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
  2. Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
  3. For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
  4. Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.

Follow-up

  • The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
  • Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?

Find overlapping job attempts and peak concurrency from lease records

Medium · sweep line · intervals · leases

A day of job_run history yields about 50,000,000 attempt records: (job_run_id, job_type, attempt, started_at, finished_at which is NULL when the worker died, lease_expires_at). Leases expire on a clock, so a job that outran its lease ran twice. Produce (a) every job_run_id whose attempts overlapped in wall-clock time and (b) the peak number of simultaneously running attempts per job_type with the minute it occurred. Target O(n log n). State how you treat a NULL finished_at and what clock skew does to your answer.

Approach

  1. Define the interval before sorting anything: an attempt occupies [started_at, COALESCE(finished_at, lease_expires_at)). finished_at is observed and lease_expires_at is only a promise, so every attempt without a finish contributes an estimate and the whole result is a lower bound on overlap rather than an exact count.
  2. For peak concurrency, sweep: emit 2n endpoints, sort by (timestamp, kind) with ends ordered before starts at equal timestamps, then walk the sequence maintaining a counter per job_type and record each type's maximum with its timestamp. O(n log n) dominated by the sort, O(n) space, or O(1) extra if the sort is external and the walk streams.
  3. For overlap detection, do not compare attempts pairwise. A single global sort by (job_run_id, started_at) gives both the grouping and the order; within a group, keep the maximum end seen so far and report an overlap exactly when the next start is less than that running maximum, which is one linear pass after the sort.
  4. Half-open intervals matter and are easy to get wrong: with closed intervals an attempt ending at the same millisecond another begins reads as concurrency two, and across 50,000,000 records that artefact swamps the real signal.

Follow-up

  • A handler is not idempotent and you have found 400 overlapping jobs. Which of them actually caused damage, and what would you query to find out?
  • Peak concurrency for one job_type is 4 against a configured cap of 4. Is the cap working, or is the data hiding attempts that never started?

Archive a resource graph without breaking live references or recursing

Medium · graph traversal · topological order · tenant isolation

Resources reference other resources within a tenant; for the largest tenant the reference table holds up to 2,000,000 nodes and 8,000,000 edges. Archiving a resource must archive everything reachable from it that nothing outside the set still references, refuse when a live external referrer exists, and terminate when references form cycles, which they legitimately do. Produce the archive order and the refusal list, targeting O(V+E). Say what stops the traversal crossing a tenant boundary, and why recursion is the wrong control structure at this size.

Approach

  1. Load the subgraph with the tenant predicate on both endpoints of the edge, not only on the side you started from. Scoping the left table alone is the classic cross-tenant leak: one mis-entered edge then pulls another tenant's resources into the traversal and, worse, into the archive.
  2. Traverse iteratively with an explicit stack. A 2,000,000-node graph can hold a chain deep enough to exhaust a native stack in the low tens of thousands of frames, and that failure is a process crash rather than an error you can return.
  3. Treat cycles as data rather than corruption: compute strongly connected components with Tarjan in O(V+E) using its own explicit stack, then condense. The condensation is a DAG, so a topological order over it gives the archive order, and every member of a component archives in one transaction because no order within a cycle is valid.
  4. Decide refusals with reverse edges. A candidate is archivable only if every in-edge originates inside the candidate set, so build the transpose or count in-degrees restricted to the visited set, and emit each blocked resource with the id of the external referrer, which is the only part of the answer an operator can act on.

Follow-up

  • The graph is read in one query and the archive writes a minute later. What can change in between, and how do you make the write safe?
  • The candidate set is 400,000 resources. Is that one transaction, and if not, what does a half-finished archive look like to a reader?

Explain why the owner filter ignores the listing index

Medium · composite index · query plans · sort elimination

The only index on resource is (tenant_id, status, updated_at DESC, resource_id DESC). A new endpoint returns one user's resources across all statuses, newest created first: WHERE tenant_id = $1 AND owner_user_id = $2 ORDER BY created_at DESC LIMIT 20. On a tenant with 2M rows it takes 900 ms and EXPLAIN shows a sort above a large scan. Explain precisely why the existing index cannot serve it, give the index that can, and state which of these the new index still will not help: owner_user_id alone across tenants; the same query ordered by updated_at. PostgreSQL 16.

Approach

  1. Separate the two jobs an index does. For filtering, a composite btree is seekable only on a left prefix, so with no predicate on status the scan can at best range over tenant_id and test owner_user_id per row; PostgreSQL 16 has no btree skip scan to jump the unconstrained column.
  2. For ordering, the index is sorted by (status, updated_at) within a tenant and not by created_at, so the LIMIT cannot stop early: every matching row is read and then sorted. That is the 'Sort Method: top-N heapsort' line, and it is why the plan reads 2M rows to answer with 20.
  3. Derive the replacement from the access path — equality, equality, then the ordering column: CREATE INDEX CONCURRENTLY ON resource (tenant_id, owner_user_id, created_at DESC). The scan seeks to the (tenant, owner) range and walks 20 entries in order, so the Sort node disappears along with the row-read.
  4. Treat INCLUDE (title, status) as conditional, not free. An index-only scan still visits the heap for any row whose page is not marked all-visible, so on a table taking 1.2k writes/second the win depends on autovacuum keeping the visibility map current, and the wider index costs more on every insert.

Follow-up

  • 90% of rows are status='active'. Would a partial index WHERE status = 'active' change your answer, and for which of the three queries?
  • A dashboard runs this for 40 owners in one page load. What changes about the design?

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?

Explain the difference between == and .equals in Java.

Medium · Technical Fundamentals & Java

Approach

  1. Split it by type first. For primitives, == compares values, after numeric promotion. For references, == compares identity, so it is true only when both variables point at the same object. equals() is a method on Object whose default body is this == obj. String, Integer, List and records override it to compare logical content.
  2. Use String as the worked example. Compile-time constants are interned, so "ab" == "a" + "b" is true. new String("ab") == "ab" and a string built at runtime with a variable compare false. equals() is true in all three cases. I never rely on interning for correctness.
  3. Cover the boxing trap. Autoboxed or Integer.valueOf values are guaranteed to share an instance only inside -128 to 127, so two boxed 127s are == but two boxed 128s usually are not (the upper bound can be raised with -XX:AutoBoxCacheMax). Comparing an Integer to an int unboxes it, which throws NullPointerException when the Integer is null.
  4. State the equals contract: reflexive, symmetric, transitive, consistent, and x.equals(null) is false. Any class that overrides equals must also override hashCode so equal objects get equal hashes. Otherwise HashMap and HashSet lookups miss.
  5. Give the safe idioms. Objects.equals(a, b) handles null on either side, and "literal".equals(s) avoids an NPE when s is null. == is correct for enums and other singletons, and for a deliberate identity check such as IdentityHashMap.

Follow-up

  • You override equals but not hashCode, then put two equal objects in a HashSet. What happens, and why?
  • How do you write equals for a class with subclasses: getClass() or instanceof, and what does each choice do to symmetry?
  • Why can new Integer(5) == new Integer(5) be false while Integer.valueOf(5) == Integer.valueOf(5) is true?

Can you identify common anti-patterns in a provided class structure and suggest improvements?

Medium · Technical Fundamentals & Java

Approach

  1. Practise on a class shaped like this role's work: a ProductService for the site that concatenates a product id into a SQL string, opens its own Connection, calls a pricing REST endpoint inline with a hand-built HTTP client, validates input and formats the page response, with public mutable fields and a static cache map. Read it before naming anything: list its responsibilities, what it creates with new, what state it exposes and who calls it.
  2. Rank findings by risk. First the concatenated query (SQL injection; fix with a PreparedStatement and bind parameters) and the Connection that is never closed. Next the REST call: no timeout, an empty catch that returns null, so a slow pricing service stalls the page and failures vanish. Then the static cache map shared across request threads with no synchronisation and no eviction. Readability comes last.
  3. Coupling: the DataSource and HTTP client are built inside the class, so it cannot be tested without a database and a live endpoint. Move them behind interfaces (a ProductRepository and a PricingClient) passed through the constructor, and make fields private final.
  4. Split by responsibility: the repository owns SQL and resource cleanup, the client owns the HTTP call with a timeout and maps errors to a typed exception, a mapper owns formatting, and the service only orchestrates. If a switch on product type keeps growing, replace it with a strategy map or polymorphism rather than adding a subclass just to reuse code.
  5. Describe the order of changes: write characterization tests that pin current output, using a fake PricingClient, then refactor in small steps (Extract Class, Extract Method) and rerun after each. Name the trade-off: adding interfaces to a class with one implementation and few callers is over-engineering, so say where you would stop.

Follow-up

  • The class has no tests and is in production. How do you refactor it without changing behaviour?
  • Fix the concatenated query in this class. What does a PreparedStatement with bind parameters protect against, and what does it not protect against (for example a dynamic ORDER BY column)?
  • The pricing call is slow under load. Where do you add a timeout, retry or cache, and what can go wrong with each?

Describe the relationship between parent and child nodes in a DOM tree.

Medium · Technical Fundamentals & Java

Approach

  1. The DOM is a tree of Node objects with the Document at the root. Every node has at most one parent, exposed as parentNode. It is null for the Document and for the root of a detached subtree; nodes inside a detached subtree keep their parentNode. A parent holds an ordered list of children, and siblings are linked through previousSibling and nextSibling.
  2. Separate nodes from elements. childNodes is a live NodeList that includes text and comment nodes, such as whitespace between tags. children is a live HTMLCollection of Element nodes only. The element-only navigators are firstElementChild, nextElementSibling and parentElement. document.documentElement.parentNode is the document, but its parentElement is null.
  3. A node cannot have two parents, so appendChild or insertBefore on a node already in the tree moves it rather than copying it. cloneNode(true) makes a deep copy. removeChild or element.remove() detaches a node, and the node keeps its own subtree.
  4. Events travel along the parent chain: capture runs from window down through the ancestors to the target, then bubbling runs back up. Event delegation attaches one listener on a parent and resolves the source with event.target.closest(selector). That covers children added later, and it fails if a child handler calls stopPropagation.
  5. Edge cases and cost: removing children while looping forward over a live collection skips every other node, so loop backwards or copy first with Array.from. querySelectorAll returns a static NodeList. Walking up to an ancestor costs O(depth), and a full subtree traversal (recursion or TreeWalker) costs O(n).

Follow-up

  • Write a function that finds the lowest common ancestor of two DOM nodes, and give its complexity.
  • Why does a for loop that removes each entry of childNodes leave half the children behind, and how do you fix it?
  • How would you serialise a subtree and rebuild it, keeping the parent-child order?

Listing latency scales with page size, not with filters

Easy · n+1 · latency · query batching

The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.

Approach

  1. Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
  2. Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
  3. Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
  4. Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.

Follow-up

  • The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
  • How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?

Suggested practice checklist

Day 1 — Confirm the format and inventory your projects

  • Email your recruiter to ask which stages you will face, whether there is an online assessment, whether any interview is a panel, which language you may use for coding and whether you will be writing code live.
  • List every project on your resume and, for each, write the technologies used, two design choices and the alternative you rejected for each.

Deliverable: A sent recruiter email with your format questions, and a one-page project inventory with design choices and rejected alternatives for each project.

Day 2 — Java equality, exceptions and memory

  • Write and run snippets comparing Strings built with new against literals, boxed Integers at 127 and 128, and a class with and without equals/hashCode; write one sentence explaining each result.
  • Write a JDBC transaction with the Connection in an outer try-with-resources, a PreparedStatement inside, rollback in the inner catch and auto-commit restored in finally; then force the body and close() to throw and print the suppressed exception.
  • Memory management is listed next to concurrency: write a few lines on heap versus stack and GC roots, then write a leak (a static Map that only grows, or Connections never closed), run it with a small -Xmx, capture a heap dump and find the retaining path in a heap analyser.
  • Answer aloud, without notes: the difference between == and .equals(), and how you handle exceptions and release resources in Java.

Deliverable: Runnable snippets with a one-line explanation per output, the JDBC transaction with its suppressed-exception test, the leak program with the retaining path you found, and two recorded spoken answers.

Day 3 — Reading a class for anti-patterns

  • Write a 60-line product-data service class that builds SQL by concatenation, opens its own Connection, calls a pricing REST endpoint inline with no timeout, swallows exceptions, keeps a static shared cache and exposes public mutable fields.
  • Review your own class as if in a code review: list issues in order of risk, and rewrite it as a repository with bind parameters, a client interface with a timeout, and a thin service using constructor injection.
  • Write the characterization tests, with a fake pricing client, that you would add before refactoring so you can show behaviour stayed the same.

Deliverable: A ranked issue list, the before and after versions of the class, and the characterization tests.

Day 4 — DOM tree and REST API basics

  • Draw a small HTML tree and label parent, child, sibling and ancestor relations, then write which of parentNode, childNodes, children, firstChild and nextSibling returns what, including the whitespace text nodes.
  • Write a recursive and an iterative traversal that collects all descendants of a node.
  • Design a REST resource with its URLs, HTTP methods, status codes and which calls are idempotent, then list the tests you would write for it.

Deliverable: An annotated DOM diagram with a method cheat sheet, both traversals, and a one-page REST resource design with its test list.

Day 5 — SQL under time

  • Work through the window-function question on version gaps and relay lag, writing the query before checking.
  • Take one query you wrote this week and rewrite it with bind parameters, then note which index it relies on.

Deliverable: Two solved SQL practice questions with written explanations, and one parameterised query with its supporting index named.

Day 6 — Coding and debugging while narrating

  • Solve the resource-graph archiving question iteratively, and say why you avoided recursion.
  • Work the listing-latency debugging question, writing the sequence of checks you would run in order.
  • Write a short Java example of a lost update on a shared counter and fix it with AtomicInteger or synchronized, since concurrency is a listed area.

Deliverable: Two timed solutions with spoken complexity statements, a written debugging sequence, and a working concurrency fix.

Day 7 — Behavioral stories and a full spoken run-through

  • Write three STAR stories from your own work: a failure, a disagreement or requirements change with a QA lead or product owner, and a decision you made and defended.
  • Answer all four reported questions back to back, aloud and timed, then write the two questions you want to ask the interviewers.

Deliverable: Three written STAR stories, recorded answers to both behavioral situations, a timed run-through of the four reported questions and your two interviewer questions.

Project deep-dive and STAR stories

Candidates describe the behavioral side as a conversation about your own history: the projects on your resume, the reasoning behind your design choices, your failures, your interests and how you collaborate. For this role the collaboration side includes QA leads, product owners and senior architects. The first prompt below is filed as behavioral but asks a Java question, so answer it technically and back it with a case from your own code. Build the others as STAR stories (Situation, Task, Action, Result) that put your decision and its outcome in the Action and Result.

  1. Walk me through the most substantial project on your resume and explain why you chose its main technologies and structure.
  2. Tell me about a project that failed or missed its goal, and what you changed afterwards.
  3. Describe a time you worked with QA leads, product owners or an architect to refine unclear requirements before you built anything.
  4. Tell me about a bug that reached production, how you found it and what you changed so it would not recur.
  5. What are you learning right now, and what draws you to maintaining large, long-lived enterprise software?

How do you handle exceptions and ensure proper resource management (e.g., closing statements) in Java?

Medium · Technical Fundamentals & Java

Approach

  1. Start with the mechanism: a resource such as a Connection, Statement or ResultSet implements AutoCloseable, so declare it in try-with-resources. Java closes each resource when the block ends, in reverse order of declaration, whether the body finishes normally or throws.
  2. Explain why that beats try/finally: with hand-written finally blocks, an exception thrown by close() can replace the original failure and hide the real cause. With try-with-resources the original exception propagates and the close() failure is attached as a suppressed exception, readable through getSuppressed().
  3. Cover catching: catch the most specific exception you can act on, such as SQLException, never leave a catch empty, and avoid catching Throwable or a bare Exception. If you cannot recover, wrap and rethrow with the cause attached (new DataAccessException("load order failed", e)) so the stack trace is kept, and log once at the boundary.
  4. For a transaction, note that resources declared in a try-with-resources header are out of scope in its catch and already closed when the catch runs, so rollback cannot go there. The shape I use: try (Connection conn = ds.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps = conn.prepareStatement(sql)) { bind; execute; conn.commit(); } catch (SQLException e) { conn.rollback(); throw new DataAccessException("save failed", e); } finally { conn.setAutoCommit(true); } }. Bind parameters also close the SQL injection hole.
  5. Name the pitfalls: a return or throw inside finally can discard an in-flight exception; closing a pooled Connection returns it to the pool, so a leaked one drains the pool; checked exceptions suit failures a caller can handle, unchecked suit programming errors. Answer technically and add a short story from your own code, such as a connection leak you fixed.

Follow-up

  • What happens if the try body throws and close() also throws, and how would you read the second exception?
  • What goes wrong if you put the Connection, the PreparedStatement and the rollback all in one try-with-resources with a single catch?
  • How would you find a connection leak in a running service, and how does a connection pool change what close() does?

Tell callers you do not own that their integration breaks

Medium · deprecation · compatibility · stakeholders

A field in a write endpoint's response must change shape. You own the endpoint; you do not own the four internal callers or the outbound webhook consumers who read it. Describe a deprecation you were responsible for: what you shipped first, how you established who was actually reading the field, the window you gave and what set its length, what you did about the consumer who never moved, and how you decided removal was safe. Name the signal you used, not the announcement you sent.

Approach

  1. Establish the reader set empirically rather than from a wiki of owners: per-field usage counters keyed by principal, or access logs attributed to a consumer. State the blind spot of whichever you pick, since a consumer that reads the field only on a monthly job will not appear in a week of logs.
  2. Ship additive first. Populate the new field alongside the old one so no reader is forced to move, which is also what keeps a rolling deploy safe, because old and new instances answer the same requests at the same time and a rollback must still find the old shape present.
  3. Set the window from the slowest legitimate consumer's release cadence, not from your calendar, and decide separately what to do for a consumer with no release process at all, such as an external webhook endpoint you can only email.
  4. Convert silence into evidence before you rely on it: a short, low-traffic removal window that makes a still-dependent consumer fail visibly and loudly while you are watching, rather than at three in the morning after you have moved on.

Follow-up

  • How would you detect a consumer that reads the field only during a monthly export?
  • One caller refuses to move and has a commercial relationship behind it. What changes in your plan and what does not?

Unblock an engineer without taking the keyboard

Easy · mentoring · leases · at-least-once

A teammate has spent two days on a job handler that occasionally writes duplicate rows. They are certain the queue is delivering twice by mistake. You suspect a lease expiring under a slow handler, so the job is running concurrently with itself. Describe how you have unblocked someone in this position: what you asked before offering a hypothesis, what you showed them rather than told them, and what you left them owning. Then say what you would do if their theory turned out to be the right one.

Approach

  1. Ask before diagnosing, and ask for things answerable from data they already have: the attempt count on the job rows that produced duplicates, the handler's observed duration against its lease expiry, and whether the duplicate rows share a natural key that a unique constraint could have caught.
  2. Teach the shape rather than the answer. A lease cannot distinguish a dead worker from a slow one, so a handler that outruns its lease is running twice by design, and deploys deliver the other half by killing handlers mid-run on every rollout. Both of their candidate theories produce identical duplicate rows, which is why the evidence has to come from timings rather than from argument.
  3. Hand over a checklist they execute: a natural key on every write the handler performs so the second copy collides rather than appends, the record of intent written before any external effect, a lease heartbeat while running, and the metric that shows it working.
  4. Keep ownership with them deliberately. Pair on the first write, then step back; if you finish it yourself you have closed one ticket and left the same person stuck on the next redelivery.

Follow-up

  • How would you distinguish a genuine double-delivery from a lease expiry using only the data already stored?
  • Their handler calls an external endpoint before recording that it did. What do you tell them to change first?