cswork / libraryOpen practice workspace

Interview guides · 75way Technologies Pvt.

75way Technologies Pvt. Software Engineer Interview Guide

75way Technologies Pvt. Software Engineer interview guide: reported rounds, React, REST and schema questions with worked approaches, and a 7-day plan.

The 60-second brief

Candidates describe 75way Technologies Pvt. as a company that builds client-facing software across varied projects, from administrative panel integrations to user-facing web interfaces. As candidates report it, a Software Engineer here works across the stack. That means React front ends, RESTful APIs, and relational or NoSQL schemas, with Ruby on Rails, admin-panel work and real-time data features listed as nice-to-haves. The reported day-to-day work is debugging, writing APIs, maintaining existing codebases and connecting front-end components to back-end services. Candidates also mention filtering logic and administrative workflows as recurring tasks.

This guide is for Software Engineer candidates at 75way Technologies Pvt. who will be asked about their own portfolio projects and have to change code live. It covers the three stages candidates report: Resume Shortlist, Virtual Screening and Technical Interviews. It gives question-specific approaches to every reported question: architecture walk-throughs, price and rating filters, approval-gated admin data, React pagination, table-to-card refactors, npm lockfiles, cinema booking and nested-comment schemas, Rails callbacks and a loop-based letter pattern. It closes with a seven-day plan, common ways candidates lose points and an FAQ.

Candidates report three stages over about 3-5 weeks: a Resume Shortlist, a Virtual Screening, then Technical Interviews made up of two separate hands-on rounds. In those rounds you share your screen, explain your code as you write it and modify it on request.

Focus areas

  • React
  • Database Design
  • API Development (RESTful)

Overview

The reported questions fall into two groups. The first group is about your own work: the architecture of your main project, plus small features built on top of it, such as a price and rating filter, admin data that appears only after super-admin approval, pagination in React, and turning a table into cards. The second group covers fundamentals: npm's package.json versus package-lock.json, a cinema booking schema with seat availability and show timings, a schema for comments nested to any depth, callbacks in Rails, and printing a letter shape with loops.

Candidates report that the technical rounds focus on hands-on work rather than recall. You share your screen, talk through your code as you go, and get asked for on-the-spot changes, such as a new API endpoint or a UI change. That shapes how you should prepare. Practise on your real project, running locally, and rehearse changes that cross the stack: a new query parameter in the API, the SQL that uses it, and the React state that sends it. Candidates mention tasks sized at around 30 minutes, so build the common pieces (a paginated list, a filter form, a role-gated button) until you can write them without looking anything up.

Database design comes up repeatedly in candidate reports. Treat the two schema questions as full exercises. For each one, write the tables, keys and unique constraints, the query that answers the main read (free seats for a show, a whole comment thread), and the write path that has to stay correct when two requests run at the same time. A seat that can be sold twice, or a comment tree that needs one query per level, is the kind of gap a follow-up question exposes quickly.

The listed requirements put React, RESTful APIs and schema design (SQL or NoSQL) first, with Rails as a plus. Since NoSQL is named, also sketch both schemas in a document store. For the cinema, keep one document per seat per show (or a seats array embedded in the show document) and take a hold with an atomic conditional update that matches the seat id and status 'available', backed by a unique index on show and seat. For comments, embed replies only for shallow threads, and reference a parent id when depth is unlimited, because embedding is capped by document size. If your projects are in Node and React, still learn the Rails callback lifecycle well enough to explain it, because it is a reported question. If your projects are in Rails, be ready to show how the same flow looks from the React side.

The 75way Technologies Pvt. interview process

Candidates report three stages in this order: Resume Shortlist, Virtual Screening and Technical Interviews, with the last stage made up of two separate technical rounds. Each stage below gives what candidates describe and how to prepare for it.

Resume Shortlist

Candidates report a resume-based shortlist as the first stage, with further evaluation for those who are shortlisted. The projects on your resume matter later, since candidates report questions about the architecture of a primary project and on-the-spot changes to it. List only projects you can rebuild and modify on demand. For each one, name the stack (React, a REST API, the database), say what you personally built, and include one concrete feature, such as filtering, pagination or an admin approval flow, that you are ready to extend.

