cswork / libraryOpen practice workspace

Interview guides · Bristlecone

Bristlecone Software Engineer Interview Guide

Bristlecone Software Engineer interview guide: reported rounds, Java and OOP, SQL, concurrency and behavioral questions, plus a 7-day prep plan.

The 60-second brief

Bristlecone builds software for enterprise clients, and candidates describe the Software Engineer work as supply-chain and enterprise problems solved through code. The projects they list include system integrations, enterprise resource planning modules and custom application development. In this role you take features from requirements through implementation and testing, work with product managers and QA, and contribute through code reviews and documentation. Required skills reported for the role are Java, JavaScript or C#, SQL and object-oriented programming, with cloud platforms, Spring Boot, React or Angular and CI/CD pipelines listed as extras.

This guide is for Software Engineer candidates, fresh graduates or experienced engineers, preparing for the Bristlecone loop. It covers the three reported stages, the core programming and project questions candidates report, the behavioral questions, and a seven-day plan that moves from fundamentals (OOP, Java, SQL, concurrency, data structures) to project stories and behavioral answers.

Candidates report three stages over roughly 3-5 weeks: a screening phase, a set of technical interviews, and behavioral evaluations. The process can differ by location and team, and can mix virtual and face-to-face meetings.

Focus areas

  • Data Structures & Algorithms (DSA / DSA questions)
  • Channels (Go channels) / Concurrency Primitives
  • Java

Overview

The reported questions fall into three groups. Core programming covers abstract class versus interface, exception handling, the lifecycle of an object or page in your preferred framework, multithreading and concurrency, and a real-world example of polymorphism. Project and role questions cover a complex project you worked on, integration challenges in an enterprise setting, your own responsibilities, optimizing code or a database query, and keeping code scalable and maintainable. Behavioral questions cover motivation, disagreements, five-year plans, learning under deadline pressure and managing priorities.

Candidates are told to be ready to discuss every technology on their resume in depth. That makes your resume the first study guide: list each language, framework, database and tool you claim, and for each one prepare how it works internally, a project where you used it, and one thing that went wrong. For Java that means OOP, memory management and collections, plus the architecture of the JVM, which candidates report as a sample topic.

Reported technical areas also include arrays, linked lists and trees, programs such as prime-number checks and string manipulation, SQL queries, joins and database design, and optimizing a slow SQL query. Topic breakdowns for this role also list Go channels and goroutines under concurrency, so if Go is on your resume, rehearse it alongside Java threads. For more senior roles, candidates report design discussion on scalability, microservices, integration and trade-offs such as consistency versus availability, with examples like a booking system or an e-commerce cart.

Because the work is integrations, ERP modules and custom applications built with product managers and QA, the strongest answers explain why you chose a design, not just what you built. Prepare to explain trade-offs aloud, and use STAR for the behavioral stories.

The Bristlecone interview process

Candidates describe three stages in this order: a screening phase, technical interviews, and behavioral evaluations. Details vary by location and team.

Screening Phase

Candidates report that the loop opens with either an aptitude test or a telephonic technical screen. No format or content for the aptitude test is reported, so ask your recruiter what to expect. A phone screen means explaining ideas aloud without an IDE, so core concepts such as abstract class versus interface and exception handling need to come out cleanly in speech, and you should be able to talk through a short program such as a prime-number check or a string manipulation.

What to demonstrate

  • Command of fundamentals such as OOP, collections and exception handling
  • Ability to explain code and concepts verbally on a call
  • Basic problem solving on short programs

How to prepare

  • Rehearse the abstract class versus interface, polymorphism and exception-handling answers out loud, each with a short code example you can describe without seeing it.
  • Write the prime-number check and two common string manipulations from memory in your main language, and say the time complexity of each.
  • Ask the recruiter whether the first stage is an aptitude test or a phone screen, and what format it takes.

Technical Interviews

