cswork / libraryOpen practice workspace

Interview guides · Amura Marketing Technologies

Amura Marketing Technologies Software Engineer Interview Guide

Amura Marketing Technologies Software Engineer interview prep: MySQL, MongoDB, Rails and puzzle questions with worked answers and a 7-day plan.

The 60-second brief

Candidates describe Amura Marketing Technologies as a company that builds marketing technology software, which businesses use to manage and improve their online presence. The Software Engineer role they describe builds and maintains web applications and integrations that move substantial amounts of data. Candidates describe a stack of Ruby on Rails, JavaScript, MySQL and MongoDB, and the reported questions mention jQuery. The reported day-to-day work covers turning product requirements into technical specifications, tuning databases and connecting modules of the platform, reviewing code, writing documentation and sometimes fixing production issues. For preparation, the database layer and the Rails stack matter as much as the algorithm questions.

This guide is for candidates in the Amura Marketing Technologies Software Engineer loop. The requirements candidates describe apply to both new graduates and experienced engineers. Ruby on Rails, JavaScript and SQL or NoSQL databases are the core requirements. Cucumber, RSpec, other testing frameworks and data structures are listed as a plus. The guide covers relational and document-database fundamentals, SQL joins, MongoDB aggregation, Rails modelling and routing, Rails testing, browser-side JavaScript, Python-style modularity, linked-list and array coding, logic puzzles, and the project and leadership conversations.

Candidates report four rounds over roughly 3-5 weeks: an Initial Assessment, a Technical Screening, a Programming Task that may be a take-home, and Discussions with Leadership.

Focus areas

  • Ruby on Rails
  • JavaScript
  • MongoDB

Overview

The reported questions fall into three groups. The first is database fundamentals: MongoDB versus MySQL, primary keys in each, UUIDs in MySQL, MongoDB aggregation, and SQL join types including the full join versus Cartesian product question. The second is coding and logic: detecting a loop in a linked list, reversing a linked list, the maximum sum of consecutive numbers, the 5- and 4-litre jar puzzle, and a pattern-based programming task. The third is the web stack: Rails associations, routes and modelling, Cucumber and RSpec, modules in Python or similar languages, and browser logic with JavaScript and jQuery. Most of these are explain-the-concept questions, so your preparation should produce precise answers that each come with an example. Grinding large numbers of algorithm problems will help less.

Six of the 15 reported questions are about databases, and the answers that stand out work at the storage level. Know that InnoDB clusters rows on the primary key. Know that MongoDB's _id is an indexed ObjectId by default, and that a UUID belongs in BINARY(16), not CHAR(36). Know that MySQL has no FULL OUTER JOIN, and that an aggregation pipeline only uses an index in its first stages. Have one concrete case ready for when you would choose each database, because the comparison questions invite a follow-up on exactly that.

The coding questions are classic problems: Floyd's cycle detection, three-pointer reversal and Kadane's algorithm. Candidates report the Technical Screening as whiteboard-style, so practise writing these without running them. Trace them by hand on empty, single-element and all-negative inputs, and state the time and space cost unprompted. Candidates also report logical reasoning tested through puzzles and pattern tasks. For the jar puzzle and the pattern task, clarify the constraints first, then model the problem as states or as a row-by-row formula instead of guessing.

Candidates report that past projects are a main topic of discussion, that the resume is used to steer technical questions, and that the last round is with senior leadership or directors and focuses on cultural alignment. Every technology on your resume, whether Rails, MongoDB, RSpec or jQuery, needs a deeper answer behind it. Prepare one project you can walk through from inception to deployment, including the hardest bug and the trade-offs you made.

The Amura Marketing Technologies interview process

The opening test and the whiteboard screen lean on database and linked-list fundamentals, so those answers need to be exact before the first stage. The take-home is where Rails coding standards and tests show, so build that scaffolding before you are asked for it. Each stage below lists what to practise.

Initial Assessment

Candidates report that the process opens with an online test or a set of technical screenings. Nothing more specific is reported about its content, so prepare broadly. Candidates say a significant portion of the process covers basic programming concepts and database operations. Write short, exact answers to the database fundamentals (MongoDB versus MySQL, primary keys, UUIDs, join types), and practise the array and linked-list problems in a plain editor without autocomplete or a debugger, in case the test is online.

What to demonstrate

  • Breadth across programming and database fundamentals, as candidates describe the early tests
  • Basic programming concepts and database operations, which candidates say take up a significant portion of the process

How to prepare

  • Write a five-line answer for each database fundamentals question (MongoDB versus MySQL, primary keys, UUIDs, join types) and check every claim against the MySQL and MongoDB documentation
  • Code Kadane's maximum consecutive sum, linked-list reversal and Floyd's cycle detection in a plain editor, then run them against empty, single-element and all-negative inputs
  • Solve the 5- and 4-litre jar puzzle on paper and write down the shortest sequence of pours

