Source notes for the Software Engineer role at Onemain Financial describe work on the software behind its lending products: designing, building and maintaining backend services and data-driven applications, taking part in code reviews, refining requirements with product managers, and modernising legacy systems alongside new feature work. The stack named in those notes is Java, Kotlin or Node.js on the server, SQL and database design, and RESTful APIs, with AWS, GraphQL and React listed as nice-to-haves.
The reported questions follow that stack. The coding questions are validating a binary search tree, explaining the two-pointer approach to a palindrome problem and its time complexity, and implementing breadth-first search in a given scenario. Next to those sit a question on best practices for specifying API endpoints and a live coding session in SQL and ActiveRecord. The reported design questions are architecting an application domain from scratch, the trade-offs between database schemas for a financial application, and keeping a distributed system highly available and observable. The question bank for this company and role adds explaining Java in depth, OOP classes in practice, ILE programs and compilation, an IAM, Lambda and database architecture question, lowest common ancestor, first unique character, and designing a chess game.
Candidates report three stages over roughly three to five weeks: a recruiter screen about background and fit, a technical assessment or phone interview, and an onsite or virtual panel with managers and senior engineers that covers past work and professional goals. The sources do not say which reported question comes up at which stage, so prepare every category before the technical assessment. Then use the time before the panel to deepen your project stories and your design answers.
Recruiter Screen
reportedCandidates describe this as an initial call with a recruiter about your background and fit for the role. Treat it as two jobs. First, connect your experience to the stack the posting names (Java, Kotlin or Node.js, SQL and database design, REST APIs) in a two-minute walkthrough. Second, get the details you need for the next stage: whether the technical step is an assessment or a phone interview, which languages you may use, and whether SQL or ActiveRecord is part of it. Raise hard constraints now, such as start date, work authorisation, location and a competing timeline. They are cheap to settle here and expensive to discover at offer stage.
What to demonstrate
- Whether your background walkthrough maps clearly onto the languages, database work and API experience the posting lists
- Whether your constraints on start date, location, authorisation and timeline fit the role before later stages are booked
- Whether you can give a compensation range backed by current data points for the level and location instead of deflecting twice
How to prepare
- Write a walkthrough that names one project per must-have area: a service in your main language, a schema or query you owned, and an API you designed or changed
- Prepare three questions about the next stage: assessment or phone format, allowed languages, and whether SQL or ActiveRecord is in scope
- Write each hard constraint down in one line before the call and state it as a fact, together with the status of any other process you are in
Technical Assessment
reportedCandidates describe this stage as a technical assessment or a phone interview that evaluates technical skills. The sources do not say which reported questions appear here. Prepare the whole reported technical set: BST validation, a two-pointer palindrome with its complexity, BFS in a scenario, API endpoint practices, and live SQL with ActiveRecord. The format changes what matters. On a phone interview, your spoken reasoning is the only evidence of work in progress, so narrate constraints, the brute force and the improvement before you type. In a self-paced assessment, the examples in the prompt are the specification, so test the empty and single-element inputs yourself before you submit.
What to demonstrate
- Whether your code is correct on edge cases you name yourself: an empty tree, duplicate keys, a disconnected graph, a single-character string
- Whether you state time and space complexity and can justify them; the palindrome question asks for time complexity directly
- Whether you can move between an ORM call and the SQL it produces when a data-access question comes up
How to prepare
- Solve BST validation with bounds passed down the recursion and again with an in-order check, then explain why checking only parent against child is wrong
- Write BFS with visited marked on enqueue and state its O(V + E) time and O(V) extra space, then do the same for a two-pointer palindrome that skips non-alphanumeric characters
- For each ActiveRecord query you practise, write the raw SQL beside it and find at least one N+1 pattern in your own code
Panel Interview
reportedCandidates describe an onsite or virtual panel with managers and senior engineers that covers your past work and professional goals. Prepare to explain both the scope of each project and its design trade-offs, and fix the figures you will quote in case more than one person asks about the same project. The sources do not assign the reported design questions to any stage, so review them again before the panel: architecting an application domain from scratch, schema trade-offs for a financial application, and high availability and observability. Go in with a short, specific answer on professional goals that connects to the backend, data and API work the role describes.
What to demonstrate
- Whether you can explain your own design decisions on past projects, including what you rejected and why
- Whether the figures and trade-offs you give for a project stay the same when a different interviewer asks about it
- Whether your professional goals are stated specifically enough to connect to backend, data and API work
How to prepare
- Write a one-page sheet per project with the figures you will quote: traffic, data size, team size, timeline and what broke. Say them aloud until they come out the same every time
- Rehearse one domain-architecture answer from scratch: entities and boundaries, the API surface, the schema, then the failure you are designing for
- Prepare two sentences on professional goals and one example from your past work that supports them
2 candidate reports. Individual accounts describe a particular role and hiring cycle.
OneMain Financial New Grad Data Scientist Interview Experience — A Case on Ditching Traditional Branches for Digital
Had my interview today, so let me do a recap while it's still fresh. I got a VP who spent 15 years at Capital One before jumping to this company. He came in like a machine gun from the start — honestly it's a waste he's not a rapper. He jumped straight into a case: OneMain's credit card business has two flows — a traditional branch business and a digital business. Q1 was why OneMain would want to…
Read full experienceOneMain Financial Data Scientist Interview Experience — Four Rounds, All Correct, Then Ghosted
View report detailsPracHub editorial advice for the preparation topics above.
Validating a binary search tree by comparing each node only with its direct children
A tree with root 5, left child 4, and right child 6 whose left child is 3 passes every parent-child check but is not a valid BST, because 3 sits in the right subtree of 5. Pass (low, high) bounds down the recursion, or check that an in-order traversal is strictly increasing. Say how you treat duplicate keys before you code, and state O(n) time and O(h) stack, where h is the tree height and is n for a skewed tree.
Writing ActiveRecord in a live SQL session without being able to say what SQL it runs
One reported prompt asks for SQL and ActiveRecord together, so practise both side by side. For each association call, write the SQL it generates and point out where a loop over records causes an N+1 query. Know which methods eager-load and fix it (includes, preload, eager_load), and that joins is not one of them: it adds an INNER JOIN for filtering but does not load the associated records, so iterating over the association still runs one query per record. Be able to say whether each filter runs in the database or in memory, and what that costs.
Answering the financial-schema trade-off question with normalisation theory and no money-specific decisions
Name the choices that matter for financial data. Store amounts as integer minor units with a currency, not as floats. Decide whether history is append-only or updated in place, and what an auditor can reconstruct under each. Decide which balances are derived and which are materialised. Name the isolation level that stops two concurrent writes from breaking an invariant. The SQL drills in this guide on materialised balances and loan-schedule versioning give you concrete examples of each.
Answering the high-availability and observability question with a list of tools
Start from the failure: which dependency goes down, what the user sees, and how the system recovers. Then state the signals that tell you it happened, such as error rate, latency and the age of the oldest unprocessed item, and where you would alert. The webhook-stall debugging drill in this guide shows the kind of detail that belongs here, such as an alert on the oldest undelivered row instead of on process health.
Telling the panel a project story whose numbers or trade-offs change between interviewers
The panel includes several managers and senior engineers discussing your past work, so the same project may come up more than once. Fix the figures and the key decision for each project in writing beforehand. When you answer the reported question about disagreeing with a lead, say what evidence you brought, what you conceded, and how you committed once the decision was made. An answer that only describes giving in, or only describes escalating, gives the interviewer little to credit.
Choose a category, try a prompt, then open its approach, worked solution or follow-up when you need it.
Can you validate a binary search tree?
Can you validate a binary search tree?
Approach
- State the target complexity and say which constraint rules the naive version out.
- Choose the data structure from the access pattern, not from familiarity.
- Name the brute-force solution and its complexity before improving on it.
Follow-up
- What is the worst case, and how likely is it on real data?
- How does this change if the input no longer fits in memory?
What are the best practices for specifying API endpoints?
What are the best practices for specifying API endpoints?
Approach
- Name the brute-force solution and its complexity before improving on it.
- Choose the data structure from the access pattern, not from familiarity.
- Restate the input: its shape, its size, and what is guaranteed about it.
Follow-up
- Which test case would catch an off-by-one here?
- What is the worst case, and how likely is it on real data?
How would you implement a Breadth-First Search (BFS) in a given scenar…
How would you implement a Breadth-First Search (BFS) in a given scenario?
Approach
- Walk one small example through your approach before writing the whole thing.
- Restate the input: its shape, its size, and what is guaranteed about it.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- How does this change if the input no longer fits in memory?
- Which test case would catch an off-by-one here?
Explain the two-pointer approach and its time complexity in the contex…
Explain the two-pointer approach and its time complexity in the context of a palindrome problem.
Approach
- Name the brute-force solution and its complexity before improving on it.
- Restate the input: its shape, its size, and what is guaranteed about it.
- Choose the data structure from the access pattern, not from familiarity.
Follow-up
- Which test case would catch an off-by-one here?
- How does this change if the input no longer fits in memory?
Derive per-account balances and catch unbalanced transactions
You are given ledger_entry rows streamed in entry_id order: transaction_id, account_id, direction (debit or credit), amount_minor (a positive int64), currency, business_date. Up to 500 million rows, at most 20 million distinct (account_id, currency) pairs, and the entries of one transaction are contiguous in the stream. In a single pass with no re-reads, return the closing balance per (account_id, currency) and the transaction_id of every transaction whose entries do not sum to zero within each currency. State your time and space bounds.
Approach
- Normalise the sign at read time from
direction, not from the amount:signed = +amount_minorfor debit,-amount_minorfor credit (state which convention you picked). The schema constrainsamount_minor > 0precisely so the sign lives in exactly one place. - Hold one hash map keyed
(account_id, currency)to an int64 running total. Twenty million keys at 16 bytes of payload plus map overhead is order 1 GB in most runtimes — quote the number, and offer the fallback: partition the stream byhash(account_id) % Pand run P passes for 1/P of the memory. - Ride the zero-sum check on the same pass. Because a transaction's entries are contiguous, keep a tiny
currency -> int64map for the currenttransaction_idonly, test it against zero on the boundary and at EOF, then clear it. That is O(currencies in one transaction), typically one or two. - Bound the arithmetic explicitly. Int64 holds about 9.22e18, so overflowing one account across 500 million entries needs an average of 1.8e10 minor units per entry — safe here, but use a checked add so an adversarial file fails loudly rather than wrapping.
- Complexity: O(n) time, O(distinct account-currency pairs) space, one sequential pass, no sort. The zero-sum check adds no asymptotic cost, which is the argument for doing it here rather than in a second job.
Worked solution 20 min
- Write the sign rule down in one sentence before any code, naming which side debit is positive on, and apply it at read.
- Implement with two maps —
balances: (account_id, currency) -> int64andtxn: currency -> int64— plus the currenttransaction_id. - On a change of
transaction_id, assert every currency intxnsums to zero, record the id if not, then clear. - Feed a fixture: one 2-entry transaction that balances; one 4-entry transaction with USD and JPY legs that balances within each currency; one 3-entry transaction off by a single minor unit.
- Re-run with the entries shuffled inside each transaction to prove the result is order-independent within a transaction.
Follow-up
- Entries of a transaction are no longer contiguous. What does the zero-sum check cost now, and which is cheaper: buffering open transactions or an external sort on
transaction_id? - How would you produce the same balances as of an arbitrary
business_datewithout a second full scan? - The job is restarted after a crash halfway through the file. What makes the second run produce identical output?
Decide whether balance is derived or materialised, then hold the floor
Authorisation needs an account's available balance inside an 80 ms budget at 3,000 requests per second; that account already has 200 million ledger_entry rows. A product rule says the balance may never fall below the account's negative overdraft limit. Decide whether the balance is summed from entries or held in a materialised account_balance(account_id, balance_minor, floor_minor, currency, version) row, and justify the choice from the read pattern. Then give the write path that holds the floor, naming the isolation level, the anomaly a weaker level permits, and the SQLSTATE you retry.
Approach
- Size the derived read before arguing about it: summing 200 million rows is not an 80 ms operation under any index, since even a covering index on (account_id, entry_id) still reads work proportional to the rows. The authorisation read pattern forces one materialised row fetched by primary key; the statement read pattern, which is low-rate and historical, stays derived from entries. That is the whole justification for the denormalisation, and its price is a write on that row per posting.
- Put the floor where it can be a single-row constraint: CHECK (balance_minor >= floor_minor) on account_balance, updated in the same transaction as the entries. As a predicate over a set of entry rows it cannot be a CHECK at all, which is the reason the materialised row earns its keep twice.
- Write the mutation as one statement: UPDATE account_balance SET balance_minor = balance_minor - $1, version = version + 1 WHERE account_id = $2 AND balance_minor - $1 >= floor_minor. Zero rows updated means refused. This is safe even under READ COMMITTED, because the UPDATE re-evaluates its predicate against the locked, post-update version of the row.
- Name the anomaly in the shape that is not safe: SELECT the balance, compute a new value in application code, then UPDATE to that constant. Every statement under READ COMMITTED takes a fresh snapshot, so two concurrent 60-unit withdrawals against 100 both read 100 and both write 40. The row then claims 40 while 120 has actually left, so the true position is -20, below a floor of 0, and the row it is checked against cannot show it. PostgreSQL REPEATABLE READ is snapshot isolation and aborts the loser with SQLSTATE 40001; SERIALIZABLE additionally closes write skew across two rows. Both require a bounded retry with backoff, and InnoDB REPEATABLE READ does not abort at all, so identical code changes behaviour on a different engine.
- For a two-account transfer, acquire the rows in a deterministic order such as ascending account_id; without it, opposing concurrent transfers deadlock and the database kills one with SQLSTATE 40P01. That is a retry, not a correctness failure, but it is a retry somebody has to write.
- Finish on the ceiling: the hot row admits one committed write per lock hold, so at a 2 ms hold it caps near 500 per second regardless of cores. Sharding into N sub-rows multiplies throughput and immediately makes the floor check cross-row again, which then needs the shard sum under SERIALIZABLE or a per-shard reserved allowance.
Worked solution 35 min
- Seed one account with balance_minor = 100 and floor_minor = 0, then run two concurrent 60-unit withdrawals under READ COMMITTED using SELECT-then-UPDATE and record the final balance.
- Replace it with the single-statement conditional UPDATE and re-run the same race, asserting on rows affected rather than on an exception.
- Re-run under SERIALIZABLE with the read-modify-write shape, count the 40001 aborts, and add a retry loop with a fixed attempt cap and jittered backoff.
- Measure committed writes per second against that single row, then write the drift query: sum signed entries per account and compare against balance_minor.
Follow-up
- Shard the balance into eight sub-rows. Write exactly what the floor check now does, and what it costs per authorisation.
- Someone proposes an AFTER INSERT trigger on ledger_entry to maintain the balance. What does that change about ordering, about batch posting, and about failure handling?
- How do you detect that the materialised row has drifted from the entries, how often do you run it, and on which replica?
Version a loan schedule instead of soft-deleting posted instalments
loan_instalment is keyed by (loan_id, schedule_version, instalment_no) and carries due_date, principal_minor, interest_minor, fee_minor, paid_principal_minor, paid_interest_minor, status, days_past_due, effective_from (date) and superseded_at (timestamptz). A borrower defers two payments on 2026-03-14, and instalments 1 to 6 already have allocations posted against them. Model exactly what the deferral writes, and write the query that returns the schedule as the borrower saw it on an arbitrary date. Say why stamping the old rows with deleted_at, or updating them in place, fails an audit.
Approach
- Treat the deferral as an insert, not an edit: write a complete new schedule_version with effective_from = the deferral instant on 2026-03-14, and in the same transaction stamp superseded_at on every row of the outgoing version with that identical instant, so the two version windows are half-open and adjacent rather than overlapping. Instalments 1 to 6 are reproduced unchanged, because they are what the borrower was told and what was posted to the ledger.
- Write the as-of read as a version selection, not a row filter: WHERE loan_id = $1 AND effective_from <= $2 AND (superseded_at IS NULL OR superseded_at > $2), then assert that exactly one schedule_version comes back - COUNT(DISTINCT schedule_version) = 1, not one row - so an overlapping window raises rather than silently returning two interleaved schedules under one instalment_no.
- Fix the type mismatch before writing that predicate: effective_from is declared date and superseded_at timestamptz, so comparing them casts the date at the session TimeZone and two readers in different zones select different versions near midnight. Put both columns in one domain, timestamptz, keep the window half-open with effective_from inclusive and superseded_at exclusive, and resolve a bare as-of date to an instant once, in the loan's booking timezone, at the edge of the system.
- Say what deleted_at loses. It records that a row stopped being current but not what replaced it or from when, it leaves every downstream query obliged to remember deleted_at IS NULL, and one query that forgets double-counts the schedule. Versioning puts the same information in the primary key where it cannot be forgotten.
- Close the arithmetic: under the loan's stated day-count convention the new version must still sum to outstanding principal plus scheduled interest to the minor unit, with the per-period rounding residual placed in one named instalment, conventionally the last, rather than smeared across the tail.
- Index (loan_id, effective_from DESC) for the as-of lookup, keep superseded versions online rather than archiving them, and enforce with a trigger that no UPDATE touches a row whose paid_principal_minor or paid_interest_minor is non-zero.
Follow-up
- What does days_past_due mean for an instalment that exists in two versions with different due_dates?
- A payment arrives allocated to an instalment_no that exists only in the superseded version. What do you do with it?
- How do you prove the ledger postings made under version 1 still reconcile once version 2 exists?
How would you approach the architecture of an application domain from …
How would you approach the architecture of an application domain from scratch?
Approach
- Name the failure you are designing for, then the recovery path.
- State the consistency you need, and where you are willing to be stale.
- Name the read and write paths separately; they rarely have the same bottleneck.
Follow-up
- How does this behave when that dependency is down for an hour?
- What breaks first when traffic grows ten times?
How do you ensure high availability and observability in a distributed…
How do you ensure high availability and observability in a distributed system?
Approach
- Choose a partition key and say what query it makes expensive.
- Name the failure you are designing for, then the recovery path.
- Fix the scope first: who calls this, how often, and what they do when it fails.
Follow-up
- What would you drop to keep the system up under load?
- How does this behave when that dependency is down for an hour?
Raise the write ceiling on one hot clearing account
The ledger posts balanced entry sets at 10,000 entries/s into append-only ledger_entry(entry_id, transaction_id, account_id, direction, amount_minor>0, currency, source_type, source_id, business_date, posted_at), roughly three entries per transaction. A materialised row per account carries the floor check available_minor >= floor_minor and is updated in the posting transaction. One pooled clearing account appears in 40 percent of transactions. Compute the throughput ceiling on that one row, then raise it. Name the isolation level each version requires and state precisely what sharding the balance costs the floor check.
Approach
- Compute the ceiling before designing anything. Throughput against a single row is one divided by the lock hold time, where the hold runs from first acquiring the row to commit, including the WAL flush. At a 2 ms hold that is about 500 commits/s on that row, and adding cores does not move it. Demand is 10,000 entries/s at three entries per transaction, so roughly 3,300 transactions/s, of which 40 percent touch this account: about 1,300/s against a 500/s ceiling. You are 2.6x over before any code exists.
- Shorten the hold before sharding, because it is free. Acquire the hot row last in the transaction and hold it for exactly one statement: UPDATE balance SET available_minor = available_minor - $1 WHERE account_id = $2 AND available_minor - $1 >= floor_minor. This is correct at READ COMMITTED, because an UPDATE that blocks on a concurrent writer re-evaluates its WHERE clause against the newly committed row version rather than its original snapshot.
- Name what the broken version needs. SELECT the balance, compute in application code, then UPDATE loses updates at READ COMMITTED, since the second statement takes a fresh snapshot. Making that shape safe requires REPEATABLE READ or SERIALIZABLE plus a bounded retry loop on SQLSTATE 40001, which is the part most implementations omit; SELECT ... FOR UPDATE also works but is simply the same lock held for longer. Note that the identical application code changes behaviour on a different engine: InnoDB's REPEATABLE READ does not abort the loser.
- Order locks deterministically on any two-account movement, always ascending by account_id. Concurrent opposing transfers without that ordering deadlock with SQLSTATE 40P01, and the deadlock rate rises with exactly the concurrency you added to gain throughput.
- Shard the hot balance into N sub-rows keyed (account_id, shard_no), writers choosing a shard by hash of transaction_id. Per-shard hold time is unchanged, so capacity rises roughly N-fold, and at N=16 you clear 1,300/s with headroom. The cost is precise: the floor is now a predicate over the sum of N rows and is no longer expressible as a single-row CHECK, so it must be paid for deliberately. Either give each shard its own floor, which rejects postings while other shards hold headroom, or add a slow path that locks all shards in ascending shard_no under SERIALIZABLE, rebalances and retries on 40001. For a clearing account whose floor is effectively unreachable, dropping the per-shard floor and enforcing it as a monitored invariant is a defensible stated choice; for a customer deposit account it is not.
- Keep the entries immutable throughout: no UPDATE or DELETE grant on ledger_entry plus a trigger, corrections as reversing transactions, and partitioning by business_date so index writes stay in the current partition and retention is a DETACH.
Worked solution 45 min
- Run two concurrent sessions doing SELECT then UPDATE to a computed value against the same account under READ COMMITTED and observe one update lost.
- Replace it with the single atomic conditional UPDATE and re-run; then run the original read-modify-write under REPEATABLE READ with a bounded retry on 40001.
- Measure committed transactions per second against the single pooled row at 1, 8, 32 and 128 concurrent writers, recording mean lock hold time.
- Shard into 16 sub-rows hashed on transaction_id, re-measure, and write the query that answers the true available balance.
- Run concurrent opposing two-account transfers with and without ascending account_id lock ordering.
Follow-up
- While the balance is sharded, what query answers the account's true available balance, and what does it cost?
- A transaction has legs in two currencies. What does the balanced-to-zero check mean now, and where does the rounding residual go?
- What does the nightly reconciliation compare the sharded balance against?
One merchant stops receiving webhooks while the rest deliver
The outbound relay delivers outbox_event rows to merchant endpoints preserving per-destination ordering. One merchant has received nothing since 09:14; every other destination is current. The relay process is healthy and CPU is flat. For that merchant, unpublished rows are accumulating, the oldest has attempts = 412 with backoff capped at 30 seconds, and last_error is the same string on every attempt. Diagnose in order, and state what you change so one undeliverable row can never stall a destination again.
Approach
- Separate destination down from one row undeliverable before anything else, because they are identical from the merchant's side. The discriminator is the error class: a transport error (connection reset, 503, TLS failure) varies and implicates the endpoint, while an identical deterministic error on every one of 412 attempts implicates the row.
- Read the blocked set in version order: SELECT event_id, aggregate_id, aggregate_version, attempts, last_error FROM outbox_event WHERE published_at IS NULL AND aggregate_id = $1 ORDER BY aggregate_version LIMIT 5. If everything is queued contiguously behind one row, the ordering guarantee is working exactly as designed and the defect is that it has no exit.
- Confirm outside the relay: replay that single payload against the destination by hand. Reproducing the identical error proves the row, not the transport, and stops the escalation to the merchant before it is sent.
- Decide what skipping means under a per-destination ordering contract. You cannot simply publish the next row; you either park the poison row in a dead-letter table and accept that the consumer sees an aggregate_version gap, or you deliver a degraded payload. Name which, and say what the consumer does with the gap.
- Fix the real defect, which is unbounded retry, not the malformed payload. Bound retries by attempt count or row age, park on exhaustion, alert on the age of the oldest unpublished row per destination, and validate the payload at INSERT time so it cannot be written inside the state-change transaction at all.
Follow-up
- The consumer orders on aggregate_version. What does it do with the gap your dead-letter created, and how does it learn the gap is intentional?
- How do you distinguish this from a destination returning 4xx on every request during its own deploy?
- Does the alert go on attempts, on the age of the oldest unpublished row, or both, and what are the thresholds?
For someone fluent in a dynamic language who has shipped real work but has never had to say what the runtime is doing underneath. The week is built on measuring and deliberately breaking things, because the questions that expose this background are the ones where the interviewer asks why a second time.
Prepare, practise & reflect
One practical outcome each day. Spend longer where you need it.
0 / 7 done01Recruiter screen and stack mapping
- Map your experience against the listed must-haves (Java, Kotlin or Node.js; SQL and database design; RESTful APIs) and the nice-to-haves (AWS, GraphQL, React), with one project for each
- Write and time a background walkthrough that names those projects, and rehearse it against the question-bank prompt 'Walk Through Your Background'
- List your hard constraints and the questions to ask about the next stage: assessment or phone format, allowed languages, and whether SQL or ActiveRecord is included
Deliverable: A one-page stack map, a rehearsed walkthrough, and a written list of screen questions and constraints.
Practice prompt ↗Practice prompt ↗Worked solution ↗02Trees and graphs from the reported coding set
- Solve 'validate a binary search tree' twice, with bounds and with in-order traversal, and test it on the 5 / 4 / 6-with-left-3 counterexample
- Implement BFS for a grid or graph scenario, marking visited on enqueue, and state O(V + E) time and O(V) extra space for the queue and visited set
- Solve lowest common ancestor from the question bank and explain how the recursion returns found nodes upward
Deliverable: Three working solutions, each with stated complexity and a list of edge cases you tested.
Practice prompt ↗Practice prompt ↗03Strings, arrays and explaining your approach
- Write the two-pointer palindrome check, including skipping non-alphanumeric characters, and explain O(n) time and O(1) extra space out loud
- Solve 'first unique character index' from the question bank with a frequency count in linear time
- Practise the question-bank prompt on explaining how you solve coding problems: clarify, brute force, optimise, test, all narrated on one problem
Deliverable: Two solutions and a recorded narration of one problem from clarifying questions to tests.
Practice prompt ↗Practice prompt ↗04Live SQL, ActiveRecord and data correctness
- Write five queries against a small schema (joins, grouping, a window function, a filtered aggregate, an upsert) and the ActiveRecord version of each, noting any N+1 risk
- Work the worked exercise 'Decide whether balance is derived or materialised, then hold the floor' and reproduce the lost update in two sessions
- Work the coding exercise 'Derive per-account balances and catch unbalanced transactions' and state its time and space bounds
Deliverable: A sheet of paired SQL and ActiveRecord queries, plus notes on both worked exercises.
Practice prompt ↗Practice prompt ↗Worked solution ↗05API endpoints, OOP and Java depth
- Specify endpoints for one resource: naming, HTTP methods, status codes, pagination, versioning, idempotency keys on writes, and error bodies
- Model the classes for 'design a chess game' and explain where inheritance helps and where composition fits better
- Prepare an in-depth explanation of your main language (for Java: memory model, collections, exceptions), since the bank includes an 'explaining Java deeply' question
Deliverable: An endpoint spec, a class diagram for chess, and a one-page language-depth crib sheet.
Practice prompt ↗Practice prompt ↗06Reported design categories
- Architect an application domain from scratch: entities, service boundaries, API surface, schema, then the failure you design for
- Compare two schemas for financial data (in-place updates against append-only history with versioning), using the loan-schedule versioning drill as a reference
- Work the design exercise 'Raise the write ceiling on one hot clearing account', then answer the high-availability and observability question starting from failure modes and alerts
Deliverable: Three design write-ups, each naming a trade-off and the failure it handles.
Practice prompt ↗Practice prompt ↗07Panel preparation and a full mock
- Prepare stories for the reported behavioral questions: explaining a technical concept to a non-technical stakeholder, disagreeing with your lead, and pivoting after requirements changed
- Write a one-page figures sheet for each project you will discuss, plus two sentences on your professional goals
- Run a mock panel where two people ask about the same project separately, then compare your two answers for numbers or trade-offs that changed
Deliverable: Written behavioral stories, project figure sheets, a goals statement, and notes from the mock.
Practice prompt ↗Practice prompt ↗Worked solution ↗Expand any day for tasks and deliverables. Your progress is saved on this device.
The panel is described as a discussion of your past work and professional goals with managers and senior engineers, so prepare stories where you made the decision, not stories where you carried out someone else's plan. For each story, state the constraint, the option you rejected, the evidence you used, and the result you can measure. Keep the figures consistent, because more than one interviewer may ask about the same project.
How do you handle situations where you disagree with a technical decis…
How do you handle situations where you disagree with a technical decision made by your lead?
Approach
- Name the disagreement and how you resolved it with evidence.
- Close with what you would do differently, concretely.
- Give the blast radius: what could have broken, and what you measured.
Follow-up
- What did you decide not to do, and why?
- What would you do differently if you ran that again?
Tell us about a project where you had to pivot due to changing require…
Tell us about a project where you had to pivot due to changing requirements.
Approach
- Pick a story where you made the decision, not one where you watched it.
- Give the blast radius: what could have broken, and what you measured.
- Close with what you would do differently, concretely.
Follow-up
- What did you decide not to do, and why?
- How did you know your change caused the improvement?
Resolve a review disagreement over isolation level
A colleague's pull request reads a balance, compares it to a floor in application code, then issues an UPDATE with the computed value, all under PostgreSQL's default READ COMMITTED. You comment; they reply that staging has never produced a negative balance. Describe a code review disagreement where you were confident and the author was not convinced. State how you made the failure concrete rather than theoretical, how many rounds it took, at what point you would have escalated or approved anyway, and what you conceded to the author.
Approach
- The probe is whether you can convert a correctness objection into something reproducible instead of a stalemate of opinions. Name the anomaly by its mechanism: under READ COMMITTED each statement takes a fresh snapshot, so two sessions can both read balance 100, both compute 100 minus 80, and both write 20.
- Address the staging evidence directly rather than dismissing it. Staging concurrency on one account row is effectively one, so the absence of the anomaly there is expected under both the broken and the correct implementation. That is the sentence that usually ends the argument.
- Reproduce it in two psql sessions and paste the interleaving into the review. A twelve-line transcript settles in one round what three paragraphs of theory will not settle in four.
- Offer the fix as a choice with its trade-off, not as a verdict: an atomic UPDATE ... SET balance = balance - $1 WHERE account_id = $2 AND balance - $1 >= $3 with a rowcount check keeps it single statement and needs no retry; SELECT ... FOR UPDATE serialises the row and lets you compute in application code; SERIALIZABLE covers the multi-row version of the predicate but requires a bounded retry on SQLSTATE 40001 that someone has to actually write.
- Say where your bar is. Correctness on money is a blocking comment, style is not, and a strong answer states that boundary before the disagreement rather than discovering it during one.
- Name what you conceded. The author was usually right about something (scope, naming, the follow-up being separable), and saying so is what makes the blocking comment land next time.
Follow-up
- The author switches the service to MySQL. Which of the three fixes still behaves the same, and which changes silently?
- The same endpoint later transfers between two accounts. What do you now require in the review?
- How do you keep this from being relitigated in every future pull request?
- 01
Describe a time you had to explain a complex technical concept to a non-technical stakeholder.
- 02
How do you handle situations where you disagree with a technical decision made by your lead?
- 03
Tell us about a project where you had to pivot due to changing requirements.
- 04
Walk me through your background and how it fits this role.
- 05
What are your strengths and weaknesses?
- 06
Describe your project management experience: how you planned the work and handled risk.
Is this an official Onemain Financial interview guide?
No. It is PracHub's own research and practice material for the Software Engineer role at Onemain Financial. Rounds and questions reflect what candidates have reported, not a process Onemain Financial has published, and they change over time. Confirm the current format and scope with your recruiter.
PracHub interview research ↗How long does the interview process typically take?
Candidate reports put it at roughly three to five weeks across three stages: a recruiter screen, a technical assessment or phone interview, and a panel. Another note in the same source says three to four weeks. Ask the recruiter for the expected schedule at the end of the screen, especially if you have another deadline.
PracHub interview research ↗What is the best way to prepare for the coding portion?
Practise the reported coding questions first: validating a binary search tree, the two-pointer approach to a palindrome with its time complexity, and BFS in a scenario. Then add lowest common ancestor and first unique character from the question bank. Be ready to discuss your code in detail, and give readability and tests as much attention as correctness.
PracHub interview research ↗Which language should I use?
The role notes list Java, Kotlin or Node.js as must-haves, and the question bank includes a question on explaining Java in depth. Use the language you can explain most thoroughly, including its collections, error handling and memory behaviour. One reported prompt pairs SQL with ActiveRecord, so if ActiveRecord is new to you, ask the recruiter whether it will be part of your session.
PracHub Software Engineer practice ↗Is system design part of the process?
Design questions are reported: architecting an application domain from scratch, database schema trade-offs for a financial application, and high availability and observability in a distributed system. The sources do not say which stage asks them. Prepare them before the technical assessment and review them again before the panel.
PracHub Software Engineer practice ↗What should I prepare for the panel?
Candidates describe an onsite or virtual panel with managers and senior engineers about your past work and professional goals. Prepare a figures sheet for each project you will discuss, stories for the reported behavioral questions (explaining a technical concept to a non-technical stakeholder, disagreeing with your lead, pivoting on changed requirements), and a specific statement of your goals.
PracHub Software Engineer practice ↗Sources & methodology 3 sources ↗
Official role evidence, timestamped platform data and clearly labeled preparation advice.
- 01PracHub interview research ↗
PracHub editorial research into this company and role, maintained with this guide. Candidate-reported, not an employer publication.
platform · Accessed 2026-09-24 - 02PracHub Software Engineer practice ↗
Cross-company practice questions for this role.
platform · Accessed 2026-09-24 - 03PracHub interview preparation framework ↗
The framework the preparation plan follows.
platform · Accessed 2026-09-24