What to demonstrate

  • Candidates report that shortlisting is based on the resume
  • Candidates report that the projects you list return later as architecture questions and live changes

How to prepare

  • Cut any project from your resume whose code you cannot explain line by line or change live.
  • For each remaining project, write one line naming the stack and one feature you built end to end, such as a filter, a paginated list or a role-gated admin view.
  • Make sure linked repositories have a README with setup steps and that they install and run on a clean machine.

Virtual Screening

Candidates describe an initial virtual screening that checks fit before the technical rounds. They report a standard 'tell me about yourself' opener, and they report that the technical content of the process is the priority. Use your introduction to point toward the projects you want to be asked about, because those projects carry the technical rounds that follow. Test your camera, microphone and screen sharing beforehand, since the later rounds also run with a shared screen.

What to demonstrate

  • Fit for the role, which candidates describe as the purpose of the screening
  • A clear self-introduction that connects your projects to the React, API and database work in the role

How to prepare

  • Write and rehearse a short 'tell me about yourself' that ends on your main project and the stack it uses.
  • Write a one-sentence summary of your main project's architecture (client, API, database), ready for a more detailed walk-through later.
  • Do a test call with screen sharing and an IDE open, so that setup problems do not cost you time later in the process.

Technical Interviews

Candidates report two separate technical rounds built around hands-on tasks and coding. You share your screen, explain your code in real time and handle changes requested on the spot, and candidates mention examples such as adding an API endpoint or changing the UI. They also mention short time-boxed tasks of around 30 minutes. Have your project open and running before the call starts. When a change is requested, restate it, name the files you will touch (route, query, component), make the smallest working change, then show that it works in the browser or with a request.

What to demonstrate

  • Working code produced live while sharing your screen
  • Explaining design and library choices in your own project as you change it
  • Modifying existing code on request without breaking the feature around it

How to prepare

  • Rehearse three live changes to your own project, each in one sitting: add a filter parameter to an endpoint, add pagination to a list, and convert a table view to cards.
  • Build a paginated, filterable React list backed by a small REST API from scratch, until it runs at the end of one sitting.
  • Write out the cinema booking and nested-comment schemas with their constraints and main queries, and say them aloud without notes.

Where candidates lose points

Being unable to explain, or change, a part of your own listed project when asked for a live change such as a new endpoint or UI tweak.

Before the loop, go through each listed project file by file and change three things in it yourself: a new filter parameter, a pagination change and a view change. Remove any project you cannot change confidently, and have it running locally before the call.

Hiding unapproved or admin-only data only in React, so the API still returns it to anyone.

For the super-admin approval question, put the status filter in the query of the public endpoint and the role check in middleware on the approve endpoint. Treat the conditional rendering in React as a convenience, and say so out loud.

Writing a cinema booking schema with no database-level guard against selling the same seat twice for a show.

Pre-create one show_seat row per seat per show and make the conditional UPDATE (status 'available', or a hold that has expired) the guard, checking the affected row count. Keep UNIQUE(show_id, seat_id) for integrity, and explain what the second of two concurrent requests sees.

Paginating with OFFSET and no stable sort, or keeping the old page number after a filter changes.

Always order by a unique tie-breaker (created_at, id), reset to page 1 whenever filters change, ignore stale responses with an AbortController, and be ready to explain when keyset pagination beats OFFSET.

Answering the nested-comments schema by adding a parent_id column and then fetching replies with one query per level.

Load a post's comments in one query and build the tree with a hash map, or use WITH RECURSIVE. Name materialized path and closure table as the alternatives, and explain how you handle deleting a comment that has replies.

Questions & practice

What is the difference between package.json and package-lock.json?

Medium · Technical Fundamentals & Database Design

Approach

  1. package.json is the hand-edited manifest: name, scripts, and dependencies and devDependencies declared as semver ranges. ^1.4.2 accepts anything below 2.0.0, and ~1.4.2 accepts anything below 1.5.0.
  2. package-lock.json is generated by npm. It records the exact resolved version of every package in the tree, including transitive dependencies, along with the resolved URL and an integrity hash, so every install produces the same node_modules.
  3. Commit the lockfile for applications. npm ci installs exactly what the lockfile says and fails if it disagrees with package.json, while npm install can update the lockfile within the allowed ranges.
  4. Mention the edge cases: the lockfile of a published library is not used by its consumers (npm-shrinkwrap.json is), and Yarn and pnpm use yarn.lock and pnpm-lock.yaml for the same purpose.