Candidates describe several technical interviews built around coding assessments and project-based discussions. Reported areas are OOP, memory management and collections; arrays, linked lists and trees; SQL queries, joins, database design and tuning a slow query; and JVM architecture. Go channels and goroutines appear in topic breakdowns, and for senior roles design discussion on scalability, microservices and integration. Work through each area in your main language and tie each answer back to a project from your resume.

What to demonstrate

  • Quality and clarity of live coding on data structure problems
  • Depth in the language and runtime you list on your resume
  • SQL fluency, including joins and query optimization
  • Reasoning about project decisions and, for senior roles, design trade-offs

How to prepare

  • Solve linked-list and tree problems in your main language, and write a basic thread-safe shared counter before explaining why it is safe.
  • Prepare a SQL tuning walkthrough: a slow query, its execution plan, the index or rewrite you applied, and the before-and-after result.
  • Write a one-page walkthrough of your most complex project covering requirements, your own contribution, one design decision, one failure, and the outcome.
  • For senior roles, practise sketching a booking system or e-commerce cart with services, data stores and the consistency trade-offs you would accept.

Behavioral Evaluations

Behavioral evaluations are reported as an assessment of how well you fit a collaborative team culture. Candidates report questions on motivation for joining, disagreements, career direction, learning new technology under a tight deadline and time management. Plan structured stories using STAR (Situation, Task, Action, Result), each with a decision that was yours and a result you can state in numbers or concrete outcomes.

What to demonstrate

  • Communication of technical and non-technical information to different audiences
  • Teamwork and handling of disagreement
  • Self-awareness about growth, priorities and time management

How to prepare

  • Write five STAR stories covering a disagreement, a tight deadline with new technology, a performance or query optimization, a competing-priorities week, and a mistake you owned.
  • Write a three-sentence answer to the question about why you want to join Bristlecone using only facts you can support: the enterprise, supply-chain and integration work and the stack on the role.
  • Practise each story aloud until the situation takes only a few sentences and most of the time goes to your reasoning and the result.

Where candidates lose points

Listing technologies on the resume that you cannot explain internally

Candidates are told to expect depth on every technology they list. For each item, prepare how it works, a project that used it, and a failure. Remove anything you cannot defend.

Reciting a textbook definition for abstract class versus interface or polymorphism with no example

Give a definition, a code or domain example (such as payment or shipment types), when you would choose each option, and one trade-off such as multiple inheritance of type versus shared state.

Saying you have used multithreading without naming what was shared or how it was protected

Prepare one concrete case: the shared data, the race that could occur, the lock, atomic or channel you used, and how you tested it. If Go is on your resume, cover goroutines and channels the same way.

Telling an optimization story with no baseline, no diagnosis and no measured result

Present it in order: baseline, how you found the cause (execution plan or profiler), the change, the before-and-after numbers, and the side effects.

Answering the behavioral questions with generic lines, such as general praise for the company or a job title for the five-year question

Use facts you can support about the work, such as integrations, ERP modules and cross-functional delivery, and describe the skills and scope you want to grow into.

Questions & practice

Describe your experience with multithreading and concurrency.

Medium · Technical & Core Programming

Approach

  1. Anchor the answer in one real case. Say what ran in parallel, what state was shared, and what protected it. The reported topics include Java and Go concurrency (goroutines and channels), so have an example ready for whichever of the two is on your resume.
  2. Java toolkit and when each fits. synchronized or ReentrantLock gives mutual exclusion. volatile gives visibility but not atomicity, so count++ on a volatile is still a race. AtomicInteger or LongAdder fit counters. ConcurrentHashMap.computeIfAbsent makes a per-key update atomic. ExecutorService with a bounded queue keeps fan-out from exhausting memory, and CompletableFuture composes async steps.
  3. Go toolkit. Goroutines are cheap, but each needs an owner that knows when it ends. An unbuffered channel is a synchronous handoff, and a buffered channel decouples producer and consumer up to its capacity. Only the sender should close a channel. select with context cancellation stops work cleanly, and sync.WaitGroup or errgroup waits for completion. I run tests with go test -race.
  4. Name the failure modes and how you prevent them. Avoid deadlock by acquiring locks in a fixed global order or using tryLock with a timeout. Avoid races by keeping state confined to one thread or goroutine, or immutable. Avoid starvation and pool exhaustion by never blocking a pool thread on work queued to the same pool.
  5. Explain how you verified it. Take a thread dump (jstack) to find a deadlock, run a load test to expose contention, and write tests that repeat the concurrent path many times rather than trusting one green run.