Technical Screening

Candidates describe in-person or remote sessions at this stage, where you write code on a whiteboard and answer in-depth technical questions. Whiteboard coding means writing code you cannot run, so say your invariants out loud and trace the code by hand before you call it done. A technical deep dive means following one topic several layers down, for example from 'what is a primary key' to clustered indexes and why random keys slow down inserts. Candidates also report that the resume steers technical questions, so expect any technology you list to be a possible starting point.

What to demonstrate

  • Whiteboard-style coding, as candidates describe this stage
  • Technical deep dives, as candidates describe this stage
  • Command of the technologies on your own resume, which candidates say interviewers use to steer questions

How to prepare

  • On paper, reverse a linked list and detect its cycle, drawing the pointers at each step for 0-, 1- and 3-node lists
  • Take three database answers (primary keys, UUIDs, aggregation) and write the next two layers of each, such as the storage effect and the performance consequence
  • For every technology on your resume, write one problem you solved with it and one limitation you hit

Programming Task

Candidates report a programming task or a take-home assignment at this stage, used to show coding standards. The stack for the task is not reported, so be ready in the technologies the role lists: Ruby on Rails, JavaScript and a SQL or NoSQL database. As preparation, treat it as a small production deliverable. It should run from a clean checkout and have a README that states your assumptions and how to run it. Use clear names and small functions, and add tests that cover the edge cases. A working project skeleton in your strongest listed technology saves setup time if the task lets you choose.

What to demonstrate

  • Working code that meets the stated requirements
  • Coding standards, which candidates say the task is used to show

How to prepare

  • Keep a minimal Rails app ready with RSpec configured, one model spec, one request spec and a factory, so a take-home starts from tested scaffolding
  • Write a README template with sections for assumptions, setup, running tests, trade-offs and what you would do with more time
  • Take the maximum consecutive sum solution and turn it into a small library function with unit tests for empty, all-negative and single-element input

Discussions with Leadership

Candidates report this round as conversations with senior leadership or directors, aimed at cultural alignment. Candidates also say past projects are a main topic in the process and that being able to explain your own contribution matters a great deal. Come with one project you can tell end to end, from requirement through deployment, and with examples of working under pressure and explaining technical ideas simply. Keep each answer about what you personally decided and did.

What to demonstrate

  • Cultural alignment, as candidates describe the purpose of this stage
  • Your explanation of your own contribution to past projects, which candidates say matters a great deal
  • Preparation tip: have examples of working under pressure ready

How to prepare

  • Write a two-minute walkthrough of your strongest project: the problem, your part, the main technical decision, the result
  • Prepare STAR stories for a production issue you fixed, a time you explained a technical idea to a non-engineer, and a disagreement you resolved
  • Write three questions to ask leadership about how engineering work is planned and reviewed

Where candidates lose points

Answering MongoDB versus MySQL with 'NoSQL is schemaless and faster' and nothing underneath

Compare data model, transactions, joins versus $lookup, indexing and scaling, then pick one for a concrete case. Practise the primary key and UUID answers at storage level too: clustered index, ObjectId structure, BINARY(16).

Losing the list on the whiteboard: overwriting curr.next before saving it, or calling fast.next.next when fast.next is null

Draw boxes and arrows and trace 0-, 1-, 2- and 3-node lists by hand before you say the code is done. Write the loop condition for Floyd's algorithm as 'while fast and fast.next' every time.

Returning 0 for an all-negative array in the maximum consecutive sum problem

Ask whether an empty subarray is allowed, start both cur and best at the first element, and test [-3, -1, -2] aloud before declaring the solution finished.

Guessing pours on the jar puzzle or the pattern task without fixing the rules first

Confirm that 7 litres means a combined total and which operations are allowed, then reason in states (a, b) or a row-by-row formula. Know that BFS over at most 30 states gives the shortest answer if you are asked to code it.

Listing Cucumber, RSpec, jQuery or MongoDB on the resume with only surface knowledge

Candidates report the resume is used to steer technical questions. For each listed technology, prepare one problem you solved with it, one limitation you ran into, and the second-level answer (N+1 fixes, the test pyramid, event delegation, pipeline index use).

Questions & practice

How would you detect a loop in a linked list?

Medium · Coding & Problem-Solving