Follow-up

  • The app works on your machine but fails in CI after a fresh install. How could the lockfile be involved?
  • How do you resolve a merge conflict in package-lock.json?
  • When would you use npm ci instead of npm install?

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?

Find overlapping job attempts and peak concurrency from lease records

Medium · sweep line · intervals · leases

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

Approach

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

Follow-up

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

Denormalise tenant onto revisions and backfill it live

Hard · denormalisation · online migration · backfill

resource_revision (revision_id, resource_id, version, actor_user_id, change_kind, patch, request_id, created_at) has 400M rows and no tenant column; tenant_id lives only on resource. Two reads need it: a tenant-scoped audit feed ordered by created_at DESC, and an offboarding purge. Both join back to resource today. Justify adding tenant_id to resource_revision against those two reads, name the anomaly the copy introduces and the constraint that prevents it, then give the ordered migration for a live table taking 1.2k writes/second — the lock each step takes, how the backfill is batched, and where each step stops being reversible. PostgreSQL 16.

Approach

  1. Justify from the access path rather than from taste. Without the column, the audit feed either scans resource_revision by created_at and discards other tenants' rows, or resolves the tenant's resource_ids first and probes with them — both proportional to the tenant's whole history rather than to one page. With (tenant_id, created_at DESC, revision_id DESC) it is a seek that stops at 50 rows, and the purge becomes a ranged delete instead of a join.
  2. Name the cost exactly: a second copy of a fact can disagree with the first. Make the disagreement unwritable rather than documented — add UNIQUE (resource_id, tenant_id) on resource so it can serve as a foreign-key target, then FOREIGN KEY (resource_id, tenant_id) REFERENCES resource (resource_id, tenant_id) on the revision table. A revision can then only ever carry its parent's tenant.
  3. Step one, expand: ALTER TABLE resource_revision ADD COLUMN tenant_id BIGINT NULL, with no default, so it is a catalogue change and no rewrite. It still needs ACCESS EXCLUSIVE for an instant, and that instant queues behind the longest open transaction on the table while every later query queues behind it — set lock_timeout to 2s and retry rather than wait.
  4. Step two, dual-write: deploy the writer that populates tenant_id on every new revision while reads still use the join. Reversible by redeploying the previous build, because nothing reads the column yet.

Follow-up

  • The backfill is half finished and a rollback is required. What state is the table in, and what does the previous build do with a half-populated column?
  • How do you verify the backfill actually finished, given rows are still being inserted while it runs?

Keep soft-deleted accounts from blocking re-registration

Easy · soft delete · unique constraints · engine differences

app_user holds user_id, tenant_id, email CITEXT, password_hash (NULL for SSO principals), email_verified_at, auth_version, status ('invited','active','suspended','deactivated'), created_at, updated_at, deleted_at. Two live accounts for one address inside a tenant must be impossible, but an address freed by a soft delete must be reusable, and the same tenant may delete and re-register it repeatedly. Write the uniqueness DDL for PostgreSQL 16, then the equivalent for MySQL 8 where partial indexes do not exist, and say what each permits once three deleted rows already hold that address.

Approach

  1. Start from what is actually unique: not (tenant_id, email), but (tenant_id, email) among live rows. PostgreSQL says that directly — CREATE UNIQUE INDEX app_user_live_email ON app_user (tenant_id, email) WHERE deleted_at IS NULL. A full constraint over the same two columns burns the address permanently the first time someone deletes an account.
  2. Keep case-insensitivity in the type or the index, never in the application: CITEXT as given, or UNIQUE (tenant_id, lower(email)) as an expression index where the extension is unavailable. A case-sensitive unique column is exactly how two accounts for one human appear.
  3. For MySQL 8 the predicate has to move inside the key: add a discriminator column that is a constant 0 while the row is live and is set to user_id on delete, with UNIQUE (tenant_id, email, deleted_marker). Live rows share the constant and still collide; deleted rows differ from each other and stop colliding.
  4. State the NULL variant and its dependency: leaving the marker NULL for deleted rows also works, because a unique index treats NULLs as distinct — true in MySQL, and true in PostgreSQL only under the default NULLS DISTINCT, which PostgreSQL 15 lets you reverse. Check the polarity against the three existing deleted rows: constant-on-live is what preserves the collision you want, and reversing it silently admits duplicate live accounts.