Follow-up

  • What happens when you send on a closed channel, receive from a closed channel, or send on a nil channel in Go?
  • How would you size a thread pool for CPU-bound work compared with work that mostly waits on a database or HTTP call?
  • Two threads each hold one lock and wait for the other's: how do you detect this in production and change the code so it cannot recur?

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?

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

  1. 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.
  2. 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.
  3. 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.
  4. 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?

Write the update path that detects a concurrent edit

Medium · optimistic concurrency · transactional outbox · audit history

resource carries version INT NOT NULL DEFAULT 1. resource_revision holds revision_id, resource_id, version, actor_user_id, change_kind, patch JSONB, request_id, created_at with UNIQUE (resource_id, version). outbox_event holds aggregate_type, aggregate_id, aggregate_version, event_type, payload, status. A PUT carries the version the client read. Write the exact statements for the single transaction that applies the edit, records the revision and enqueues 'resource.updated', and give the handler's branch on zero affected rows. Then say what PostgreSQL 16 does under READ COMMITTED when two of these updates hit one row at once.

Approach

  1. One transaction, three writes, no network call inside it: UPDATE resource SET title = $3, version = version + 1, updated_at = now() WHERE resource_id = $1 AND tenant_id = $4 AND version = $2; then INSERT the resource_revision row at version $2 + 1; then INSERT the outbox_event row at the same aggregate_version. The event goes to a table rather than a broker because no transaction spans both.
  2. Branch on the affected-row count before doing anything else. Zero has three causes — stale version, wrong tenant, row gone — so re-read once and map to 409 carrying the current version, or 404 for an id outside the caller's tenant, which also stops the endpoint confirming that another tenant's id exists.
  3. State the engine behaviour instead of assuming it. Under READ COMMITTED the second UPDATE blocks on the row lock, and when the first commits PostgreSQL re-evaluates the WHERE clause against the newly committed row, so the version predicate now fails and the statement reports zero rows. Under REPEATABLE READ the identical collision raises SQLSTATE 40001 instead, so the handler must fold both shapes into one conflict response.
  4. Keep UNIQUE (resource_id, version) even though the predicate already serialises writers. It is what makes a lost update unwritable if any other path ever reaches the revision table, and it converts a logic bug into 23505 rather than into a silently missing history row.

Follow-up

  • A client sends the version it read ten minutes ago and the resource has moved three versions. What is in your 409 so it can resolve the conflict without a full re-fetch?
  • Two editors, two disjoint fields, no overlap. Does your answer still refuse the second write, and should it?

Hold a per-tenant active cap against concurrent creates

Hard · write skew · isolation levels · constraints

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

Approach

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

Follow-up

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

Explain the difference between an abstract class and an interface.

Medium · Technical & Core Programming