Approach

  1. Baseline: walk the list and store each visited node reference (not its value) in a hash set, and report a loop when a node repeats. O(n) time, O(n) space.
  2. Floyd's tortoise and hare: slow moves one step and fast moves two. If they meet there is a loop. If fast or fast.next reaches null there is none. O(n) time, O(1) space. Check fast and fast.next before taking two steps.
  3. To find where the loop starts, put one pointer back at the head after the meeting and move both one step at a time. They meet at the entry. The reason: if the head is a steps from the entry, the meeting point is b steps into a loop of length c, and slow walked a + b, then 2(a + b) = a + b + kc, so a = kc - b.
  4. To get the loop length, keep one pointer at the meeting point and count steps until the other returns to it.
  5. Edge cases: an empty list, a single node pointing to itself, a two-node loop, and a loop that starts at the head.

Follow-up

  • Return the node where the loop begins, and prove why the reset trick works.
  • Remove the loop without losing any nodes.
  • How does Brent's algorithm differ, and when is it faster?

Write a program to find the maximum sum of consecutive numbers in an array.

Medium · Coding & Problem-Solving

Approach

  1. Clarify first. Are the numbers contiguous (yes, 'consecutive'), can the array be empty, and if every value is negative, is the answer the largest element or zero (empty subarray allowed)?
  2. Brute force with prefix sums checks every (i, j) pair in O(n^2). Kadane's algorithm is O(n) time and O(1) space: cur = max(x, cur + x), best = max(best, cur), with both starting at the first element so all-negative input works.
  3. To return the subarray, record a tentative start whenever cur restarts at x, and save start and end whenever best improves.
  4. Worked check: [-2, 1, -3, 4, -1, 2, 1, -5, 4] gives 6, from [4, -1, 2, 1]. [-3, -1, -2] gives -1, not 0.
  5. Mention the divide-and-conquer alternative (O(n log n), using the best sum crossing the midpoint) to show you know why Kadane is preferred.

Follow-up

  • The array is circular. How does the answer change? (max of Kadane and total minus the minimum subarray, with a guard for all-negative input)
  • Find the maximum product of consecutive numbers instead.
  • The subarray must have at least k elements. What changes?

How would you reverse a linked list?

Medium · Coding & Problem-Solving

Approach

  1. Iterative version with three pointers: prev = null, curr = head. While curr: next = curr.next; curr.next = prev; prev = curr; curr = next. Return prev. O(n) time, O(1) space.
  2. Recursive version: reverse the rest, then head.next.next = head and head.next = null, and return the new head from the deepest call. O(n) time but O(n) stack, which can overflow on very long lists.
  3. On a whiteboard, draw the boxes and trace the pointers for 0, 1 and 2 nodes. The classic bug is overwriting curr.next before saving it, which cuts off the rest of the list.
  4. For a doubly linked list, swap prev and next on every node and return the last node visited.

Follow-up

  • Reverse the list in groups of k nodes.
  • Reverse only the nodes between positions m and n.
  • Check whether a linked list is a palindrome using O(1) extra space.

Solve the following: You have two measuring jars of 5 and 4 liters; how do you measure exactly 7 liters?

Medium · Coding & Problem-Solving

Approach

  1. Clarify the goal. Neither jar holds 7 litres, so the answer is a combined total in both jars (5 + 2 or 3 + 4) unless a larger vessel is available. Ask about water supply, spilling, and whether the jars have markings.
  2. A shortest sequence giving 3 + 4: fill the 4, pour it into the 5 (5-jar: 4, 4-jar: 0). Fill the 4 again and top up the 5 (5, 3). Empty the 5 (0, 3). Pour the 3 into the 5 (3, 0). Fill the 4 (3, 4), for 7 litres total. That is seven operations.
  3. Another route reaches 5 + 2: fill 5, pour into 4, empty 4, pour the 1 litre across, fill 5, top up the 4 (leaving 2 in the 5), empty 4, pour the 2 across, fill 5. It works but takes more steps.
  4. If asked to code it, run a BFS over states (a, b) with six moves: fill either jar, empty either jar, pour either way. There are at most 6 x 5 = 30 states, and BFS guarantees the fewest moves. Since gcd(5, 4) = 1, every whole-litre amount from 0 to 9 can be reached.

Follow-up

  • Write the BFS that prints the sequence of pours.
  • With jars of 6 and 4 litres, which totals are reachable and why?
  • How would you show that seven operations is the minimum?

Explain the logic for a pattern-derived programming task.

Medium · Coding & Problem-Solving

Approach

  1. The reported title is broad. Candidates describe pattern recognition as finding the sequence or logic behind a programming task. Ask for one or two more rows or terms before coding, and confirm whether the output is printed shapes or a number sequence.
  2. For printed shapes, write out rows i = 0..n-1 and find a formula per row, for example a centred pyramid has n - i - 1 spaces and 2i + 1 stars. Use nested loops, handle spaces and characters separately, and note the output is O(n^2) because that is how many characters there are.
  3. For number sequences, test in order: differences (constant means linear, constant second differences mean quadratic), ratios, alternating sub-sequences, and positional formulas such as squares, triangular numbers or Fibonacci-style recurrences.
  4. Check n = 0 and n = 1, trailing spaces, and even versus odd sizes for symmetric shapes, and say what your code does with invalid input.