Follow-up

  • A deleted account re-registers with the same address the next day. Do the old resource rows follow the new user_id, and how does the API keep the two principals apart?
  • How do you honour an erasure request while resource_revision.actor_user_id still references this table?

Can you walk us through the architecture of your primary project?

Medium · Project-Based Technical Deep Dives

Approach

  1. Open with one sentence on what the project does and for whom, then draw it: React client, REST API layer, database, and any external services such as authentication, file storage or payments.
  2. Trace one real feature end to end: the component event, the HTTP request (method, path, payload), the controller or handler, the query, and the response rendered back. This shows you understand how data flows rather than only naming boxes.
  3. Show the main tables and their relationships, and give one decision with its alternative. For example: 'I stored the average rating on the product row instead of computing it per request, because the list page reads it on every load.'
  4. Cover error handling explicitly: what the API returns for missing or invalid input (400 vs 404 vs 403), and what the UI shows for loading, empty and error states.
  5. Finish with what you would change now, such as adding an index, moving logic out of a component, or adding tests, and keep the project running so you can make a live change straight away.

Follow-up

  • Add a new endpoint to this project now that returns the items for a category. Which files do you touch?
  • Why did you choose this library or framework over the obvious alternative?
  • What happens in your app when the API returns an empty list or a 500?

How would you implement a price-based rating filtration system in your application?

Medium · Project-Based Technical Deep Dives

Approach

  1. Pin down the requirement first: filter products by a price range and a minimum rating, possibly with sorting. Ask whether products with no ratings should be included.
  2. API shape: GET /products?min_price=&max_price=&min_rating=&sort=&page=. Validate inputs (non-negative, min_price <= max_price, rating within 0-5) and return 400 on bad input.
  3. Query: WHERE price BETWEEN $1 AND $2 AND avg_rating >= $3, with bind parameters and never string concatenation. Store price in integer minor units, and keep avg_rating and rating_count on the product row, updated when a review is written, rather than computing AVG with GROUP BY ... HAVING on every request. Index the column the filter narrows most.
  4. Frontend: controlled inputs, debounced requests, filters kept in the URL query string so the page can be reloaded and shared, and the page reset to 1 whenever a filter changes.
  5. Edge cases: products with NULL ratings (state whether they pass a minimum-rating filter), ties when sorting (add id as a tie-breaker), and an empty result state.

Follow-up

  • How do you keep avg_rating correct when a review is edited or deleted?
  • A product with one 5-star review outranks one with 4.8 from 500 reviews. How would you fix the ranking?
  • How would you show how many products fall in each price band next to the filter?

Can you demonstrate how to fetch data from an admin panel and conditionally display it based on super-admin ap

Medium · Project-Based Technical Deep Dives

Can you demonstrate how to fetch data from an admin panel and conditionally display it based on super-admin approval?

Approach

  1. Model the data: each record has status (pending, approved, rejected), approved_by and approved_at, and each user has a role, with super_admin as one value. Approval is a state change, not a separate copy of the data.
  2. Enforce visibility on the server. The public endpoint returns only WHERE status = 'approved'. The admin endpoint lists pending items, and PATCH /items/:id/approve checks the caller's role in middleware and returns 403 otherwise. Hiding a button in React is not access control.
  3. React side: fetch with useEffect plus an AbortController (or a data library such as React Query). Render loading, error and empty states, and show the Approve control only when the current user's role is super_admin.
  4. After an approval, update the local list or refetch so the item moves from pending to visible without a manual reload.
  5. Edge cases: an approved item is later edited (does it return to pending?), approval is revoked, and two admins act on the same item at the same time (use a conditional update such as WHERE status = 'pending' and check the affected row count).