Approach

  1. Lead with the modelling difference. An abstract class is a partial implementation that shares state across an is-a family. An interface is a contract for a capability that unrelated classes can take on. In Java a class extends exactly one class but can implement many interfaces.
  2. List what each can hold in Java. Abstract classes can have constructors, instance fields, and private, protected or package members. Interface fields are implicitly public static final constants. Since Java 8 interfaces can also have default and static methods, and since Java 9 private methods.
  3. Since default methods arrived, the real deciding line is state and construction. If subclasses need shared mutable fields or a constructor that enforces an invariant, use an abstract class. If you only need behaviour any type can adopt, such as Comparable or Runnable, use an interface.
  4. Give a concrete pairing. I would define an interface such as Exporter with export(), and an AbstractFileExporter that holds the shared path, encoding and buffered-writer setup. Callers depend on the interface, and the abstract class only removes duplication between implementations.
  5. If C# is your main language, map it across. A C# class has one base class and many interfaces, and C# 8 added default interface members, so the same state-versus-contract argument applies.

Follow-up

  • What happens when a class implements two interfaces that declare the same default method, and how do you call one of them explicitly?
  • Can an abstract class have a constructor if it can never be instantiated, and when does that constructor run?
  • Why must a functional interface used with a lambda have exactly one abstract method?

Can you explain the lifecycle of an object or page in your preferred framework?

Medium · Technical & Core Programming

Approach

  1. Pick one framework you have shipped with and name it before you start, then go deep on it rather than skimming three. For a Java role, the plain object lifecycle or the Spring bean lifecycle is the safest choice. For frontend work, use React or Angular.
  2. Plain Java object: the class is loaded and initialised, then new allocates memory and runs the constructor chain (super first, then field initialisers, then the constructor body). The object lives while it is reachable and becomes eligible for GC once it is unreachable. finalize() is deprecated (for removal since Java 18), so I release resources with try-with-resources or a Cleaner.
  3. Spring singleton bean: instantiate, inject dependencies, then the Aware callbacks. Next come BeanPostProcessor before-init, @PostConstruct, afterPropertiesSet and the custom init-method, then BeanPostProcessor after-init, which is where AOP proxies are created. The bean is used, and on shutdown @PreDestroy, destroy() and the destroy-method run. The container does not call destroy callbacks on prototype-scoped beans.
  4. React function component: render, commit to the DOM, then effects run. When dependencies change, the previous effect's cleanup runs before the new effect. On unmount the cleanup runs one last time. Angular's hook order is ngOnChanges, ngOnInit, ngDoCheck, then the content and view init/checked hooks, then ngOnDestroy.
  5. Tie the lifecycle to a bug you have hit, such as a subscription or timer never released in cleanup, or a field read in a constructor before injection had set it. That shows you used the lifecycle in practice.

Follow-up

  • Why does @Transactional have no effect when a bean calls its own annotated method, and where in the lifecycle does the proxy come from?
  • In React 18 development mode, why might an effect run twice on mount, and what does that tell you about your cleanup?
  • When exactly does a Java object become eligible for garbage collection, and why can a static collection cause a memory leak?

Provide a real-world example of polymorphism.

Medium · Technical & Core Programming

Approach

  1. Define it in one line, then split the kinds. Runtime (subtype) polymorphism is method overriding resolved by the object's actual class through dynamic dispatch. Compile-time polymorphism is overloading, resolved from the static argument types. Parametric polymorphism is generics.
  2. Give a domain example rather than Animal and Dog. Take a ShippingCarrier interface with quoteRate(Shipment) and implementations for each carrier. The checkout code holds a List<ShippingCarrier> and calls quoteRate on each without knowing which carrier it has.
  3. State the payoff and the trade-off. Adding a carrier means adding a class, with no edit to an if/else or switch over carrier types, which is the open/closed principle and the Strategy pattern. The cost is more types and indirection, which is not worth it when the set of cases is small and fixed.
  4. Cover the Java edge cases: static methods are hidden, not overridden. Private and final methods cannot be overridden. Fields are not polymorphic, so their access is resolved by the reference type. Calling an overridable method from a constructor can run subclass code before the subclass fields are initialised.
  5. If Go is your language, explain that interfaces are satisfied implicitly. Any type with the right method set fits, so polymorphism comes from small interfaces such as io.Reader rather than class hierarchies.

