Interview guides · A healthcare technology
A healthcare technology Software Engineer Interview Guide
A healthcare technology Software Engineer interview guide: reported Java, OOP and SQL questions, answer approaches and a 7-day plan.
The 60-second brief
The company is identified only as a healthcare technology business, and candidates name no products or customers. The role description mentions building backend services, tuning database performance and building secure APIs used by health systems and providers. It also mentions working with product managers and QA, writing, reviewing and testing code, taking part in architecture discussions, and debugging production issues.
This guide is for candidates preparing for a Software Engineer interview at this healthcare technology company. It covers the five questions candidates report (the four pillars of OOP, exception handling with final, finally and finalize, string reversal and palindromes, HashMap versus HashTable, and Delete, Truncate and Drop in SQL), the example coding scenarios and database topics candidates mention, and how to defend the projects on your resume. It does not cover system design, because no design round is reported.
No fixed round sequence is reported. The reported flow mentions an online assessment, technical interviews with engineers and leads, and an HR conversation, with the emphasis on core language fundamentals, SQL and your own project work.
Focus areas
- Java
- SQL
- OOP Concepts
Overview
The reported questions are fundamentals questions. Candidates are asked to explain the four pillars of OOP with a polymorphism example, to separate final, finally and finalize in Java, to write a string reversal or palindrome check, to compare HashMap and HashTable, and to explain Delete, Truncate and Drop in SQL. Other scenarios candidates mention are swapping two numbers without a third variable and finding the second-largest number in an array. None of these needs a clever trick. Each rewards an answer that is correct, names the edge cases and states the trade-off, so the preparation is depth on small topics rather than breadth across many.
The stack candidates list is Java, Python or C# for the main language, and SQL for the database side. Topics tagged on the reports are Java, SQL, OOP concepts, Python and retrieval-augmented generation (RAG). No reported question shows how RAG comes up, so cover the basics: chunking, embeddings and vector retrieval, grounding answers in retrieved sources with citations, and evaluating retrieval quality and hallucination, using a healthcare case such as answering questions over clinical documentation. Nice-to-have items candidates mention are AWS or Azure, Spring Boot or Django, CI/CD pipelines, and Docker or Kubernetes. Choose one primary language and know it well enough to explain why it behaves as it does: Java candidates should be ready for multithreading and exceptions, Python candidates for decorators.
Candidates also report being asked about the projects on their resume, including why they chose one library or database over another, and they are told that anything listed as a strength can be probed. Treat your resume as part of the syllabus: for every project, be able to say what you built, what the alternatives were, what went wrong and what you would change. The role description also mentions debugging production issues and optimizing queries, so keep one real debugging story and one query-tuning story ready.
Narrate while you work. Practice saying the plan, the complexity and the edge cases aloud as you code, and practice writing the simple functions without an IDE, so the reasoning is visible and not only the final code.
Where to focus your preparation
No named stages are reported for this loop, so the single entry below is a preparation focus rather than a fixed round. Confirm the actual sequence and whether you will write code with your recruiter.
Preparation focus
The reported flow mentions an online assessment, then technical interviews and an HR interview, with technical leads and management involved. The order and number of stages are not reported and vary by team and location, so ask your recruiter which stages apply and whether you will write code. Across all of them, the reported material is the same: Java or another main language, OOP, data structures basics, SQL with joins and subqueries, RAG as a tagged topic, and a detailed account of your own projects. Prepare by working each reported question to a spoken answer and a short piece of code.
What to demonstrate
- Command of core language mechanics: OOP pillars, exceptions, collections and strings
- Ability to write small, correct functions and state their edge cases
- SQL fluency: joins, subqueries and the difference between Delete, Truncate and Drop
- Clear explanation of the design choices behind projects on your resume
How to prepare
- Write the palindrome check, the string reversal, the swap without a third variable and the second-largest-in-array function from memory, and say the complexity of each
- Rehearse one-minute spoken answers for the four pillars of OOP, final versus finally versus finalize, HashMap versus HashTable, and Delete versus Truncate versus Drop, plus a two-minute account of each resume project with its library and database choices and one rejected alternative
- Write a join and a subquery by hand, then read the execution plan of a slow join before and after adding an index
- Explain a RAG pipeline for questions over clinical documentation: chunking, embeddings and vector retrieval, answers grounded in retrieved passages with citations, how you would measure retrieval quality and hallucination, and how you keep PHI out of prompts and indexes
Where candidates lose points
Mixing up final, finally and finalize, or describing finalize as a cleanup block
Learn them as three different kinds of thing: a modifier, a control-flow block and a garbage-collector hook. Say what each does, one edge case (System.exit skips finally; a return in finally overrides) and why finalize is deprecated.
Writing the palindrome check only for clean lowercase input, so it breaks on null, an empty string, mixed case or punctuation
Ask whether case and non-alphanumeric characters matter, handle null and empty input first, and use a two-pointer comparison from both ends in O(n) time and O(1) extra space. Reversing into a new string is acceptable only if you name the extra memory.
Answering HashMap versus HashTable as 'one is synchronized' and stopping there
Add the rest: HashTable rejects null keys and values, HashMap allows one null key, HashTable is a legacy class, and ConcurrentHashMap is the usual choice when you need thread safety. Mention that HashMap iterators are fail-fast and that its lookup is O(1) on average.
Treating Delete, Truncate and Drop as three words for removing data
Separate them by scope and behavior: Delete removes chosen rows with a WHERE clause and can be rolled back; Truncate empties the whole table and resets identity counters in SQL Server and MySQL, but in PostgreSQL only with RESTART IDENTITY (Oracle keeps the counter too); Drop removes the table definition as well. Note that transaction and trigger behavior differs by database engine, and name the engine you use.
Listing technologies on your resume that you cannot explain, then stalling when asked why you chose them
Cut anything you cannot defend. For each project you keep, prepare the reason for each library or database, one alternative and the trade-off. Candidates report that anything listed as a strength can be asked about.
Questions & practice
Collapse a redelivered event batch into per-aggregate high-water marks
Easy · hashing · at-least-once · aggregation
You drain a batch of up to 5,000,000 events, each (aggregate_id BIGINT, aggregate_version INT, event_type, payload). The log guarantees order within one aggregate only; the batch merges 64 partitions, and a relay failover has redelivered a range, so an older version for an aggregate can appear after a newer one. Given a map of last_applied_version per aggregate, produce the events worth applying, at most one per (aggregate_id, version), plus the count discarded. Target O(n) time. State the memory for 2,000,000 distinct aggregates and what you do when it does not fit.
Approach
- One pass, one hash map from aggregate_id to the highest version kept, and a discard counter. An event whose version is at or below last_applied_version for its aggregate is dropped without further work, which is the whole reason the event carries its version rather than a delta. O(n) expected time, O(d) space in distinct aggregates.
- Keep the maximum, never the last occurrence. The redelivered range means the final appearance of an aggregate in the batch can be an older version than one seen earlier in the same batch, so last-wins applies stale state over newer state and the projection regresses with no error anywhere.
- Cost the memory instead of calling it large: an 8-byte key plus a 4-byte version is 12 bytes of payload, and an open-addressed table held at a 0.7 load factor costs roughly 17 bytes per entry before per-slot metadata, so 2,000,000 aggregates is tens of megabytes in a native layout and several times that in a runtime that boxes both key and value.
- If the distinct set exceeds memory, partition on hash(aggregate_id) mod P and reduce each partition independently. Every event for one aggregate hashes to the same partition, so the per-partition result is exact and the merge is concatenation rather than a second reduction.
Follow-up
- The payload is a patch rather than a snapshot, so applying only the highest version loses the intermediate changes. What changes in your reduction?
- How do you detect that version 7 arrived while version 6 was never delivered, and what should the consumer do about the gap?
Merge partitioned event streams into one ordered feed with bounded lateness
Hard · k-way merge · watermarks · out-of-order streams
The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.
Approach
- Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
- Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
- Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
- Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.
Follow-up
- The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
- The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?
Identify the heaviest tenants in a five-minute window under memory pressure
Medium · top-k · heavy hitters · streaming
The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.
Approach
- Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
- Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
- State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
- Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.
Follow-up
- The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
- Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?
Explain the difference between Delete, Truncate, and Drop commands in SQL.
Medium · Technical Fundamentals and Programming
Approach
- DELETE is DML: it removes the rows matching a WHERE clause (or all rows without one), one row at a time with each row logged. It fires DELETE triggers, is checked against foreign keys (blocked, or cascaded if the constraint says ON DELETE CASCADE) and can be rolled back inside a transaction. It does not reset an identity or AUTO_INCREMENT counter.
- TRUNCATE removes every row by deallocating the table's storage instead of logging each row, so it is much faster on large tables and takes no WHERE clause. It does not fire row-level DELETE triggers and resets the identity counter in SQL Server and MySQL; PostgreSQL needs TRUNCATE ... RESTART IDENTITY. It is refused on a table referenced by a foreign key (PostgreSQL allows it with CASCADE).
- DROP TABLE removes the table itself: its rows, definition, indexes, constraints, triggers and grants. Dependent objects such as views either block the drop or need CASCADE, depending on the engine, and the table must be recreated before it can be used again.
- State the engine differences instead of one blanket rule on rollback: TRUNCATE and DROP are transactional and can be rolled back in PostgreSQL and SQL Server, but they cause an implicit commit in MySQL and Oracle. Oracle can restore a dropped table from the recycle bin with FLASHBACK TABLE.
- Add the operational angle: on a large live table, one big DELETE holds locks and grows the transaction log, so delete in batches by primary-key range. In PostgreSQL, DELETE leaves dead tuples that VACUUM must reclaim, while TRUNCATE frees the space immediately but takes an ACCESS EXCLUSIVE lock.
Follow-up
- How would you remove 50 million old rows from a table that is still taking writes, without blocking it?
- If a table has an ON DELETE trigger that writes to an audit table, which of the three commands still records the removal?
- How would you keep deleted records recoverable for an audit, and what does a soft-delete column do to your unique constraints and queries?
Keep soft-deleted accounts from blocking re-registration
Easy · soft delete · unique constraints · engine differences
app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.
Approach
- Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
- Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
- For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
- State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.
Follow-up
- A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
- How do you honour an erasure request while resource_revision.actor_user_id still references this table?
Explain the four pillars of OOP and provide a real-world scenario for polymorphism.
Medium · Technical Fundamentals and Programming
Approach
- Name the four pillars, one sentence each, and keep the two that get confused apart: encapsulation hides state behind methods that enforce invariants (private fields, validated setters); abstraction exposes what an object does and hides how (an interface or abstract class); inheritance reuses and specialises a parent type; polymorphism lets one call site run different implementations.
- Give a concrete polymorphism scenario from the healthcare domain: a ClinicalDocument interface with summarize(), implemented by LabResult, VitalSign and ImagingReport. A patient-chart view loops over a List<ClinicalDocument> and calls summarize() without any if/else on type, so supporting a new document type means adding a class, not editing the caller (open/closed principle). A claims adjudicator with one implementation per payer works the same way.
- Split polymorphism into its two kinds: overloading is resolved at compile time from the static argument types; overriding is resolved at runtime from the object's actual class (dynamic dispatch). In Java, static methods are hidden rather than overridden and fields are not polymorphic, so Parent p = new Child(); p.field reads the parent's field.
- Show judgement about inheritance: prefer composition when the relationship is 'has-a' or when a subclass would have to weaken the parent's contract. Cite Liskov substitution with the Square-extends-Rectangle example, where setWidth breaks the caller's assumption that width and height vary independently.
- Close with a two-minute code sketch in your strongest language (Java, Python or C#): the interface, two implementations and the calling loop, and say which line is the dynamically dispatched call.
Follow-up
- What is the difference between an abstract class and an interface in Java, now that interfaces can have default methods?
- Given an overloaded method and an overridden method called through a parent reference, which version runs for each and why?
- When would you replace an inheritance hierarchy with composition, and what does the refactor look like?
Write a function to reverse a string or check for a palindrome.
Medium · Technical Fundamentals and Programming
Approach
- Clarify before coding: is the input null or empty possible, should the palindrome check ignore case, spaces and punctuation ("A man, a plan, a canal: Panama"), and is the input ASCII or arbitrary Unicode.
- Reverse: in Java strings are immutable, so copy to a char[] and swap with two pointers i=0, j=n-1 until they meet, or use new StringBuilder(s).reverse(). Both are O(n) time and O(n) extra space for the new string. In Python, s[::-1] is idiomatic and also O(n) time and space.
- Palindrome: two pointers moving inward, comparing s.charAt(i) with s.charAt(j) and returning false on the first mismatch. That is O(n) time and O(1) extra space, better than building the reversed string and comparing (O(n) extra). For the relaxed version, skip characters where Character.isLetterOrDigit is false and compare Character.toLowerCase of each side.
- Edge cases to state and test: empty string and single character (both palindromes), even and odd lengths, a string of only punctuation under the relaxed rule, and null (throw or return false, but say which).
- Unicode caveat: a manual char swap splits surrogate pairs such as emoji, while StringBuilder.reverse() keeps surrogate pairs in order. Neither handles combining marks or multi-codepoint graphemes, which need a grapheme-aware iterator such as java.text.BreakIterator.
Follow-up
- Can the string become a palindrome by deleting at most one character? (On the first mismatch, check whether either remaining substring is a palindrome; still O(n).)
- Find the longest palindromic substring: what does expand-around-centre cost, and what does Manacher's algorithm improve?
- Reverse the order of words in a sentence in place, keeping each word's letters in order.
What are the differences between a HashMap and a HashTable, and when would you choose one over the other?
Medium · Technical Fundamentals and Programming
Approach
- Lead with the concrete differences in Java: Hashtable is the Java 1.0 legacy class whose public methods are all synchronized; HashMap is unsynchronized. HashMap allows one null key and any number of null values, while Hashtable throws NullPointerException on a null key or value. Hashtable extends Dictionary and also offers Enumeration, which is not fail-fast; HashMap's iterators are fail-fast and throw ConcurrentModificationException on structural modification outside the iterator.
- Explain how HashMap works, since that tends to come next: hash spreading (h ^ (h >>> 16)), bucket index (n - 1) & hash on a power-of-two table (default 16, load factor 0.75), resize by doubling. Since Java 8 a bucket with more than 8 entries becomes a red-black tree once the table has at least 64 slots, so worst-case lookup in a bucket drops from O(n) to O(log n) for comparable keys.
- Answer the 'when' part directly: single-threaded or confined to one thread, use HashMap. Shared between threads, use ConcurrentHashMap, not Hashtable. Hashtable locks the whole map for every call, while ConcurrentHashMap allows lock-free reads and locks per bucket on writes. It also offers atomic putIfAbsent, computeIfAbsent and merge.
- Point out the trap in 'synchronized means thread-safe': a check-then-act sequence (if (!map.containsKey(k)) map.put(k, v)) is still a race on Hashtable or Collections.synchronizedMap because each call locks separately. Iterating a synchronizedMap also requires manually synchronizing on the map. ConcurrentHashMap's atomic methods remove both problems.
- Mention the key contract: keys need consistent equals() and hashCode(), and mutating a key after insertion makes it unreachable. In C#, the pair is the non-generic Hashtable versus Dictionary<TKey,TValue> (type-safe, no boxing), with ConcurrentDictionary for sharing. In Python, dict is a hash table using open addressing that keeps insertion order from 3.7; single operations are atomic under the GIL but check-then-act is not, so use a threading.Lock or dict.setdefault, and know collections.OrderedDict and defaultdict.
Follow-up
- Why does ConcurrentHashMap reject null keys and values when HashMap accepts them?
- What happens if you put an object into a HashMap and then change a field that its hashCode depends on?
- When would you pick LinkedHashMap or TreeMap instead, and what does each cost per operation?
Edge instances grow 400 MB per hour until the nightly restart
Hard · memory leak · heap analysis · metric cardinality
Edge API instances start at 700 MB resident and grow about 400 MB/hour; a nightly rolling restart has hidden it for weeks. Growth continues unchanged when request rate halves overnight, p99 degrades in the last hours before an instance is recycled, and heap used immediately after a forced full GC rises monotonically. The service holds no product state. Name the discriminating measurement that separates the plausible causes, give the most likely cause, and give the fix and how you would verify it.
Approach
- Separate resident memory from live heap first, because they fail differently. Resident size can grow from fragmentation, native buffers or thread stacks while the heap is flat; heap used after a full GC rising monotonically is the measurement that says objects are reachable and not being released. You already have it, so this is retention, not fragmentation, and that closes off half the candidate list.
- Use the rate's independence from traffic as the discriminator. Growth that continues at half the request rate rules out per-request objects that are merely slow to collect and points at a structure that grows with distinct values observed rather than with call volume. Write the candidates that have that property: a metrics registry keyed on a high-cardinality label, an unevicted cache, an interner, a per-key lock map.
- Take two heap snapshots an hour apart and diff by retained size, reading the dominator tree, not by allocation count or instance count. Expect one root holding a map with millions of entries, then follow the reference chain to the code that inserts and never removes. Allocation profilers point at churn, which is the wrong signal here.
- The candidate that fits this service is an observability label carrying an identifier, such as a request path recorded before templating so that /v1/resources/48213 becomes its own metric series. That grows with distinct ids seen, is independent of rate, and explains the late p99 degradation, since GC cost rises with the size of the live set.
Follow-up
- Post-GC heap is now flat but resident size still creeps. What are you looking at, and does it matter?
- How would you have detected this before an OOM, given the nightly restart masked the trend?
Suggested practice checklist
The plan works through the reported questions one topic per day, then moves to SQL, exceptions and project defense, and finishes with a full spoken mock.
Day 1 — OOP pillars and the polymorphism example
- Confirm with your recruiter which stages apply and whether you will write code, and note your primary language
- Explain encapsulation, inheritance, abstraction and polymorphism aloud, with a small class example for each in your main language
- Write one polymorphism scenario, for example a ClinicalDocument interface with LabResult and VitalSign implementations called through the interface, and say which overload or override is chosen at compile time versus run time
Deliverable: A written recruiter reply plus a half-page note with one example per pillar and one runnable polymorphism example.
Day 2 — Strings, arrays and small functions
- Write the palindrome check with two pointers, then a version that ignores case and punctuation, and test it on null, empty, single-character and mixed-case input
- Write string reversal three ways (StringBuilder, character-array swap, recursion) and state the time and space cost of each
- Write the swap without a third variable (arithmetic and XOR). The arithmetic form is safe in Java (int wraps) and Python (no overflow) but undefined on signed overflow in C/C++ and fails in checked C#; either form zeroes the value when swapping a[i] with a[j] and i == j
- Write the second-largest-in-array function in one pass, handling duplicates and arrays with fewer than two distinct values
Deliverable: Four tested functions with a one-line complexity and edge-case list for each.
Day 3 — Collections, language features and RAG basics
- Write a comparison table of HashMap, HashTable and ConcurrentHashMap covering null handling, synchronization, iteration behavior and typical use, with the Python dict and C# Dictionary equivalents
- Explain how a hash map handles collisions and what happens on resize, and why equals and hashCode must agree for keys
- Pick the language feature your resume names, such as Java multithreading or Python decorators, and write one small working example you can explain line by line
- Outline a RAG pipeline that answers questions over clinical documentation: chunking, embeddings, vector retrieval, answers grounded in retrieved passages with citations, how you would evaluate retrieval quality and hallucination, and how you keep PHI out of prompts and indexes
Deliverable: The comparison table, one explained language-feature example and a one-page RAG outline with its evaluation and privacy points.
Day 4 — SQL: joins, subqueries, tuning and Delete, Truncate, Drop
- Write one query with an inner join, one with a left join that finds unmatched rows, and one with a correlated subquery, then rewrite the subquery as a join
- Run EXPLAIN (ANALYZE) on a slow join, add a composite index, compare the two plans, and explain why wrapping the indexed column in a function such as LOWER(email) defeats the index
- Run each of Delete, Truncate and Drop on a scratch table in the database you know, and record what happens to rows, identity counters, rollback and triggers
Deliverable: Three working queries, a before-and-after plan comparison, a Delete/Truncate/Drop behavior table for your database engine, and a worked solution to the soft-delete exercise.
Day 5 — Exceptions and production debugging
- Write a short Java example that shows try/catch/finally ordering, a return inside finally, and try-with-resources with a failing close()
- Explain final, finally and finalize aloud in under a minute each, then say how you handle exceptions in a service you have built
Deliverable: A runnable exception demo, a recorded spoken answer to the exceptions question, and a written debugging sequence for the memory-growth exercise.
Day 6 — Defend your resume and your stories
- For every project and tool on your resume, write the design choices, one rejected alternative and one thing that went wrong
- Cut or prepare any technology you cannot explain in detail
Deliverable: A one-page defense sheet per project and two written stories with the decision and the result named.
Day 7 — Full spoken mock
- Answer all five reported questions aloud in sequence without notes, and write the two functions (palindrome and second-largest) on paper
- Write the two questions you want to ask the team about their current engineering challenges
Deliverable: A completed mock with a list of the answers that stumbled and two questions to ask.
Exceptions, incidents and your own projects
General preparation advice is to have stories about teamwork, problem-solving and adaptability ready. The first two prompts below come from reported material: the exceptions question and the resume-project walk-through. The rest are practice prompts built around work the role description mentions, such as debugging production issues and working with product managers and QA. Choose stories where you made the decision, and bring the outcome.
- Walk me through a project on your resume and explain why you chose one library or database over another.
- Practice prompt: Tell me about a production issue you debugged: how it appeared, how you found the cause and what you changed afterwards.
- Practice prompt: Describe a time you worked with a product manager or a QA engineer to ship something, and a disagreement you resolved.
- Practice prompt: Tell me about a time you had to learn a new technology quickly, and how you showed you could apply it.
How do you handle exceptions in your code, and what is the difference between final, finally, and finalize?
Medium · Technical Fundamentals and Programming
Approach
- Answer in two parts: first your own practice, then the three terms. For your practice, describe a layered pattern: catch a specific exception type only where you can do something useful (retry, fall back, translate), let everything else propagate to one boundary such as a request handler that maps it to a response, and never leave an empty catch block.
- Show concrete habits: wrap a low-level exception in a domain exception and keep the cause (new OrderException(message, e)) so the stack trace survives; log once at the boundary with context such as the record id, not at every layer; use checked exceptions for failures the caller can recover from and unchecked ones for programming errors; open resources with try-with-resources so close() runs even on failure.
- Then separate the three terms. final is a modifier: a final variable is assigned once (the reference is fixed, not the object's contents), a final method cannot be overridden, a final class such as String cannot be extended. finally is a block after try/catch that runs whether or not an exception was thrown, used for cleanup; it does not run if the JVM exits through System.exit or crashes, and a return or throw inside it replaces the earlier outcome.
- finalize() is a method on Object that the garbage collector may call before reclaiming an object. There is no guarantee when, or whether, it runs, which is why it is deprecated (marked for removal in recent Java versions) and why try-with-resources or java.lang.ref.Cleaner is used for cleanup instead. Say this explicitly, because the three names sound alike and mixing them is the usual mistake.
- For the story version of the question, choose one real failure: the symptom, the exception handling that was missing or wrong (a swallowed exception, a catch of Exception, a retry on a non-retryable error), what you changed, and how you confirmed it. The mistake that sinks it is a generic answer ('I use try-catch') with no example of a decision you made.
Follow-up
- What happens if both the try block and the finally block throw, and how does try-with-resources treat the second exception?
- When would you create a custom exception rather than reuse a standard one, and should it be checked or unchecked?
- Why is finalize discouraged, and what would you use to release a file handle or a database connection?
Narrate an outage you owned from page to postmortem
Hard · incident response · blast radius · postmortems
Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.
Approach
- Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
- Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
- Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
- Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.
Follow-up
- What would you do differently in the first five minutes, given the same dashboard and no more information?
- Which follow-up action did you deliberately not take, and why was dropping it the right call?
Turn a code review disagreement into a decision
Easy · code review · optimistic concurrency · communication
A colleague's change updates a row with UPDATE resource SET version = version + 1 WHERE resource_id = $1 AND version = $2 and treats an affected-row count of zero as a successful no-op. You read that as a silently lost update; they think returning 200 is friendlier to clients than returning a conflict. Describe how you have handled a review disagreement of this shape: what goes in the comment, when you leave the thread, and who decides. Then write the comment you would leave here, in under 80 words.
Approach
- Sort the disagreement before writing anything. A silently discarded write is a correctness claim about data; the choice between 409 and 412 is taste. Only the first justifies blocking a merge, and saying which one you are doing is most of the value of the comment.
- Make the claim reproducible in the comment itself with an interleaving rather than a principle: A reads version 7, B reads version 7, B commits version 8, A's predicate matches zero rows, A is told it succeeded and A's edit is gone.
- Offer the alternative with its cost attached: return 409 carrying the current version and the revision that won, so the client can re-read and re-apply. Note that automatic retry is not the fix, because a retry re-reads the winner's state and reapplies an intent formed against data that no longer exists.
- Apply an escalation rule you can state: two round trips on the thread, then a call, and the service's owner decides rather than the reviewer. A reviewer who cannot be overruled is a bottleneck with extra steps.
Follow-up
- Where would you put the test that fails if someone reintroduces the swallowed zero rowcount?
- The author says clients cannot handle a 409. How do you check whether that is true?