Follow-up

  • How would you show an edit to an already-approved item to the public only after it is re-approved?
  • How would you notify the creator when their item is approved or rejected?
  • What changes if approval needs two separate admins?

Can you convert this table-based data display into a card-based format?

Medium · Project-Based Technical Deep Dives

Approach

  1. Treat it as a presentation change only. Keep the fetching, sorting and filtering state exactly as it is, and replace the table body with a list of Card components mapped from the same array, each keyed by a stable id rather than the array index.
  2. Move the column definitions (label, accessor, formatter) into a config array, so that both the table and the card view render from one source. Each card then shows label and value pairs instead of relying on column headers.
  3. Layout: a CSS grid such as grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)) gives responsive columns without media queries. Truncate long text and keep action buttons in a consistent position.
  4. Keep the existing loading, empty and error states, and keep actions (edit, delete, approve) working on cards. Run through them once on screen to show nothing broke.
  5. If asked, add a table/card toggle stored in state or in the URL, rendering one of two components from the same data.

Follow-up

  • How do users sort in the card view, now that there are no column headers?
  • How would you remember the user's chosen view across reloads?
  • What accessibility do you lose by dropping the table, and how do you restore it?

How would you design a database schema for a cinema booking system, including seat availability and show timin

Medium · Technical Fundamentals & Database Design

How would you design a database schema for a cinema booking system, including seat availability and show timings?

Approach

  1. Entities: cinema; screen (cinema_id); seat (screen_id, row_label, seat_number, seat_type, with UNIQUE(screen_id, row_label, seat_number)); movie (title, duration); show (movie_id, screen_id, starts_at, ends_at, base_price); booking (user_id, show_id, status, total_amount, created_at); show_seat (show_id, seat_id, booking_id NULL, status, hold_expires_at), created as one row per seat per show when the show is scheduled.
  2. Pick one guard and say so. With pre-created rows, the guard is a conditional UPDATE and its affected row count: 1 means you got the seat, 0 means you lost the race. Keep UNIQUE(show_id, seat_id) on show_seat for integrity, so a seat can never have two rows, but do not present it as the thing that stops double booking.
  3. Hold and availability: UPDATE show_seat SET status = 'held', booking_id = $1, hold_expires_at = now() + interval '...' WHERE show_id = $2 AND seat_id = $3 AND (status = 'available' OR (status = 'held' AND hold_expires_at < now())). The expired-hold clause lets a seat be re-taken without waiting for a sweeper. Free seats for a show are the rows matching that same predicate; index (show_id, status).
  4. Cancellation and timings: a cancelled booking sets its seats back to 'available' and clears booking_id and hold_expires_at in the same transaction. If you keep a booking_seat history table instead, use a partial unique index on (show_id, seat_id) WHERE status = 'active', so cancelled rows do not block a rebooking. For show timings, stop overlaps on a screen with a check in the write path or, in Postgres, an exclusion constraint over a time range per screen_id, and store times with time zone.
  5. Pricing: compute the per-seat price from seat_type and the show's base price, and store the charged amount on the booking so later price changes do not rewrite history.

Follow-up

  • Two users select the same seat at the same moment. Walk through exactly what happens.
  • A user holds seats and abandons payment. How are those seats released?
  • Write the query that lists all of today's shows for a movie with their remaining seat counts.

Can you design a schema that supports nested comments (replies to replies) up to the Nth level?

Medium · Technical Fundamentals & Database Design

Approach

  1. Start with an adjacency list: comments(id, post_id, parent_id NULL REFERENCES comments(id), author_id, body, created_at, depth). It supports any depth with one self-referencing foreign key and cheap inserts.
  2. Reading a thread: fetch all comments for a post in one query (WHERE post_id = $1 ORDER BY created_at) and build the tree in memory in O(n) with a map from id to node. Alternatively, use WITH RECURSIVE to fetch a subtree starting from one comment.
  3. Name the alternatives and their trade-offs. A materialized path (path '0001/0004/0009' with a prefix index) gives subtree reads and thread ordering through a single ORDER BY path. A closure table (ancestor_id, descendant_id, depth) makes subtree and ancestor queries easy but costs extra rows on each insert. Nested sets give fast reads but expensive writes.
  4. Deletion: soft-delete a comment that has replies (keep the row and show it as removed) so its children are not orphaned. Hard-delete leaf comments if the product allows it.
  5. For the UI, store depth so the display can cap indentation while the data still allows unlimited nesting.