Follow-up

  • Given overloaded methods print(Object) and print(String), which one runs for Object o = "x"; print(o), and why?
  • When would you choose a switch over sealed types with pattern matching instead of virtual methods?
  • How does polymorphism make this code easier to unit test?

Walk us through a complex project you led or contributed to.

Medium · Project & Role-Specific Experience

Approach

  1. Open with a short frame: the business problem, who used the system, and the scale in numbers (requests, records, users or integrations). Then sketch the architecture as components and data flow before any detail.
  2. Separate your work from the team's. Say which modules, services or tables you designed or wrote, and which decisions were yours, using I for your own actions.
  3. Pick the hardest technical decision and walk through it: the options you considered, why you chose one (latency, consistency, cost, team skills), and what that choice made harder later.
  4. Close with a measurable result and one thing you would do differently. Examples include a latency drop, a defect rate, a manual step removed, or a batch window shortened.
  5. Prepare to defend every technology the project names. Candidates report detailed questions on what they list on their resume, so rehearse why you used each framework, database and cloud service, not just that you used it.

Follow-up

  • Why that database or framework rather than the obvious alternative, and what would make you switch?
  • What went wrong in production on this project, and how did you find the root cause?
  • If load grew tenfold, which component would fail first and what would you change?

How do you approach integration challenges in an enterprise environment?

Medium · Project & Role-Specific Experience

Approach

  1. Start from the contract. Write down the schema (OpenAPI, XSD or an event schema), the field mapping between systems, and a versioning rule, and validate every inbound message against it at the boundary so bad data is rejected with a clear error before it reaches business logic.
  2. Choose sync or async on purpose. Use a REST call when the caller needs the answer now. Use a queue or event stream when the target can be slow or offline, as ERP and partner systems often are, so a downstream outage does not take your service down with it.
  3. Make retries safe. Use timeouts on every outbound call, retries with exponential backoff and jitter, and a circuit breaker for a failing dependency. Give each request an idempotency key so the receiver deduplicates, because at-least-once delivery means duplicates will arrive.
  4. Avoid the dual write. If a database update and a published message must both happen, write the event to an outbox table in the same transaction and publish from it. Send messages that keep failing to a dead-letter queue with enough context to replay them.
  5. Plan for drift and diagnosis. Run a scheduled reconciliation job that compares counts or totals between the two systems, and pass a correlation ID through every hop so one transaction can be traced across logs.

Follow-up

  • The target system is down for several hours: what happens to the messages, and how do you catch up without flooding it when it returns?
  • The partner sends the same order twice and occasionally out of order: how does your consumer handle both?
  • The partner adds a required field without notice: how do you find out, and how do you keep both versions working?

What are your specific roles and responsibilities within your current team?

Medium · Project & Role-Specific Experience

Approach

  1. In one sentence, give the team's purpose and what it ships. Then give your scope in concrete nouns: the services, modules, integrations or reports you own.
  2. Walk through the delivery cycle as you live it. Cover requirement clarification with product or business analysts, design, coding, unit and integration tests, code review (given and received), the CI/CD pipeline, and handoff to QA or release.
  3. Name responsibilities beyond your own tickets, and only those you actually hold. Examples are on-call or production support, reviewing others' pull requests, mentoring, and documentation.
  4. Back each claim with one specific example: a decision you made, an incident you resolved, or a process you changed, with its result. That keeps the answer from reading as a job description.
  5. Be exact about seniority. Do not claim to lead what you only contributed to, because a follow-up on the technical details will expose the gap.

Follow-up

  • Walk me through how a change goes from your branch to production on your team.
  • What part of the system would be hardest for your team to support if you left tomorrow?
  • Which decision in your current role did you make on your own, and who did you have to convince?

How do you ensure your code is scalable and maintainable?

Medium · Project & Role-Specific Experience