Follow-up

  • Print a hollow diamond of size n.
  • Generate row n of Pascal's triangle in O(n) using C(n, k+1) = C(n, k) x (n - k) / (k + 1).
  • Find the next two terms of 2, 6, 12, 20, 30 and give the general formula.

Can you explain the difference between a full join and a Cartesian product?

Medium · Technical & Domain Expertise

Approach

  1. A full outer join matches rows on a condition and returns every matched pair, plus unmatched rows from both sides padded with NULLs. Every row from each table appears at least once, so the result has at least max(|A|, |B|) rows.
  2. A Cartesian product (CROSS JOIN) has no condition and returns every pairing, exactly |A| x |B| rows.
  3. Edge case that separates the two: FULL JOIN ON 1=1 gives the same rows as a cross join when both tables are non-empty. If one table is empty, the cross join returns zero rows while the full join returns the other table's rows with NULLs.
  4. MySQL has no FULL OUTER JOIN. Emulate it with a LEFT JOIN, then UNION ALL a RIGHT JOIN filtered to WHERE left.id IS NULL. Plain UNION would also drop genuine duplicate rows.
  5. Name the accidental Cartesian product: a comma join with a missing WHERE condition, or a join on a non-unique key that multiplies rows. Name the deliberate use: cross joining dates with campaigns to build a grid, then left joining the daily lead counts so missing days show as zero.

Follow-up

  • Write the MySQL version of a full outer join between campaigns and leads.
  • Your join returned millions of rows instead of thousands. How do you find the cause?
  • When is a cross join the right tool?

What are the differences between various types of joins in SQL?

Medium · Web Technologies & Databases

Approach

  1. Name each join and its result. INNER returns matched rows only. LEFT returns all left rows, with NULLs where there is no match. RIGHT is the mirror. FULL OUTER returns all rows from both sides (MySQL emulates it with LEFT JOIN UNION ALL an anti-joined RIGHT JOIN). CROSS returns every pairing. A SELF join links a table to itself, for example employee to manager.
  2. Show semi and anti joins: campaigns that have leads (EXISTS) and campaigns with none (LEFT JOIN leads l ON l.campaign_id = c.id WHERE l.id IS NULL, or NOT EXISTS). Avoid NOT IN when the subquery can return NULL, because then nothing matches.
  3. Explain ON versus WHERE for outer joins: a WHERE condition on the right table's columns removes the NULL rows and turns a LEFT JOIN into an INNER JOIN, so put that condition in ON.
  4. Explain fan-out: joining a one-to-many table before SUM repeats the parent values, such as a campaign budget repeated once per lead. Aggregate in a subquery first, then join. NULL keys never match each other.
  5. Performance: index the join columns. MySQL uses nested-loop joins and, from 8.0.18, hash joins for equi-joins without a usable index.

Follow-up

  • Find campaigns that have no leads, in two different ways.
  • A budget SUM came out doubled after adding a join. Why, and how do you fix it?
  • Why can NOT IN return no rows when NOT EXISTS returns some?

Explain the difference between MongoDB and MySQL.

Medium · Technical & Domain Expertise

Approach

  1. Start with the data model. MySQL stores rows in tables with a declared schema, foreign keys and joins. MongoDB stores BSON documents in collections, with a flexible schema and related data either embedded or referenced.
  2. Compare transactions. InnoDB gives multi-row ACID transactions. In MongoDB a single-document write is atomic, and multi-document transactions exist (since 4.0 on replica sets, 4.2 on sharded clusters) but cost more, so schemas are usually designed to avoid needing them.
  3. Compare querying and scaling. SQL with joins versus find() and the aggregation pipeline, where $lookup is a left outer join. MySQL scales with read replicas and needs extra tooling to shard. MongoDB has built-in sharding on a shard key and replica sets for failover.
  4. Finish with a choice and a reason, in a marketing-platform setting. Pick MySQL for accounts, campaigns, billing and permissions, where integrity across entities matters. Pick MongoDB for lead or engagement event records whose fields vary by channel or campaign and that are read as one unit. In Rails that means ActiveRecord with MySQL, or an ODM such as Mongoid with MongoDB.
  5. Avoid saying MongoDB is 'schemaless and faster'. The schema still exists in application code, and speed depends on access patterns and indexes.

Follow-up

  • How would you model campaigns and their leads in MongoDB, and when would you embed instead of reference?
  • What does adding a required field cost in MySQL compared with MongoDB?
  • How can you enforce a schema in MongoDB if you need one?