Follow-up

  • How would you paginate top-level comments while still loading their first few replies?
  • How do you show a reply count on each comment without counting on every read?
  • What happens to the children when a comment in the middle of a thread is deleted?

Can you explain the concept of callbacks in Rails?

Medium · Technical Fundamentals & Database Design

Approach

  1. Define them: Active Record callbacks are hooks that run at points in a model's lifecycle. Give the order on create: before_validation, validation, after_validation, before_save, around_save, before_create, around_create, after_create, after_save, then after_commit or after_rollback once the transaction ends.
  2. Halting: from Rails 5 onward, throw(:abort) in a before_* callback stops the chain and the save returns false. Returning false no longer halts it.
  3. Name the pitfalls: update_column, update_columns, update_all, delete and insert_all skip callbacks, and side effects such as emails or jobs placed in after_save run before the transaction commits. Put them in after_commit so a rollback cannot send a mail about a record that never existed.
  4. Mention controller callbacks (before_action, after_action, around_action) as the other meaning of the term, for example before_action :authenticate_user!.
  5. Give a judgement: callbacks suit data normalisation on the model itself (such as downcasing an email). Cross-model workflows are usually clearer in a service object that is called explicitly.

Follow-up

  • What is the difference between after_save and after_commit, and when does it matter?
  • How would you skip a callback in a data migration, and what is the risk?
  • When would you move logic out of a callback into a service object?

Write a program to print a specific alphabet pattern (e.g., 'K') using loops.

Medium · Technical Fundamentals & Database Design

Approach

  1. Treat the letter as a grid of n rows and decide, for each (row, column) cell, whether to print a star. For a K with odd n, let mid = n // 2. Every row has a star at column 0 and a second star at column abs(mid - i) + 1, giving a diagonal that moves in toward the middle and back out.
  2. Code: an outer loop over i in 0..n-1 and an inner loop over j in 0..mid+1, printing '*' when j == 0 or j == abs(mid - i) + 1 and a space otherwise, with a newline after each row. Building the row as a string does not remove trailing spaces: the middle row still ends in padding. Either rstrip the row before printing, or stop the inner loop at the last star column, abs(mid - i) + 1.
  3. Cost: O(n^2) time to visit the cells and O(n) extra space per row string.
  4. Edge cases: even n has no single middle row, so state whether the two central rows share it. n = 1 is a single star. Validate negative or zero input.
  5. Generalise when asked: each letter is just a different predicate on (i, j). X is j == i or j == n - 1 - i, and Z is i == 0, i == n - 1 or j == n - 1 - i.

Follow-up

  • Print 'X' or 'Z' using the same loop structure.
  • How would you print a whole word, with letters side by side?
  • Avoid trailing spaces on each line. What changes?

Edge instances grow 400 MB per hour until the nightly restart

Hard · memory leak · heap analysis · metric cardinality

Edge API instances start at 700 MB resident and grow about 400 MB/hour; a nightly rolling restart has hidden it for weeks. Growth continues unchanged when request rate halves overnight, p99 degrades in the last hours before an instance is recycled, and heap used immediately after a forced full GC rises monotonically. The service holds no product state. Name the discriminating measurement that separates the plausible causes, give the most likely cause, and give the fix and how you would verify it.

Approach

  1. Separate resident memory from live heap first, because they fail differently. Resident size can grow from fragmentation, native buffers or thread stacks while the heap is flat; heap used after a full GC rising monotonically is the measurement that says objects are reachable and not being released. You already have it, so this is retention, not fragmentation, and that closes off half the candidate list.
  2. Use the rate's independence from traffic as the discriminator. Growth that continues at half the request rate rules out per-request objects that are merely slow to collect and points at a structure that grows with distinct values observed rather than with call volume. Write the candidates that have that property: a metrics registry keyed on a high-cardinality label, an unevicted cache, an interner, a per-key lock map.
  3. Take two heap snapshots an hour apart and diff by retained size, reading the dominator tree, not by allocation count or instance count. Expect one root holding a map with millions of entries, then follow the reference chain to the code that inserts and never removes. Allocation profilers point at churn, which is the wrong signal here.
  4. The candidate that fits this service is an observability label carrying an identifier, such as a request path recorded before templating so that /v1/resources/48213 becomes its own metric series. That grows with distinct ids seen, is independent of rate, and explains the late p99 degradation, since GC cost rises with the size of the live set.