Approach

  1. Split the question in two and answer both. Maintainable means the next engineer can change it safely. Scalable means it keeps meeting its latency and throughput targets as load or data grows. The techniques differ, and you should say so.
  2. Maintainability: small cohesive classes with one reason to change, and dependencies injected through interfaces so they can be mocked in unit tests. Add integration tests on the integration boundaries, linting and static analysis in CI, code review on every change, and short notes on why a design was chosen.
  3. Scalability in the service: keep instances stateless, with session state in a shared store, so you can scale horizontally behind a load balancer. Move slow or bursty work to a queue with workers, and pool database and HTTP connections.
  4. Scalability in the data layer: add indexes that match the real WHERE and ORDER BY patterns (check with EXPLAIN), and remove N+1 queries by batching or joining. Paginate with keyset pagination instead of large OFFSET values, and cache hot reads with an explicit TTL or invalidation rule.
  5. Measure before and after. Profile to find the actual hotspot, load-test against a target such as p95 latency, and watch metrics in production. Optimising without a measurement trades readability for speed you may not get.

Follow-up

  • Tell me about a refactor you did: what made the code hard to change, and how did you keep behaviour the same while you changed it?
  • Your service is fine at current load: what do you expect to break first at ten times the traffic, and how would you confirm it before it happens?
  • How do you decide whether to add a cache, and what is your invalidation strategy when the underlying row changes?

One log partition stops advancing while the others drain

Easy · poison message · head-of-line blocking · consumer lag

Search results for a subset of tenants are hours stale; the rest are current. The projection consumer reports lag of zero on 15 of 16 partitions and 400,000 on one. Its error rate is flat and its CPU is idle. outbox_event has no pending rows older than a second, so the relay has published everything it holds. Identify the mechanism, give the ordered checks, and state what you do in the first ten minutes versus what you change permanently.

Approach

  1. Read the lag distribution first. A slow consumer lags everywhere; zero on fifteen partitions and 400,000 on one is not throughput. Idle CPU on the stuck partition means the consumer is not advancing its offset at all, which points at one message it cannot get past rather than at a rate problem.
  2. Exonerate the producer before touching the consumer. No pending outbox rows older than a second means the relay published, so the event exists in the log. This separates never sent from sent and never applied, which are different code paths and usually different owners.
  3. Read the message at the stuck offset and the handler's log lines for its event_id. A flat error rate with no progress has two explanations and you must distinguish them: the handler is throwing and the retry loop is swallowing it, or the handler is blocking on something and never returning. Idle CPU with no error lines favours the second.
  4. Mitigate before diagnosing further. Move the offending event to a dead-letter store and commit the offset past it. Adding consumers does nothing here, because a partition is consumed by exactly one member of the group, and the blast radius is every aggregate hashed to that partition, not only the aggregate that produced the bad event.

Follow-up

  • The dead-lettered event carried aggregate_version 7 and the projection had applied 6. What must the replay do differently if 8 and 9 landed in the meantime?
  • How do you show staleness to the user while the partition is behind, given the API already returns the projection's watermark?

Suggested practice checklist

The plan follows the three reported stages: fundamentals and resume depth first, then coding, SQL and projects, then behavioral stories.

Day 1 — Map the loop and audit your resume

  • Write the three reported stages and the question groups candidates report for each area, then mark your weakest.
  • List every technology on your resume and write, for each one, a project, how it works internally and one failure.

Deliverable: A one-page loop map and a resume audit table with a project and a failure story for each technology.

Day 2 — OOP, Java and exceptions

  • Answer aloud with code examples: abstract class versus interface, polymorphism with a real-world case, and exception handling.
  • Explain the lifecycle of an object or page in your preferred framework, and the JVM architecture, memory management and collections.

Deliverable: Spoken answers recorded for four core programming questions, each with a short code example.

Day 3 — Multithreading and concurrency

  • Write a thread-safe counter and a producer-consumer example in Java, and in Go with goroutines and channels if Go is on your resume.

