Interview guides · Bizzeonline
Bizzeonline Software Engineer Interview Guide
Bizzeonline Software Engineer interview guide: four reported rounds, AEM, PHP, Node.js and React prep, question approaches and a 7-day plan.
The 60-second brief
Candidates describe Bizzeonline as a company whose engineers build and maintain the digital infrastructure behind the platforms its users interact with. The role description lists four technology tracks, AEM (Adobe Experience Manager), PHP, Node.JS and React.JS, so a Software Engineer here works on web applications in one of those stacks. The listed responsibilities are writing tested code, taking part in code reviews, turning product requirements into technical specifications, working through the full development lifecycle, and helping troubleshoot production issues and manage technical debt with cross-functional teams.
This guide is for engineers interviewing for a Software Engineer position at Bizzeonline under the AEM, PHP, Node.JS or React.JS track.
Candidates report four rounds over roughly three to five weeks: Initial Screening, Technical Evaluations, Behavioral Interviews and Final Team Interviews.
Focus areas
- Software Engineering
- Web Development
- React
Overview
The role description candidates see is a web engineering job in one of four stacks: AEM, PHP, Node.JS or React.JS. The requirements list experience in your primary stack, data structures and algorithms, and Git, with cloud infrastructure, CI/CD pipelines and Agile or Scrum as extras. Prepare one stack deeply instead of four shallowly: you should be able to explain how your framework behaves at runtime, not just how to use it.
The reported questions fall into three groups. The technical group asks about the lifecycle of a component in your primary framework, optimizing database queries for high-traffic applications, a hard bug and how you resolved it, and state management in a data-heavy application. The design group covers a high-concurrency microservice, third-party API integration, defending against SQL injection and XSS, and caching to improve response times. The third group is behavioral: changing requirements, architecture disagreements, mentoring or process improvement, and a tight deadline without lower code quality.
Candidates also report performance themes: memory management and garbage collection, asynchronous operations, database indexing and query optimization, and latency reduction. Several of the reported questions overlap with these, so one well-built mental model of where time goes in a request, from browser to cache to database, will serve you in more than one conversation.
Two pieces of reported advice shape how to practise. Expect to walk through your previous projects in detail, including the technical decisions you made and what they changed, and expect live coding or whiteboarding where you must explain your assumptions aloud. So rehearse speaking, not just solving: answer each question out loud, name the trade-off you are making, and keep a resume project ready for every technology you list.
The Bizzeonline interview process
Candidates describe four stages. The wording below follows what they report, and the preparation advice is built around the question types the guide lists.
Initial Screening
Candidates report this first stage as a screening of your background and your technical interest, used to judge fit before the deeper rounds. Treat it as a short, structured retelling of your work: which of the AEM, PHP, Node.JS or React.JS tracks you belong to, what you built in it, and why the role interests you.
What to demonstrate
- How well your background matches the role
- Your technical interest in the stack you are applying under
- How clearly you summarise your own projects
How to prepare
- Write a short account of your last two projects: the stack, your own part, one technical decision and its outcome.
- Pick your track (AEM, PHP, Node.JS or React.JS) and be ready to name the tooling, version control workflow and deployment path you used in it.
- Prepare two or three questions about the team and its stack so the call is a conversation.
Technical Evaluations
Candidates report deep-dive technical evaluations meant to assess real-world problem solving. The reported advice mentions live coding or whiteboarding where you explain assumptions and logic aloud, and a shared online editor without IDE autocomplete. The technical and design questions in this guide (component lifecycle, query optimization, a high-concurrency microservice, third-party APIs, SQL injection and XSS, caching) are the material to rehearse for a stage like this.
What to demonstrate
- Working code and fluency in your primary language and framework
- Reasoning about performance: async work, indexing, latency
- Clear spoken explanation of assumptions and trade-offs
How to prepare
- Solve data-structure problems in a plain editor with no autocomplete, speaking your plan before you type; the overlapping lease records and resource graph practice problems suit this.
- Prepare a worked answer for tuning a slow query: read the plan, choose an index, then decide what to cache.
- Sketch a high-concurrency service on paper, listing where requests queue, where state lives and what you cache.
- Write down the failure modes of one third-party API integration: timeouts, retries, rate limits and changed responses.
Behavioral Interviews
Candidates report interviews about your past experiences and team fit. The reported prompts here are about changing requirements, architecture disagreements, mentoring or improving a process, and delivering under a tight deadline without lowering quality. Come with real stories in which you made a visible decision.
What to demonstrate
- Your account of past work and your own role in it
- How you handle disagreement, change and pressure
- Collaboration with teammates and product partners
How to prepare
- Outline four stories, one per reported prompt, each with the situation, your decision, the alternatives you rejected and the result.
- Attach a number or an observable outcome to each story, such as fewer incidents, shorter review cycles or a met release date.
- Rehearse each story in first person singular and cut any step that was not yours.
Final Team Interviews
Candidates report final interviews with members of the team, aimed at overall compatibility. The role description centres on code review, translating requirements with product managers, troubleshooting production issues and managing technical debt, so expect a conversation about how you would work day to day beyond writing code.
What to demonstrate
- Overall compatibility with the team you would join
- Communication across product and engineering
- Depth of the projects on your resume
How to prepare
- Re-read your resume and prepare a technical story for every technology listed, including one mistake and what you changed afterwards.
- Prepare an example of a production issue you helped troubleshoot, told in the order you investigated it.
- Prepare an example of reviewing code or paying down technical debt, and the reasoning behind what you chose to fix first.
- Bring questions about how the team reviews code, ships changes and handles incidents.
Where candidates lose points
Describing the component lifecycle as a list of method or hook names
For your track, explain what runs when something is created, updated and removed, what belongs in each phase, and what leaks if cleanup is skipped, such as a timer, a listener or an open subscription. Add one real bug you saw from getting it wrong.
Answering the query optimization question with 'add an index' and nothing else
Walk the sequence: find the slow statement, read the execution plan, check for full scans and N+1 access patterns, pick an index with the right column order for the filter and sort, limit result sizes, and only then consider caching. Say how you would confirm it improved.
Proposing a cache with no invalidation or failure story
Say what is cached, the key, the time-to-live and how a write invalidates or refreshes it. Cover stale reads, a cold cache after restart and many requests hitting the database at once on a miss.
Naming SQL injection and XSS defences as 'sanitise the input'
Separate the two: parameterized queries keep data out of the SQL text, while XSS is stopped by encoding output for its context (HTML, attribute, script) and, where supported, a content security policy. Add least-privilege database accounts.
Telling the debugging or behavioral story as a team effort where your decision is invisible
Use 'I' for what you did. For the bug, give the symptom, your hypotheses in order, the check that eliminated each and the confirmed cause. For behavioral prompts, name the decision, the alternatives and the measurable result.
Questions & practice
Find overlapping job attempts and peak concurrency from lease records
Medium · sweep line · intervals · leases
A day of job_run history yields about 50,000,000 attempt records: (job_run_id, job_type, attempt, started_at, finished_at which is NULL when the worker died, lease_expires_at). Leases expire on a clock, so a job that outran its lease ran twice. Produce (a) every job_run_id whose attempts overlapped in wall-clock time and (b) the peak number of simultaneously running attempts per job_type with the minute it occurred. Target O(n log n). State how you treat a NULL finished_at and what clock skew does to your answer.
Approach
- Define the interval before sorting anything: an attempt occupies [started_at, COALESCE(finished_at, lease_expires_at)). finished_at is observed and lease_expires_at is only a promise, so every attempt without a finish contributes an estimate and the whole result is a lower bound on overlap rather than an exact count.
- For peak concurrency, sweep: emit 2n endpoints, sort by (timestamp, kind) with ends ordered before starts at equal timestamps, then walk the sequence maintaining a counter per job_type and record each type's maximum with its timestamp. O(n log n) dominated by the sort, O(n) space, or O(1) extra if the sort is external and the walk streams.
- For overlap detection, do not compare attempts pairwise. A single global sort by (job_run_id, started_at) gives both the grouping and the order; within a group, keep the maximum end seen so far and report an overlap exactly when the next start is less than that running maximum, which is one linear pass after the sort.
- Half-open intervals matter and are easy to get wrong: with closed intervals an attempt ending at the same millisecond another begins reads as concurrency two, and across 50,000,000 records that artefact swamps the real signal.
Follow-up
- A handler is not idempotent and you have found 400 overlapping jobs. Which of them actually caused damage, and what would you query to find out?
- Peak concurrency for one job_type is 4 against a configured cap of 4. Is the cap working, or is the data hiding attempts that never started?
Diff a projection against the primary without per-row point reads
Hard · reconciliation · range hashing · throttling
The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.
Approach
- Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
- Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
- For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
- Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.
Follow-up
- The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
- Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?
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
- 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.
- 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.
- 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.
- 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?
How do you ensure your code remains secure against common vulnerabilities like SQL injection or XSS?
Medium · System Design and Architecture
Approach
- SQL injection: never build SQL by string concatenation. Use prepared statements with bound values, for example PDO prepare('... WHERE id = ?') then execute([$id]) in PHP, or $1-style or ? placeholders in Node's pg or mysql2 drivers. ORMs are safe until you fall back to raw query strings.
- Identifiers such as table names, column names and sort direction cannot be bound as parameters. Check them against an allowlist in code. Connect with a least-privilege database user so a missed injection cannot drop tables.
- XSS: encode output for its context. React escapes text in JSX by default. The risky paths are dangerouslySetInnerHTML, href or src values that accept javascript: URLs, and direct innerHTML. In PHP, use htmlspecialchars($s, ENT_QUOTES, 'UTF-8'). HTL in AEM escapes by context automatically, so do not bypass it with context='unsafe'.
- When users must submit rich HTML, sanitize it with an allowlist sanitizer such as DOMPurify instead of writing your own regex. Add defence in depth: a Content-Security-Policy that blocks inline and unknown-origin scripts, and HttpOnly plus Secure cookies so script cannot read the session.
- Make it a process, not a one-time fix. Run static analysis and dependency audits (npm audit, composer audit) in CI, add a review checklist item for raw queries and raw HTML, and add tests that send injection payloads.
Follow-up
- A user can choose which column to sort a table by: how do you implement that safely?
- What does an HttpOnly cookie protect against, and what can an XSS payload still do despite it?
- Input validation is in place: why is it not enough on its own against either attack?
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
- 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.
- 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.
- 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.
- 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 lifecycle of a component in your primary framework.
Medium · Technical Proficiency
Approach
- Pick the framework you actually ship in and name the phases. For React these are render (pure, no side effects), commit (DOM updated) and effects. Mount, update and unmount map to class methods: constructor, render, componentDidMount, then componentDidUpdate, then componentWillUnmount.
- In hooks terms, useEffect runs after the browser paints. Its cleanup runs before the next effect when a dependency changes, and again on unmount. useLayoutEffect runs synchronously after DOM mutation and before paint, so use it only for measuring layout.
- Say what belongs where. Subscriptions, timers and fetches go in effects, and each one gets a cleanup (unsubscribe, clearTimeout, AbortController.abort()). Derived values are computed during render, not mirrored into state by an effect.
- Mention that React 18 StrictMode in development mounts, unmounts and remounts a component to surface missing cleanups. A correct effect survives that run without double side effects.
- If your primary stack is AEM, the lifecycle is per request. Sling resolves the resource through sling:resourceType, the component's HTL script renders it, and a Sling Model is adapted with its fields injected before its @PostConstruct method runs.
Follow-up
- An effect fires twice in development: why, and how do you make it harmless?
- How do you stop a fetch that resolves after the component unmounted from updating state?
- A child re-renders although its props look unchanged: what causes that, and when is React.memo or useMemo worth adding?
How do you optimize database queries for high-traffic applications?
Medium · Technical Proficiency
Approach
- Measure before changing anything. Find the expensive queries by total time (pg_stat_statements, or the MySQL slow query log), then read EXPLAIN ANALYZE for sequential scans on large tables and for estimated row counts far from actual ones.
- Index for the query shape. In a composite B-tree index, put equality columns first and the range or sort column last. Wrapping the column in a function, an implicit type cast or a leading-wildcard LIKE '%x' stops a plain B-tree index being used. Every extra index makes writes slower.
- Remove wasted work in the application. Fix N+1 ORM access with eager loading or one batched IN query, select only the columns you need, and replace large OFFSET pagination with keyset pagination (WHERE id < last_seen ORDER BY id DESC LIMIT n), because OFFSET still reads and discards the skipped rows.
- Protect the database under load. Pool connections (PgBouncer, or bounded pools per Node process or PHP-FPM worker) so a traffic spike cannot exhaust max_connections. Put a cache in front of hot reads.
- Scale reads with replicas once a single primary is the limit. Replicas lag, so a read-your-own-write path (a user viewing what they just saved) must go to the primary.
Follow-up
- A query uses the index in staging but does a full scan in production: what would you check?
- How would you paginate a feed of tens of millions of rows that users scroll deep into?
- When would you denormalize or precompute a table instead of adding another index?
Describe a challenging bug you encountered and your systematic approach to resolving it.
Medium · Technical Proficiency
Approach
- Choose a bug whose cause was not obvious, such as a race between async calls in Node, a stale cache entry, a timezone or encoding error, or a memory leak. Open with the symptom and its scope: who saw it, since when, how often, and what it cost.
- Show the sequence. First make it reproducible, ideally as a minimal failing case or test. Then narrow it down with logs, metrics and request ids, binary-search the cause (git bisect start, git bisect bad, git bisect good <sha>, or git bisect run with a test script), and change one variable at a time.
- Separate the trigger from the root cause. 'The value was null' is the trigger. Why it became null, and why nothing caught it, is the cause. State the evidence that confirmed each hypothesis or ruled it out.
- Close with the fix, a regression test that failed before it and passes after, and one prevention step: an alert, a lint rule, a type, or a code-review checklist item.
- The story falls apart if it ends at a defensive null check or a retry that hides the symptom. It also falls apart if you cannot say which steps you took yourself.
Follow-up
- How did you prove the fix addressed the root cause rather than masking it?
- If you had not been able to reproduce it locally, what would you have instrumented in production?
- What did you change so this class of bug is caught earlier next time?
How would you design a microservice to handle high-concurrency requests?
Medium · System Design and Architecture
Approach
- Pin down the load first: requests per second at peak, read/write ratio, latency target (p99), payload size, and what a caller does on failure. These numbers decide everything that follows.
- Keep instances stateless behind a load balancer so you can scale horizontally, with state in the database or a shared cache. In Node.js the event loop is single-threaded: keep I/O async, move CPU-heavy work to worker_threads or a separate service, and run one process per core (cluster or the orchestrator).
- Protect downstream resources. Size connection pools to the database's limit across all instances, put timeouts on every outbound call, and use rate limiting (token bucket), load shedding with 429 or 503, and a circuit breaker so a slow dependency does not tie up every request.
- Move slow or bursty work to a message queue. Return 202 Accepted and let workers process the job, with an idempotency key so a retried request is not applied twice.
- Handle concurrent writes to the same row explicitly. Use an atomic conditional update (UPDATE stock SET qty = qty - 1 WHERE id = ? AND qty > 0, then check the affected row count) or optimistic locking with a version column, rather than a read followed by a write.
Follow-up
- Two requests try to take the last item in stock at the same moment: how does your design stop both from succeeding?
- Twenty instances each open a pool of 20 connections against a database that allows 200: what happens, and how do you fix it?
- Which metric would tell you first that the service is saturating?
What considerations do you make when integrating third-party APIs?
Medium · System Design and Architecture
Approach
- Treat every call as able to fail or hang. Set connect and read timeouts. Retry with exponential backoff and jitter only when the request is idempotent or carries an idempotency key, and honour 429 responses and the Retry-After header.
- Isolate the vendor behind your own adapter interface. Map its payloads and error codes to your own types so a schema change or a vendor swap touches one module. Pin the API version and run contract tests against its sandbox.
- Stay within its limits. Throttle on the client side to the vendor's quota, batch where the API allows it, queue non-urgent calls, and add a circuit breaker with a fallback (cached data, a degraded feature) for outages.
- Secure the integration. Keep keys in a secret manager or environment variables, never in the repo. Request least-privilege scopes, validate the data that comes back, and verify webhook signatures (HMAC over the raw body plus a timestamp check to block replays).
- Expect webhooks to be duplicated or to arrive out of order, so deduplicate by event id and make handlers idempotent. Log the vendor's request id and track latency and error rate per vendor so you can show whose side failed.
Follow-up
- Your call to create a charge timed out: did it go through, and how do you find out without charging twice?
- The vendor's webhook arrives before your own API call has returned: how do you handle it?
- How do you test this integration in CI without calling the live vendor?
Explain your strategy for implementing caching to improve response times.
Medium · System Design and Architecture
Approach
- Start from measurements. Find the hot, read-heavy data that can tolerate some staleness, and set a target hit rate and latency. Caching data that changes on every write adds complexity without benefit.
- Choose the layer. HTTP caching uses Cache-Control max-age plus ETag and If-None-Match returning 304. A CDN suits static assets and public pages (in AEM, the Dispatcher cache in front of publish instances, flushed on activation). A shared Redis or Memcached serves application data. An in-process cache is fastest but separate for each instance.
- Name the pattern and its consistency. With cache-aside, a read checks the cache and on a miss loads from the database and sets the key with a TTL; a write updates the database and then deletes the key. Deleting avoids a slower writer overwriting the cache with an older value. Write-through and write-behind trade write latency against durability.
- Plan for a stampede on hot keys. When a popular key expires, many requests hit the database at once. Prevent this with request coalescing or a short lock so a single request rebuilds the entry, random jitter on TTLs, or serving stale data while one request refreshes it.
- Get the keys and privacy right. Personalised responses need the user in the key and Cache-Control: private so a shared CDN never stores them. Bound memory with an eviction policy (LRU or LFU), and make the service still work, more slowly, when the cache is down.
Follow-up
- A user updates their profile and still sees the old version: trace how that happens with cache-aside and how you fix it.
- Redis goes down at peak traffic: what happens to the database, and what would you have done in advance?
- How do you choose the TTL for a value with no clear invalidation event?
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
- 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.
- 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.
- 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.
- 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
Each day targets one cluster of the reported questions: stack fundamentals, algorithms in a bare editor, data and caching, design and security, debugging and state, then spoken behavioral stories.
Day 1 — Map the loop and pick your track
- Write the four reported rounds in order and note, for each, which of the reported question types it would cover.
- Choose your primary track (AEM, PHP, Node.JS or React.JS) and list every project on your resume with its stack, your role and one decision.
- Record yourself summarising your background and why you want this role, then rewrite the weakest part.
Deliverable: A one-page round map, a project inventory with a decision per project, and a recorded self-introduction.
Day 2 — Framework internals and runtime behaviour
- Write out the lifecycle of the main unit in your track (component, request or module) with what belongs at each stage and what needs cleanup.
- Write short explanations of how asynchronous work is scheduled in your language and how memory is reclaimed, each with one pitfall you have seen.
- Answer the lifecycle question aloud and compare with your written notes.
Deliverable: A lifecycle sheet for your track, two pitfall explanations, and one recorded spoken answer.
Day 3 — Algorithms in a bare editor
- Solve the overlapping lease records and peak concurrency problem in a plain text editor with no autocomplete, speaking your plan first.
- Solve the problem of diffing a projection against the primary without per-row reads, stating complexity as you go.
- Solve the resource graph archiving problem iteratively and list its edge cases (cycles, live references).
Deliverable: Three working solutions, each with a stated complexity and an edge-case list.
Day 4 — Database tuning and caching
- Take a slow query on a sample table, read its execution plan, add an index, and record before and after plans.
- Write a caching plan for one endpoint: key, time-to-live, invalidation on write and behaviour on a cold cache.
- Work the per-tenant active cap problem and explain how concurrent creates are kept from exceeding the cap.
Deliverable: A before and after plan comparison, a one-page caching plan, and a written solution to the concurrent cap problem.
Day 5 — Service design and security
- Sketch a microservice for high-concurrency requests: entry point, queueing, statelessness, caching, database access and back-pressure.
- List the failure modes of a third-party API integration and the handling for each: timeout, retry with idempotency key, rate limit, changed response.
- Write the SQL injection and XSS defences for your stack, with a code snippet for parameterized queries and output encoding.
Deliverable: A service diagram with a bottleneck list, a third-party failure table, and a security checklist with code.
Day 6 — Debugging and state management
- Write up your hardest bug as symptom, hypotheses, checks, root cause and fix, then speak it aloud.
- Diagnose the log partition that stops advancing while the others drain, writing your investigation order before reading any solution.
- Write a state map for a data-heavy screen you built: server data, shared state, local state and URL state, with the derived values.
Deliverable: A written bug narrative, a debugging sequence for the stalled partition, and a state map.
Day 7 — Behavioral stories and full run-through
- Write and rehearse four stories: a pivot after changed requirements, an architecture disagreement, mentoring or a process improvement, and a deadline met without lowering quality.
- Give each story a measurable result and trim it until it fits in a short spoken answer.
- Do a full mock run: project walk-through, one technical answer from each earlier day, then the four stories.
Deliverable: Four stories each with a decision and a result, plus one recorded mock run noting what to tighten.
Behavioural preparation
Candidates describe the behavioral and final team stages as conversations about your past work and how you fit a team. The reported prompts cover changing requirements, architecture disagreements, mentoring or process improvement, and quality under a deadline. The role description also lists code review, working with product managers, production troubleshooting and technical debt, so choose stories that show those, each with your own decision and a result.
- Describe a challenging bug you encountered and your systematic approach to resolving it.
How do you handle state management in a complex, data-heavy application?
Medium · Technical Proficiency
Approach
- Start by splitting state into kinds: server data fetched from APIs, shared client state, local component state, and state that belongs in the URL such as filters, sort and page. Each kind gets a different home, and mixing them is what makes data-heavy screens hard to reason about.
- For server data in a React app, use a cache layer (for example React Query or SWR, or your store's async tools) keyed by request parameters, with explicit stale times and invalidation after mutations. Keep only truly shared client state in a store, with local state staying in components via useState or useReducer.
- Keep one source of truth. Store normalized entities by id and compute derived values through selectors or memoization instead of copying them into state, which is the usual origin of fields that drift out of sync.
- For large datasets, paginate or virtualize lists, subscribe components to narrow slices so a change does not re-render the whole screen, and use optimistic updates with a rollback path for mutations.
- Ground the answer in a real project: the screen, the data volume, the bug or slowdown that pushed you to restructure state, and what you measured afterwards. Name one thing you deliberately did not put in global state.
Follow-up
- When would you refuse to put something in a global store?
- How do you handle an optimistic update that the server rejects?
- How would you find and fix unnecessary re-renders on a screen with thousands of rows?
Describe a time you had to pivot your technical approach due to changing requirements.
Medium · Problem Solving and Behavioral
Approach
- Choose a change that forced a technical rethink, such as a requirement that invalidated a data model, an integration or a framework choice, not merely a schedule shift. Say in a sentence or two what was planned and what changed.
- Show the decision: what you salvaged, what you threw away, and the options you weighed (patch, adapter layer, rebuild) with the cost of each. Make clear it was your call or your recommendation.
- Cover communication: how you told product and teammates what the change cost in time and risk, and how you protected work already in flight, with feature flags, a migration path or tests around the old behaviour.
- Close with the result and a lesson that changed your later practice, for example isolating volatile requirements behind an interface. Avoid blaming stakeholders for the change; the story is about how you adapted.
Follow-up
- How did you decide what to keep from the original approach?
- Could you have anticipated the change, and what do you do differently now?
- How did you explain the revised timeline to the product manager?
How do you handle disagreements regarding technical architecture with team members?
Medium · Problem Solving and Behavioral
Approach
- Pick a disagreement over a real architectural choice, such as where to put a cache, whether to split a service, or which data store to use, with a colleague whose argument had merit. State their position fairly before yours; a story where the other side is a strawman fails.
- Show how you moved from opinion to evidence: a small prototype, a benchmark or load test, a review of failure modes, or a written design note comparing options against the constraints that mattered, such as latency, team skills and operating cost.
- Explain how the decision was made and by whom. If the outcome went against you, show that you committed to it and helped it succeed; if it went your way, show what you did to keep the relationship intact.
- End with the outcome and what you would do differently, and a rule you now follow, such as writing down decision criteria before debating options.
Follow-up
- What if the data was inconclusive and you still disagreed?
- Tell me about a time you turned out to be wrong in such a discussion.
- How do you handle disagreeing with a more senior engineer?
Give an example of how you mentored a junior engineer or improved a team process.
Medium · Problem Solving and Behavioral
Approach
- Choose one thread and commit to it. For mentoring, describe how you found the gap (patterns in pull request comments, a stuck task), the support you chose (pairing, a small scoped task, a written checklist) and how you gradually handed over ownership.
- For a process improvement, start with the measured pain: slow reviews, flaky releases, repeated production mistakes. Then say what you changed (a pull request template, a CI check, a runbook, a smaller review size) and how you got the team to adopt it.
- Show the effect with evidence: the junior engineer shipping a feature alone, shorter review turnaround, fewer repeated incidents. Name your own contribution separately from the team's.
- Be honest about what did not work at first. A story where everything went smoothly sounds invented, and one that credits the whole team does not show what you did.
Follow-up
- What did you do when the person kept repeating the same mistake?
- How did you measure whether the process change helped?
- How did you respond to teammates who resisted the change?
Tell us about a project where you had to meet a tight deadline without sacrificing code quality.
Medium · Problem Solving and Behavioral
Approach
- Set the scene briefly: the deadline, what was at stake and why it was fixed. Then spend most of the answer on how you protected quality while the clock ran.
- Describe the scope negotiation. Say what you cut or deferred, such as secondary features, polish or edge-case handling, and what you refused to cut: tests on the critical path, code review, input validation and security checks.
- Describe mechanics that make speed safe: a feature flag, a staged rollout, automated tests in CI, small reviewable pull requests, and a rollback plan you had rehearsed.
- Be explicit about debt. Name what you knowingly left behind, where you recorded it and when it was repaid. Close with the delivery outcome and the post-release quality, such as incidents or defects after launch.
Follow-up
- What technical debt did you take on, and when was it repaid?
- What would you have done if product refused to reduce scope?
- How did you decide which tests were worth writing under that pressure?