Follow-up

  • Post-GC heap is now flat but resident size still creeps. What are you looking at, and does it matter?
  • How would you have detected this before an OOM, given the nightly restart masked the trend?

Suggested practice checklist

Each day of the plan targets one cluster of reported questions and ends with something you built or wrote down. It moves from your own project, through React and API features and the two schema questions, to fundamentals and a timed live-change mock.

Day 1 — Own the project you will be asked to walk through

  • Draw your main project's architecture (React client, REST API, database, external services) and trace one feature from click to query to rendered response.
  • Write the schema of its three most important tables, with keys and one decision you would defend.
  • Clone the repository into a fresh folder, follow your own README, and fix whatever stops it running.

Deliverable: An architecture diagram with one feature traced end to end, a written schema with one defended decision, and a project that runs from a clean clone.

Day 2 — React pagination and the table-to-card refactor

  • Build a list backed by a small API with server-side pagination. Store the page in the URL and ignore stale responses with an AbortController.
  • Add a keyset (cursor) version of the same endpoint and write down when you would choose each one.
  • Convert the list's table into a responsive card grid that renders from a shared column config, with loading, empty and error states intact.

Deliverable: A working paginated list in both OFFSET and cursor versions with a written comparison, and the same data rendered as a card grid.

Day 3 — Filters and approval-gated admin data over REST

  • Add GET /products?min_price=&max_price=&min_rating= with validation, bind parameters and a stored avg_rating, and decide how products with no rating are treated.
  • Add a status column, a super_admin role, a public endpoint that returns only approved rows and a role-checked approve endpoint that returns 403 to anyone else.
  • Wire both into React: debounced filter inputs that reset the page, and an Approve button shown only to super-admins that updates the list after approval.

Deliverable: A filter endpoint and an approval flow, each working end to end in your project, with server-side checks you can point to in the code.

Day 4 — Cinema booking schema, in SQL and in a document store

  • Write the CREATE TABLE statements for cinema, screen, seat, movie, show, booking and a show_seat table with one pre-created row per seat per show, including UNIQUE(show_id, seat_id) for integrity.
  • Write the availability query for one show and the conditional-update hold (status 'available' or an expired hold), and describe aloud what the losing request sees when two users race for one seat.
  • Add a guard against overlapping shows on a screen, a rule that frees the seat when a booking is cancelled, and a rule for releasing expired holds.
  • Sketch the same model as documents: seats embedded in the show or one document per seat per show, and the atomic conditional update that takes a hold.

Deliverable: A runnable schema file, an availability query, a seat-hold statement, a document-store sketch and a written walk-through of the race between two users.

Day 5 — Nested comments and soft deletes

  • Write an adjacency-list comments table, load one post's thread in a single query and build the tree in memory with a hash map.
  • Write the same subtree read with WITH RECURSIVE, then outline a materialized-path and a closure-table version with one sentence on each trade-off.
  • Work the soft-deleted accounts practice question, then apply the same soft-delete reasoning to a comment that has replies.

Deliverable: A comments schema with both read paths written, a note comparing three tree models, and a worked soft-delete answer.

Day 6 — Fundamentals: npm lockfiles, Rails callbacks, loop patterns

  • Explain package.json versus package-lock.json aloud, including ^ versus ~, npm ci versus npm install, and why the lockfile is committed.
  • Write the Active Record callback order from memory, a before_save that halts with throw(:abort), and an example of why a mail belongs in after_commit.
  • Write the K pattern for odd n from a predicate on (row, column), without trailing spaces, then change the predicate to print X and Z.

Deliverable: A spoken lockfile explanation, a written callback-order sheet with two code examples, and a working pattern program that prints K, X and Z.