Deliverable: Two working concurrency examples plus a written race condition explanation.

Day 4 — Data structures and short programs

  • Solve linked-list and tree problems in your main language and write prime-number and string manipulation programs from memory.

Deliverable: Four solved problems with stated time and space complexity.

Day 5 — SQL, optimization and scalable code

  • Write join queries and tune a slow query using the execution plan, noting before-and-after cost.
  • Prepare an optimization story and an answer on keeping code scalable and maintainable.

Deliverable: A tuned query with plan notes and a written optimization story with baseline and result.

Day 6 — Project and integration discussions

  • Write and rehearse a walkthrough of your most complex project, covering requirements, your contribution, a decision and a failure.
  • Prepare an answer on enterprise integration challenges covering retries, failure handling and API contracts, and practise a booking-system or e-commerce-cart design if you are interviewing for a senior role.

Deliverable: A project walkthrough and an integration answer, spoken aloud once.

Day 7 — Behavioral stories and full run-through

  • Write STAR outlines for the motivation, disagreement, five-year, tight-deadline and time-management questions.
  • Run through all stories and the core technical answers aloud from start to finish and mark any weak spots.

Deliverable: Five STAR story outlines and a final list of weak topics.

Behavioral preparation for Bristlecone

Behavioral evaluations are reported as an assessment of how well you fit a collaborative culture. The role works with product managers and QA across teams, so prepare stories that show communication, handling disagreement, learning quickly and managing competing work.

How do you handle exceptions and errors in your code?

Medium · Technical & Core Programming

Approach

  1. State the principle first: catch an exception only where you can recover, retry, translate it, or add context; otherwise let it propagate to a boundary that owns the response. Never write an empty catch block or log and continue as if nothing happened.
  2. Show mechanics in your primary language. In Java: checked versus unchecked exceptions, try-with-resources so resources always close, a custom exception that keeps the original as its cause, and why a return inside finally can hide a thrown exception.
  3. Describe the boundary: validate input early and fail fast, then use one central handler (in a Spring Boot service, a controller advice) that maps domain errors to status codes and a stable error body. Log once with a request identifier rather than at every layer.
  4. For external calls add timeouts and bounded retries with backoff, only on idempotent operations. Anchor everything in one real incident: what failed, how you found it in the logs, and what you changed.

Follow-up

  • Would a missing customer record be a checked or unchecked exception, and why?
  • What happens to an exception thrown inside a task submitted to an ExecutorService?
  • How do you avoid logging the same error at several layers?

Describe a time you had to optimize a piece of code or a database query.

Medium · Project & Role-Specific Experience

Approach

  1. Choose a story where you measured first. Give the baseline from your own work (latency, rows scanned, memory, batch run time) and what triggered the work. Do not invent numbers.
  2. Diagnose before changing anything. For a query, read the execution plan (EXPLAIN or EXPLAIN ANALYZE) for full scans, bad row estimates, sorts spilling to disk and ORM N+1 patterns. For code, use a profiler or timing around the hot path.
  3. Name the change and why it addresses the cause: a composite index matching the filter and join columns, a correlated subquery rewritten as a join, only needed columns selected, batching, keyset pagination, or a hash map replacing a list scan (O(n) lookups become O(1)).
  4. Verify with the same input before and after, check the results are identical, and name the side effects (index write cost, cache invalidation, extra memory). Close with the measured result and what you would monitor.

Follow-up

  • How did you confirm the new index was used by the plan?
  • What would you have tried if the plan had not changed after adding the index?
  • What did the optimization cost somewhere else?

Why are you interested in joining Bristlecone?

Medium · Behavioral & Situational