What is a primary key, and how does it function in MySQL versus MongoDB?

Medium · Technical & Domain Expertise

Approach

  1. Define it. A primary key uniquely identifies a row, cannot be NULL, and a table has only one, though it can span several columns.
  2. MySQL InnoDB. The primary key is the clustered index: rows are stored in a B+tree ordered by it, and every secondary index stores the primary key value. A wide or random key therefore makes every index bigger and scatters inserts. Without a declared key, InnoDB uses the first UNIQUE NOT NULL index, or a hidden 6-byte row id if there is none.
  3. MongoDB. Every document has an _id field with a unique index created automatically, and _id cannot be changed after insert. By default it is a 12-byte ObjectId (4-byte timestamp, 5-byte random value, 3-byte counter), so it sorts roughly by creation time. _id can be any BSON type except an array, a regex or undefined. An embedded document works as a composite key, in which case field order matters.
  4. Mention Rails conventions: an auto-increment id column by default, with self.primary_key available for legacy tables.
  5. Edge cases to name: natural keys that change (email addresses), composite keys in join tables, and collisions when you generate ids yourself.

Follow-up

  • Why can a random primary key slow down inserts in InnoDB as the table grows?
  • How would you get a document's creation time without a created_at field?
  • Can you change a document's _id, and if not, how would you migrate to a new key?

What are the common use cases for Cucumber and RSpec in testing?

Medium · Technical & Domain Expertise

Approach

  1. RSpec is the unit and integration layer: model specs for validations and methods, request specs for controller behaviour and status codes, and system specs for browser flows. It is built from describe, context, it, let and before blocks, with doubles and allow/expect for collaborators. Test data comes from FactoryBot factories, not hand-built fixtures.
  2. Cucumber is behaviour-driven acceptance testing. Feature files in Gherkin (Given/When/Then) describe a user-visible scenario in plain language, step definitions in Ruby implement it, and they usually drive a browser through Capybara. Its value is a specification that product people can read.
  3. Use the test pyramid as the trade-off: many fast RSpec unit specs, fewer request and system specs, and a small set of Cucumber features for the flows that must never break, such as signup or checkout. Cucumber runs slowly and breaks easily when the UI changes.
  4. Example pairing: an RSpec model spec asserts that an email must be unique, and a Cucumber scenario walks a visitor through signup and checks for the confirmation message.
  5. Edge cases: database state leaking between tests (transactional tests or DatabaseCleaner), JavaScript-dependent pages needing a real browser driver, and over-mocking that lets specs pass while the integration breaks.

Follow-up

  • When would you use RSpec system specs instead of Cucumber?
  • A browser test fails one run in twenty. How do you track it down?
  • What is the difference between a stub, a mock and a spy in RSpec?

What is a UUID in MySQL?

Medium · Web Technologies & Databases

Approach

  1. A UUID is a 128-bit identifier, written as 32 hex digits in five hyphen-separated groups (8-4-4-4-12), which makes 36 characters including the hyphens. MySQL's UUID() function returns a version-1 (time-based) UUID. IS_UUID validates a string, and from MySQL 8.0, UUID_TO_BIN and BIN_TO_UUID convert between text and binary.
  2. Store it as BINARY(16), not CHAR(36). It is less than half the size, which matters most when it is the primary key, because InnoDB copies the primary key into every secondary index.
  3. Explain the cost as a primary key. Random (v4) values land all over the clustered B+tree, causing page splits and a cold buffer pool. UUID_TO_BIN(uuid, 1) swaps the time fields so v1 values insert roughly in order. Time-ordered UUIDv7 generated in the application is another fix.
  4. Explain why you would use one: ids generated without a database round trip, records merged across databases or shards without collisions, and public ids that cannot be enumerated. Note that v1 exposes timestamp and node information, so use v4 or v7 for public ids.
  5. A common design: a BIGINT AUTO_INCREMENT internal primary key plus a UUID column with a unique index for URLs and APIs.

Follow-up

  • Inserts slow down as a UUID-keyed table grows. Why, and how would you fix it?
  • How would you expose records by UUID in Rails routes while keeping an integer primary key?
  • What is the difference between UUID() in MySQL and a random v4 UUID?

How does aggregation work in MongoDB?

Medium · Web Technologies & Databases