Day 7 — Timed live-change mock and introduction

  • Ask a friend to request one unseen change to your project (a new endpoint, a new filter, or a different view) and make it while sharing your screen and talking through it, in about 30 minutes.
  • Rehearse 'tell me about yourself' so that it ends on the project you just modified.
  • Work the deadline-trade-off practice question as a spoken story about a shortcut you shipped and how you contained it.

Deliverable: One completed live change on your own project, a rehearsed introduction, and a spoken deadline story.

Behavioural preparation

Candidates report that technical skills are the priority and that a standard 'tell me about yourself' introduction is expected. Keep yours short and use it to point toward your main project. The prompts below help if a project discussion turns to how you worked: vague requirements, changes made under time pressure, and failures caused by bad data.

  1. Tell me about yourself.
  2. Walk through your main project and name the one design decision in it you would make differently today.
  3. Describe a time you had to change working code quickly, such as reworking a view or adding an endpoint, and how you made sure nothing else broke.
  4. Tell me about a requirement that arrived vague and the questions you asked before writing any code.
  5. Describe a bug caused by missing or invalid data, how you found it and what the application does now in that case.

How do you handle pagination in React for large datasets?

Medium · Project-Based Technical Deep Dives

Approach

  1. Choose where pagination happens. If you have only a few hundred rows, fetching everything and slicing on the client is fine. For large datasets, the server returns one page: either LIMIT/OFFSET with a total count, or keyset (cursor) pagination such as WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT 20.
  2. State the trade-off: OFFSET is simple and allows jumping to page N, but it slows down on deep pages and can skip or repeat rows when data changes. A cursor is stable and fast, but it only supports next and previous.
  3. React: keep page or cursor in state, ideally mirrored in the URL. Fetch in useEffect and ignore stale responses (AbortController or an ignore flag) so fast clicking cannot render the wrong page. Keep the previous page visible while the next one loads.
  4. For infinite scroll, use an IntersectionObserver on a sentinel element. If the rendered list grows very large, virtualise it (for example with react-window) so only the visible rows are in the DOM.
  5. Edge cases: resetting to page 1 when filters change, requesting a page past the end, an empty dataset, and always using a stable sort with a unique tie-breaker.

Follow-up

  • A user clicks Next three times quickly and sees page 2's data on page 4. What went wrong, and how do you fix it?
  • How does the backend return the total count, and what does that cost on a large table?
  • When would you choose infinite scroll over numbered pages?

Unblock an engineer without taking the keyboard

Easy · mentoring · leases · at-least-once

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

Approach

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

Follow-up

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

Ship under a deadline and bound the debt you chose

Medium · pagination · technical debt · tradeoffs

You have four days to ship a tenant-facing listing endpoint. The version you would defend uses keyset pagination over (tenant_id, status, updated_at DESC, resource_id DESC); the version you can finish uses LIMIT/OFFSET with no matching index. Describe a deadline call you actually made of this shape: what you shipped, what you knowingly deferred, how you bounded the damage with a mechanism rather than an intention, and the specific numeric condition that would force the follow-up. Name who you told and where you wrote it down.

Approach

  1. Name the deferred failure precisely instead of calling it slow. OFFSET n makes the database produce and discard n rows, so cost grows with page depth; without an index matching the sort, every matching row is read and sorted before the limit applies; and rows inserted between two page fetches shift across the boundary so items are skipped or repeated with nothing in the response to signal it.
  2. Bound the blast radius with something mechanical rather than a promise: cap maximum page depth, cap page size, restrict the endpoint to one internal caller, or keep it behind a flag. State which failure each cap removes and which it leaves standing.
  3. Attach a number to the trigger and wire it to an alarm: the first tenant crossing N resources, or the endpoint's p99 crossing its share of the 400 ms budget, so the debt announces itself instead of waiting to be remembered.
  4. Write it where the next engineer looks, which is the code and the ticket, not a chat message: what was deferred, why, the cap, and the trigger.

Follow-up

  • At what page depth does the offset version breach your latency budget, given your page size and row counts?
  • What breaks first when you switch to keyset pagination later, and what does a client holding an old page token see?