Approach

  1. Build the answer from three parts: what the work is, what you bring, and what you want to learn. Facts you can safely use are the ones candidates report: enterprise clients, supply-chain problems, integrations, ERP modules, custom applications, and cross-functional work with product managers and QA.
  2. Link one concrete piece of your experience to that work, such as an integration with an external API, a SQL-heavy module or a Java service, and state the outcome.
  3. Stay truthful about what you know. Do not claim facts about culture or reputation you cannot support; say what you read, what interested you, and what you would like to ask.
  4. Keep practical reasons such as location or remote policy secondary, and never lead with pay.

Follow-up

  • What do you know about the kind of projects you would join?
  • Which part of the stack would you want to work on first?
  • What other options are you considering, and how does this one compare?

How do you handle disagreements within a team setting?

Medium · Behavioral & Situational

Approach

  1. Choose a real disagreement about a work decision (design, scope, estimate, review comment) with a peer, product manager or QA colleague. Avoid a story where the other person was simply wrong.
  2. Show your first move: understand their position well enough to restate it, and name what they were protecting (a delivery date, quality, risk).
  3. Describe the resolution by evidence: a small prototype, a benchmark, a failing test, bug data, or laying out options for the person who owned the decision. If the decision went against you, say you committed and carried it out fully.
  4. State the result and what changed afterwards in how you work. If you turned out to be wrong, say so; the story that sinks is the one with no disagreement or no learning.

Follow-up

  • What if the decision owner had chosen the other option?
  • Tell me about a time you were wrong in a disagreement like this.
  • How do you handle disagreement in a code review?

Where do you see your career progressing in the next five years?

Medium · Behavioral & Situational

Approach

  1. Answer with direction, not job titles: the skills you want to be known for (depth in Java, system design, owning integrations), then the scope you want (owning a module, designing across services, mentoring).
  2. Anchor it in evidence from the last year that points that way: a project with stretched ownership, a certification, a side project.
  3. Connect to the work in this role: enterprise integrations, SQL, and the cloud, Spring Boot or CI/CD areas listed as nice-to-have. Say which you would deepen.
  4. Adapt to your stage. As a fresh graduate, aim at mastering fundamentals and owning a first production feature. As an experienced engineer, describe moving from delivering features to shaping design. Be honest about technical versus management preference.

Follow-up

  • What would you need to learn to get there?
  • What if the team needed you on a different technology?
  • What have you done in the past year toward that goal?

Describe a time you had to learn a new technology under a tight deadline.

Medium · Behavioral & Situational

Approach

  1. Pick a real episode with a hard deadline, a technology new to you, and a deliverable that shipped. State the stack and deadline in a couple of sentences.
  2. Show your learning method: scope the learning to what the deliverable needs, read the official documentation for core concepts, build a small spike or thin end-to-end slice to test assumptions, and ask someone experienced to review key decisions.
  3. Show risk management: tell your lead early what was uncertain, tackle the riskiest part first, cut scope if needed, and use tests as the safety net.
  4. Give the result: what shipped, defects found afterwards, and what you would do differently. The mistake that sinks the story is claiming mastery after a few days or skipping tests.

Follow-up

  • How did you decide what not to learn?
  • What went wrong, and how was it caught?
  • How did you keep the code maintainable for teammates who did not know the technology?

How do you manage your time when juggling multiple priorities?

Medium · Behavioral & Situational

Approach

  1. Describe a concrete system: one list of commitments with deadline, impact and dependencies, reviewed daily, with blocked time for focused work and batching of reviews and messages.
  2. State your rule: first unblock colleagues who depend on your work, then take the highest-impact or earliest deadline. Illustrate with one story where two deliverables collided.
  3. Show communication: when everything cannot fit, tell the product manager or lead early with options (move the date, reduce scope, get help) instead of missing silently.
  4. Give the result: what was delivered, what was deferred, and who agreed to it. Working late with no trade-off is the answer that sinks this question.

Follow-up

  • Two stakeholders both say their task is top priority; what do you do?
  • What did you drop in that story, and who agreed?
  • How do you handle an urgent production bug in the middle of planned work?