Approach

  1. An aggregation is a pipeline: an array of stages, each transforming the stream of documents. The core stages are $match, $group (with $sum, $avg, $min, $max, $push, $addToSet), $project/$addFields, $sort, $limit, $unwind, $lookup and $facet.
  2. Give a concrete pipeline: db.engagements.aggregate([{$match: {type: 'click'}}, {$group: {_id: '$campaignId', clicks: {$sum: 1}}}, {$sort: {clicks: -1}}, {$limit: 5}]). Then map it to SQL: WHERE, GROUP BY, ORDER BY, LIMIT. A $match after $group plays the role of HAVING.
  3. Performance: put $match and $sort first so they can use an index, because only the opening stages use indexes. Each stage has a 100 MB memory limit. Before MongoDB 6.0, large $sort or $group stages needed allowDiskUse: true. From 6.0, allowDiskUseByDefault is on, so they spill to disk unless that is disabled. Check the plan with explain().
  4. Pitfalls: $unwind multiplies documents and can inflate sums, $lookup on large unindexed collections is slow, and map-reduce is deprecated in favour of pipelines.

Follow-up

  • Rewrite a SQL query with GROUP BY and HAVING as a pipeline.
  • Join campaigns to engagement counts and return campaign names with their totals.
  • The pipeline is slow on a large collection. What do you check first?

Explain the role of modules in Python or similar modular programming concepts.

Medium · Web Technologies & Databases

Approach

  1. In Python, a module is a .py file with its own namespace, and a package is a directory of modules (with init.py, or a namespace package without it). Import searches sys.path, runs the module once and caches it in sys.modules, so later imports are cheap and share state.
  2. Cover the mechanics: import x versus from x import y, the if name == 'main' guard, all for star imports, relative imports inside packages, and the leading-underscore convention for private names.
  3. Explain why modularity matters: cohesive modules with a small public interface can be tested on their own and changed without touching callers.
  4. Circular imports: the symptom is an ImportError about a partially initialised module. Fix it by moving shared code into a third module, importing inside a function, or inverting the dependency.
  5. Connect to the stack candidates report. Ruby modules serve as namespaces and as mixins (include for instance methods, extend for class methods), Rails concerns are mixins, and JavaScript has ES modules (import/export) and CommonJS (require).

Follow-up

  • What happens when two Python modules import each other at the top level?
  • What is the difference between a Ruby module and a class, and when do you use include versus extend?
  • How would you split a 2,000-line file into modules?

How do you manage browser-side logic using JavaScript and jQuery?

Medium · Web Technologies & Databases

Approach

  1. Run code after the DOM is ready, using $(function () { ... }), DOMContentLoaded or script defer. Keep logic in named modules or functions, not inline onclick attributes.
  2. Use event delegation for dynamic content: $(document).on('click', '.js-delete', handler) works for rows added later, while $('.js-delete').click(...) binds only to elements that exist when it runs. Know bubbling versus capturing, and the difference between preventDefault and stopPropagation.
  3. For server calls, use $.ajax or fetch with promises or async/await. In a Rails app, send the CSRF token from the csrf-token meta tag as X-CSRF-Token, show loading and error states, and disable the submit button to prevent double submissions.
  4. For safety and speed, insert user content with .text(), not .html(), to avoid XSS. Cache selectors, batch DOM writes, and debounce input handlers. Client-side validation is for usability only, and the server must validate again.
  5. Know what replaces jQuery in modern browsers: querySelectorAll, classList, fetch and addEventListener. Be ready to say when keeping jQuery in an existing codebase is the right call.

Follow-up

  • A click handler fires twice. What are the likely causes?
  • Debounce a search box that calls the server on every keystroke.
  • How do you send a Rails form over AJAX and update the page from the response?

Exports duplicate a row range about once a week

Hard · leases · clock skew · idempotency

Roughly once a week an export writes a file containing a duplicated range of rows. The affected job_run rows show attempt = 1, status = succeeded, one started_at, and a lease_owner naming a different host from the one whose logs show the job starting. Leases last 30 seconds and are heartbeated every 10 from inside the handler; lease_expires_at is computed on the worker and compared against the database's now(). Find the mechanism, and give a fix that holds even if you cannot fix the clocks.

Approach

  1. Start from the fact that eliminates the obvious answer. attempt = 1 means no retry was recorded, so this is not a re-run after failure; two workers ran the same row concurrently and the takeover path never touched the counter. lease_owner naming a host other than the one that started the job is the same statement from the other side.
  2. Enumerate the mechanisms that cause a premature takeover, then find the signal that separates them. Either the lease genuinely expired because the heartbeat did not fire, which is what happens when the heartbeat runs on the handler's own thread and the handler makes a long blocking call, or it only appeared expired because two clocks disagree, since lease_expires_at is written from the worker's clock and evaluated against the database's. The discriminator is the distribution: incidents clustered on the longest exports indict the heartbeat, incidents clustered on one host indict skew. Measure both, and measure each host's offset against the database directly.
  3. Read the reclaim query precisely. In PostgreSQL now() is transaction start time, not statement time, so a reclaimer holding a long transaction compares against an older timestamp than expected; clock_timestamp() is the statement-time function. This is worth ruling in or out before you redesign anything, because it changes which rows look expired.
  4. Remove the second clock rather than trying to synchronise it. Issue and extend the lease in the database, with lease_expires_at = now() + interval '30 seconds' in both the claim and the heartbeat, so exactly one clock is ever compared and worker skew stops mattering to this predicate.

Follow-up

  • The displaced worker has already streamed half the file to object storage. What makes that side effect safe to repeat?
  • You now count takeovers. What alert fires on that counter, and at what threshold?

Suggested practice checklist

The primary key and UUID answers on day 1 feed the join answers on day 2 and the aggregation and index answers on day 3, and six of the 15 reported questions are about databases. Days 4 and 5 cover coding and puzzles. Days 6 and 7 cover Rails, the web front end and the leadership round.

Day 1 — Relational versus document storage

  • Write a comparison table of MySQL and MongoDB: data model, transactions, joins versus $lookup, indexing, scaling, and one marketing-platform use case for each (accounts and billing in MySQL, variable engagement events in MongoDB)
  • Write the primary key answer for both: InnoDB clustering and secondary indexes, ObjectId structure, composite keys
  • In a MySQL 8 instance, create one table keyed by BINARY(16) UUID and one by BIGINT AUTO_INCREMENT, insert rows with UUID_TO_BIN, and read them back with BIN_TO_UUID

Deliverable: A one-page comparison table, a written primary key answer, and a working UUID schema script

Day 2 — SQL joins and normalization by hand

  • Build campaigns and leads tables with an unmatched row on each side and a duplicate join key
  • Write INNER, LEFT, RIGHT, CROSS and the MySQL full outer join emulation, and predict each row count before running it
  • Write campaigns-with-no-leads with LEFT JOIN ... IS NULL and with NOT EXISTS, then reproduce a doubled SUM caused by fan-out and fix it
  • Take one flat leads-and-campaigns table through 1NF, 2NF and 3NF, then write down when you would denormalize it or embed the data in a MongoDB document

Deliverable: A SQL file with every join type, predicted and actual row counts, a fixed fan-out query, and a short normalization write-up

Day 3 — MongoDB aggregation and data debugging

  • Load a small engagements collection and write pipelines for top campaigns by clicks, monthly totals, and a campaigns lookup
  • Translate one SQL GROUP BY ... HAVING query from day 2 into a pipeline and compare the outputs
  • Work the practice debugging question on exports that duplicate a row range, writing the investigation sequence step by step

Deliverable: Three working pipelines, one SQL-to-pipeline translation, and a written debugging sequence

Day 4 — Linked lists and arrays without running code

  • On paper, write iterative and recursive linked-list reversal and trace both on 0-, 1- and 3-node lists
  • Write Floyd's cycle detection plus loop-start detection and explain the a = kc - b argument aloud
  • Write Kadane's algorithm returning both sum and indices, then test all-negative, empty and single-element inputs

Deliverable: Handwritten solutions for the three problems with traced edge cases, retyped and run to confirm they pass

Day 5 — Puzzles and pattern tasks

  • Solve the 5- and 4-litre jar puzzle by hand, then write the BFS that finds the shortest sequence of pours
  • Write a centred pyramid, a hollow diamond and row n of Pascal's triangle from row-by-row formulas
  • Practise saying the clarifying questions aloud before starting each puzzle

Deliverable: A BFS jar solver, three pattern programs, and a list of clarifying questions for puzzles

Day 6 — Rails modelling, routing and tests

  • Build a small Rails app with Customer, Order, LineItem and Product using has_many :through, with foreign keys and indexes in the migrations
  • Add nested resources with shallow routes and check the output of rails routes; reproduce an N+1 query and fix it with includes
  • Write RSpec model and request specs with FactoryBot and one Cucumber feature for creating an order, then add a README as you would for a take-home

Deliverable: A runnable Rails repo with associations, routes, RSpec specs, one Cucumber feature and a README

Day 7 — Browser logic, modules and the leadership round

  • Build the day-6 order form view with semantic HTML and a simple CSS layout, then add a jQuery feature: a delegated delete button and an AJAX form that sends the CSRF token and escapes output with .text()
  • Write the modules answer covering Python imports and circular imports, Ruby include versus extend, and ES modules
  • Write STAR stories for the practice prompts on arguing against a design and reversing your own decision
  • Write a two-minute project walkthrough covering the problem, your part, the main technical decision and the result

Deliverable: A working form view with an AJAX feature, a written modules answer, two STAR stories and a project walkthrough

Behavioural preparation

Candidates report that the last round is with senior leadership or directors and focuses on cultural alignment, and that past projects are discussed throughout the process. Your stories should show what you personally built and decided, how you handled pressure, and how you explained technical work to people outside engineering.

  1. Walk me through a project you took from the first requirement to deployment. What was your part, and what was the hardest technical problem?
  2. Tell me about a production issue you had to troubleshoot. How did you find the cause, and what did you change so it would not happen again?
  3. Describe a time you had to explain a technical decision to someone without an engineering background.
  4. Tell me about a time you were asked something technical you could not answer right away. What did you do?
  5. Describe a disagreement in a code review or design discussion and how it was resolved.
  6. Pick a technology on your resume and tell me about a limitation you ran into with it.

How do you handle associations, routes, and modeling in Ruby on Rails?

Medium · Technical & Domain Expertise

Approach

  1. Models and associations. Use belongs_to on the side holding the foreign key and has_many or has_one on the other. Use has_many :through when the join has its own attributes or logic, and has_and_belongs_to_many only for a bare join table. Use polymorphic belongs_to when one model attaches to several parent types. Add foreign key constraints and indexes in the migration, and choose a dependent: option on purpose.
  2. Modelling example: 'I gave Customer has_many :orders, Order has_many :line_items, and Order has_many :products, through: :line_items, so quantity and price live on the line item.' Put validations on the model and uniqueness indexes in the database, because validates :uniqueness alone does not prevent concurrent duplicates.
  3. Routes. resources gives the seven RESTful actions. Nest at most one level (resources :customers do resources :orders end) or use shallow: true. Use member and collection routes for non-CRUD actions and namespace for admin or API controllers. Check the result with rails routes.
  4. Performance and structure. Avoid N+1 queries with includes, preload or eager_load, use counter_cache for counts, keep controllers thin with strong parameters, and move logic shared across models into concerns or service objects.
  5. Edge cases: orphaned records after a delete, validations bypassed by update_column, and deep nested routes that make URLs and controllers hard to maintain.

Follow-up

  • When would you choose has_many :through over has_and_belongs_to_many?
  • How do you find and fix an N+1 query in a Rails page?
  • How would you version an API namespace in routes without duplicating controllers?

Argue against a design, lose, and commit anyway

Medium · disagreement · service boundaries · decision records

Describe a design you argued against and lost. State the failure you predicted as a named mechanism, not a feeling about complexity: two services that would need one transaction, a projection with no rebuild path, a write path with no idempotency key. Say what evidence you brought, what the decision maker weighed instead, and what you did after the decision was made: what you instrumented, what you wrote down, and whether the prediction came true. Five minutes.

Approach

  1. State the prediction in falsifiable form up front: the mechanism, the condition that triggers it, and the observable outcome. A prediction that cannot be checked also cannot be credited to you later.
  2. Show the evidence you had at the time and label each piece honestly as measured, analogous, or intuition. Keeping the intuition is fine; disguising it as data is the thing that erodes your standing in the next argument.
  3. Represent the opposing case at full strength, including the constraint you did not control: a fixed date, a team boundary, or the fact that the decision was cheap to reverse and yours was not.
  4. Make disagree-and-commit concrete. Name the artefact you left behind so the prediction could be settled without you: the alert and its threshold, the counter on the dashboard, the decision note that recorded the trade-off and the condition that would revisit it.

Follow-up

  • What threshold on that alert would have proved you right, and did anyone ever look at it?
  • If the same proposal arrived tomorrow with the same deadline, would you argue it the same way?

Reverse your own decision and price the reversal

Medium · reversibility · measurement · migrations

Describe a technical decision you made and later reversed. Pick one that cost something: a service you split and merged back, a cache you added and removed, an index you created that pushed the planner onto a worse plan, a projection you rebuilt from scratch. State what you believed when you decided, the measurement that changed your mind, how long the wrong version ran in production, and what the reversal cost in migrations, dual writes, and a deprecation window for callers you did not own.

Approach

  1. State the original rationale without irony, in the version you would still defend given what was known then. If it is not defensible, the story is about carelessness rather than judgement, and a different example serves you better.
  2. Give the measurement that moved with a before and after: the p99 that did not improve, the cache hit rate that sat at 40%, the plan that flipped to a sequential scan once the table passed a size you can name.
  3. Cost the reversal in steps, not adjectives: expand-and-contract deploys, the dual-write window, the callers who had to be notified, the rows already written in the wrong shape that had to be backfilled or abandoned.
  4. Distinguish reversal from rewrite by naming what you kept. Most good reversals preserve the schema or the interface and undo one decision inside it, which is also why they were affordable.

Follow-up

  • What in that decision was irreversible, and did you know it was irreversible when you made it?
  • How did you tell the people who had already built on top of the original decision?