cswork / libraryOpen practice workspace

Interview guides · Andolasoft

Andolasoft Software Engineer Interview Guide

Andolasoft Software Engineer interview guide: the 3 reported rounds, PHP, Laravel, SQL, jQuery and OOP questions with answer outlines and a 7-day plan.

The 60-second brief

Candidates describe Andolasoft as a company that builds digital products and AI-based solutions for client projects, from custom web applications to AI product development. Laravel is the framework named for backend work. Candidates describe the role as turning requirements into features, with QA engineers and senior reviewers involved. Some roles lean toward AI products, and candidates are told to be ready to explain how a software engineering background helps with bringing AI models into a product and releasing them.

This guide is for candidates applying for a Software Engineer position at Andolasoft. It covers the three reported rounds (Technical Screening, System Test, Managerial Assessment), answer outlines for the five reported fundamentals questions on OOP, GET versus POST in PHP, SQL optimization, jQuery and Laravel sessions and cookies, and a plan that also covers two scenario prompts candidates mention: debugging a slow PHP page and preventing SQL injection.

Candidates report three rounds, Technical Screening, System Test and Managerial Assessment, spread over about 3-5 weeks, and some describe the main stages as finishable in a single day.

Focus areas

  • Laravel
  • SQL
  • PHP

Overview

The reported requirements make PHP the must-have skill, together with SQL database design, JavaScript and jQuery. Laravel, exposure to AI/ML integration and Agile/Scrum experience are listed as nice-to-have. The topics candidates associate with the loop match that list: Laravel, SQL, PHP, JavaScript and object-oriented programming. Preparation therefore means being fluent in one stack end to end: an HTTP request arriving in a PHP or Laravel app, the SQL it runs, and the JavaScript or jQuery that updates the page afterwards.

The five reported questions are all fundamentals, so none can be answered by a one-line definition. Each has a textbook answer and a project answer. For OOP, that means the four principles plus a class design you actually wrote. For GET versus POST, the HTTP semantics plus CSRF and form handling. For SQL optimization, EXPLAIN output and an index you added. For jQuery, an honest comparison with plain browser APIs. For Laravel sessions and cookies, the config keys and the session driver you would choose in production.

Two scenario prompts are also reported: walking through the debugging of a slow-loading page in a PHP application, and handling data security so that SQL injection is prevented. Both go better with a fixed sequence you can recite. For the slow page, measure first (server time versus query time versus front-end weight), then fix the biggest cost. For injection, bind parameters everywhere and treat raw SQL as the exception that needs justification.

Candidates describe the System Test as a mini-project. Aim for code that someone else can read and maintain, not only a result that runs: build a small PHP or Laravel feature with validation and a normalised schema, then explain your logic out loud. The Managerial Assessment is described as a discussion of fit and contribution, so prepare a couple of detailed project stories covering the problem, your decision and the result.

The Andolasoft interview process

These are the three stages candidates describe, in the order they report them. Treat the descriptions as a reported pattern, not a published process, and expect the technical emphasis to shift with the project team.

Technical Screening

Candidates describe the Technical Screening as an initial technical validation of skills and knowledge. Candidates associate Laravel, SQL, PHP, JavaScript and OOP with the loop, and the reported questions add jQuery, so expect to explain concepts and justify choices, not only name them. Approach it by giving each answer in two layers: a short precise statement, then a concrete example from a project you built. Where a concept has a security or performance side, such as POST and CSRF or an index and write cost, say so unprompted.

What to demonstrate

  • Command of core PHP and OOP concepts, explained clearly
  • Working knowledge of SQL design and query performance
  • Understanding of Laravel's MVC structure and the web basics around it, such as HTTP methods, sessions and cookies

How to prepare

  • Write one-paragraph answers, with a code example, for the OOP, GET versus POST, SQL optimization, jQuery and Laravel session and cookie questions, then say them aloud without notes
  • Prepare a spoken sequence for debugging a slow PHP page: measure, inspect queries, check caching, then check front-end weight
  • Prepare a short answer on preventing SQL injection that names PDO or Eloquent bindings, validation and a least-privilege database user

System Test

The System Test is reported as a practical test of problem-solving. One tip places it between technical rounds, but only one technical round is listed, so the exact order is unclear. Candidates describe it as a mini-project, so aim for readable, maintainable code as well as a working result. They also suggest practising clean, commented code against the clock and being able to explain your logic afterwards. No fixed format is reported, so prepare for a small PHP or Laravel feature backed by a SQL schema.

What to demonstrate

  • Turning written requirements into working, structured code
  • Code quality and maintainability, not just a passing result
  • Handling edge cases and explaining the reasoning behind the solution

How to prepare

  • Build a small Laravel feature end to end, such as a CRUD resource with a migration, a form request for validation, and an index page, under a time limit you set yourself
  • Add brief comments and a short README, and rehearse a two-minute walkthrough of why the code is structured as it is
  • List the edge cases you would check on any form or list feature: empty input, duplicates, pagination boundaries, unauthorised access
  • Write every query with bound parameters and eager loading from the start, so the habits are automatic under time pressure

Managerial Assessment

Candidates describe the Managerial Assessment as a discussion with management about cultural fit and potential contributions. Candidates suggest preparing a couple of detailed project stories and asking the manager about the team's stack and review practice. Requirements also mention explaining technical constraints to non-technical stakeholders and working with project managers and QA, so prepare stories with that shape.

What to demonstrate

  • How you describe past work, including problems and the decisions you made
  • Collaboration: handling feedback, working with project managers and QA, contributing to team goals
  • Clarity when explaining technical constraints to people outside engineering

How to prepare

  • Write two project stories as problem, your decision, trade-off and result, with at least one number in each
  • Prepare a short explanation of a technical constraint, for example why a feature needs a schema change, aimed at a non-technical reader
  • Write two or three questions for the manager about the team's stack, the code review process and how testing with QA works

Where candidates lose points

Defining the four OOP principles from memory with no example from code you wrote

Prepare one small PHP design, for instance an interface with two implementations injected through a constructor, and walk through which principle each part shows. Be ready to say why you did not use inheritance.

Answering the SQL optimization question with 'add an index' and nothing else

Say you read the EXPLAIN plan first, then fix the access path, trim columns, rewrite joins or subqueries and remove N+1 queries, and name the write-cost trade-off. Bring one real before-and-after timing.

Saying POST is more secure than GET, or leaving out CSRF and the request body when comparing them

Contrast semantics (safe and idempotent versus state-changing), where data travels, and what ends up in logs and history. Then add that POST still needs HTTPS and a CSRF token.

Guessing at causes when asked to debug a slow-loading PHP page

Rehearse a fixed order: separate server time from query time from front-end weight, count queries per request, inspect the slowest with EXPLAIN, check caching, then compress or defer assets. Fix the largest cost first and confirm with a measurement.

Delivering a System Test solution that works but is uncommented, builds SQL by concatenating strings, and cannot be explained afterwards

Use bound parameters or Eloquent from the first line, validate input, name things clearly and add short comments. Reserve the last minutes for reading the code back and rehearsing why it is structured as it is.

Questions & practice

Identify the heaviest tenants in a five-minute window under memory pressure

Medium · top-k · heavy hitters · streaming

The edge service handles about 3,000 requests per second across roughly 50,000 tenants, peaking near 9,000. Expose the 50 heaviest tenants by request count over the trailing five minutes so limits can be tightened before one tenant's backfill starves the fleet. You may not retain five minutes of raw records. Give the exact solution and its memory, then the bounded-memory approximation with its error stated as a formula, and say which you would ship and at what tenant cardinality that choice changes.

Approach

  1. Do the exact version first, because it is affordable at this cardinality: a ring of 300 one-second counters per tenant, advanced lazily, is 1,200 bytes of counters per tenant and roughly 60 to 90 MB for 50,000 tenants with overhead. Carry a running total and subtract the bucket you overwrite so a window read is O(1) rather than 300 adds.
  2. Extract the top 50 with a size-k min-heap over the tenant sums: O(d log k) for d tenants, against O(d log d) to sort them all. Maintaining the heap continuously instead of on query requires a tenant-to-heap-index map, because incrementing a count already inside the heap means sifting from a known position, and without that map you rebuild the heap on every request.
  3. State the approximation precisely rather than gesturing at sketches. Misra-Gries with m counters retains every item whose true count exceeds N/(m+1), and each retained count underestimates the truth by at most N/(m+1). With m = 1,000 and N = 900,000 requests in the window the error is roughly 900 requests, which is fine for spotting a tenant sending 50,000 and useless for ranking two tenants 200 apart.
  4. Say what breaks when the window slides: Misra-Gries and Space-Saving are insert-only and cannot be decremented as records age out. The workable construction is one summary per sub-window, say ten seconds, with 30 summaries merged at query time, and the merged error is the sum of the per-summary errors, so the bound degrades linearly in the number of sub-windows.

Follow-up

  • The heaviest tenant is heavy because of one export job rather than user traffic. Should the limiter treat those as the same tenant?
  • Two tenants sit tied at the boundary of the top 50. Does your answer flap, and does the flapping matter?

Canonicalise a request body into a stable idempotency fingerprint

Medium · parsing · canonicalisation · hashing

idempotency_key.request_fingerprint is a SHA-256 over the method, path and canonicalised body, and a retry whose fingerprint differs must be rejected with 422 rather than served the stored response. Write the canonicaliser. Bodies are JSON up to 256 KB nested at most 32 levels; clients vary key order, whitespace and unicode escaping, and some send 64-bit ids as JSON numbers. Produce a deterministic byte string such that semantically identical bodies match and any semantic difference does not. State your complexity and name two normalisations you refuse to perform.

Approach

  1. Parse once into a tree, then re-serialise under fixed rules: object keys sorted, array order preserved, one escaping convention, no insignificant whitespace. Parsing is O(n) and sorting keys is O(k log k) per object, so O(n log n) overall with O(depth) stack, and the 32-level cap is enforced during parsing because hostile nesting is how a canonicaliser becomes a stack overflow.
  2. Sort keys by their UTF-8 bytes and say why the obvious implementation is wrong in some runtimes: a default string comparison that orders by UTF-16 code units places surrogate pairs, meaning code points from U+10000 up, below U+E000 to U+FFFF, which is not UTF-8 byte order, so two services written in different languages disagree on the same document.
  3. Do not re-encode numbers through a double. IEEE-754 binary64 represents integers exactly only up to 2^53, so normalising a 19-digit id through a float changes it, and 1 against 1.0 cannot be reconciled without deciding whether they are the same value. Preserve the literal token, and require ids as strings at the API boundary if you want them comparable.
  4. Reject duplicate keys rather than picking one. JSON permits them and parsers disagree, most keeping the last, so any choice you make ties the fingerprint to a parser detail that the code handling the request does not necessarily share.

Follow-up

  • A client sends the same logical request with an extra field your API ignores. Same key, different fingerprint, so you return 422. Is that the right answer?
  • Where does the fingerprint get computed relative to request decompression and the body-size limit?

Merge partitioned event streams into one ordered feed with bounded lateness

Hard · k-way merge · watermarks · out-of-order streams

The read-model service consumes 64 log partitions carrying about 4,000 events per second in total. Each partition is ordered within itself, but partitions drift by up to 30 seconds, and the activity feed must present a tenant's events in occurred_at order. Produce the merge. State its complexity, the buffer it requires in events and in bytes, what happens when one partition is idle, and what you do with an event that arrives after you have already emitted its position. Payloads average 1 KB.

Approach

  1. Merge with a min-heap over the 64 partition heads keyed on (occurred_at, event_id): O(log P) per event and O(n log P) overall. The tie-break on event_id is what makes the output deterministic when two partitions carry the same millisecond, which matters because the feed is paginated and a non-deterministic order reorders pages under the reader.
  2. Emitting the heap head is only correct once every partition has produced everything up to that timestamp, so the emit condition is a watermark: the minimum across partitions of the highest occurred_at seen, less the allowed lateness. Events are held until the watermark passes them, which is what turns individually ordered streams into a jointly ordered one.
  3. Size the buffer from the lateness rather than guessing: 4,000 events per second times 30 seconds is 120,000 buffered events, and at 1 KB each about 120 MB of heap. That number is the real price of the ordering guarantee and belongs in front of whoever asked for it.
  4. Handle the idle partition explicitly, because it fails the feed rather than corrupting it: a partition with no traffic never advances its own maximum, so the watermark freezes and output stops entirely. Either every partition emits a periodic idle marker carrying the broker's current time, or the watermark falls back to wall clock for a partition silent beyond a threshold.

Follow-up

  • The lateness budget is raised to five minutes. What is the new buffer, and what besides memory changes?
  • The consumer restarts. Where does it resume from, and what does the feed look like for the first 30 seconds?

How do you optimize complex SQL queries to improve application performance?

Medium · Technical Fundamentals

Approach

  1. Measure before changing anything. Find the slow statement in the slow query log or an APM trace, then run EXPLAIN (or EXPLAIN ANALYZE on MySQL 8.0.18+ and PostgreSQL). Look for full table scans (type ALL in MySQL), a rows estimate far above the rows returned, and Using filesort or Using temporary.
  2. Design indexes to fit the query. In a composite index, put the equality-filtered columns first and the range or ORDER BY column after them, because B-tree indexes are used left to right. A covering index that includes every selected column lets the engine skip the table lookup.
  3. Keep predicates sargable. WHERE DATE(created_at) = '2026-01-01' or LOWER(email) = ... wraps the indexed column in a function and blocks the index, so rewrite it as a range on the raw column. LIKE '%term' with a leading wildcard cannot use a B-tree index. An implicit type conversion, such as comparing a string column to a number, can block it too.
  4. Fix the query's shape. Select only the columns you need instead of SELECT *. When a one-to-many join would inflate a SUM or COUNT, aggregate the child table in a subquery before joining. Replace deep OFFSET pagination with keyset pagination (WHERE id > last_seen ORDER BY id LIMIT n), because OFFSET still reads and throws away every skipped row.
  5. Check the application layer and the cost of each fix. In Laravel, an N+1 pattern (one query per row in a loop) is fixed with eager loading via with(). Each new index slows inserts and updates and takes disk space. Confirm the gain by comparing plans and timings on data of production size.

Follow-up

  • The query has an index on the filtered column but EXPLAIN shows a full scan. What are the likely reasons?
  • How would you paginate a large table efficiently, and what does keyset pagination give up compared with OFFSET?
  • How do you spot and fix an N+1 query problem in a Laravel Eloquent page?

Stop tag and share joins from fanning out a page

Medium · join fan-out · semi-joins · many-to-many

resource_tag is (resource_id, tag_id) with PK (resource_id, tag_id); resource_share is (resource_id, shared_with_user_id, permission). The tagged-and-shared listing inner-joins resource to both, filters tenant_id, tag_id = ANY($2) and shared_with_user_id = $3, orders by updated_at DESC and takes 50. Pages come back with fewer than 50 distinct resources and the total in the header is far too high. Explain the row multiplication, rewrite both the page query and the count query so each is correct, and name the index each one needs. PostgreSQL 16.

Approach

  1. Do the arithmetic against the predicates that are actually there. An inner join emits one row per matching child row, and both joins are filtered: tag_id = ANY($2) admits only the requested tags, shared_with_user_id = $3 admits one user's share rows. So a resource holding three of the requested tags and shared with $3 once yields three rows, not one — the multiplier is its count of matching tags times its share rows for that single user, and that second factor is 1 unless the table admits duplicate (resource_id, shared_with_user_id) pairs. LIMIT 50 then limits rows rather than resources, and COUNT(*) counts pairs — the header is the product, not the population.
  2. Reject DISTINCT as the fix. It deduplicates after the product has been built, so the planner must materialise and sort the fanned-out set before the LIMIT can apply, and it leaves any SUM or AVG in the same select list wrong.
  3. Rewrite both filters as semi-joins, keeping resource as the only row source: AND EXISTS (SELECT 1 FROM resource_tag rt WHERE rt.resource_id = r.resource_id AND rt.tag_id = ANY($2)) and the same shape against resource_share. A semi-join stops at the first match per resource and preserves the driving index order, so ORDER BY updated_at DESC, resource_id DESC LIMIT 50 still stops after 50 rows.
  4. Count with the same predicates and no join at all: SELECT count(*) FROM resource r WHERE r.tenant_id = $1 AND r.status = 'active' AND EXISTS (...) AND EXISTS (...). Nothing multiplies a resource, so the number is the population.

Follow-up

  • The filter changes from 'any of these tags' to 'all of these tags'. Rewrite it and state what it costs relative to the ANY form.
  • A resource can be shared with the same user twice under different permissions. Does your count change, and should it?

Explain the core principles of OOP (Object-Oriented Programming) and how you apply them in your projects.

Medium · Technical Fundamentals

Approach

  1. Name all four principles and give each one a PHP mechanism: encapsulation through private/protected properties exposed by methods, abstraction through interfaces and abstract classes, inheritance through extends (PHP allows a single parent class), and polymorphism through calling one interface method on different implementations.
  2. Anchor it in one example from your own code, such as a PaymentGateway interface with a charge() method and two concrete gateways. Show that the calling code depends only on the interface, so you can add a third gateway without editing the caller.
  3. Explain how PHP covers reuse that single inheritance cannot: a class can implement many interfaces, and traits copy shared methods into unrelated classes. When two traits define the same method, you resolve the conflict with insteadof and alias with as.
  4. Say when you choose composition over inheritance. Deep extends chains couple subclasses to their parent's internals. Injecting collaborators through the constructor (which Laravel's service container resolves from type hints) keeps classes small and lets you mock them in tests.
  5. Finish with one principle you broke and how you fixed it. For example, a subclass that throws on a method its parent supports violates Liskov substitution. Splitting the interface fixed it.

Follow-up

  • When would you use an abstract class rather than an interface in PHP, and what can each one hold?
  • How do you bind an interface to a concrete class in Laravel's service container, and how does that help in testing?
  • Give an example of a Liskov substitution violation and the refactor that removes it.

Can you describe the difference between GET and POST methods in PHP?

Medium · Technical Fundamentals

Approach

  1. Start with where the data travels. GET parameters go in the URL query string and PHP exposes them in $_GET. POST data goes in the request body, and PHP fills $_POST only for application/x-www-form-urlencoded and multipart/form-data bodies.
  2. State the HTTP semantics. GET is meant to be safe and idempotent, so browsers and proxies can cache it, bookmark it and repeat it. POST changes state and is not idempotent, which is why a refresh after a form POST asks to resubmit. Use Post/Redirect/Get to avoid duplicate submissions.
  3. Correct the usual myth. POST is not encrypted and is no more secure in transit; both need HTTPS. GET values do end up in server logs, browser history and Referer headers, so tokens and passwords never belong in a query string.
  4. Cover the practical limits and PHP details. Servers and browsers cap URL length, so large payloads and file uploads need POST with enctype="multipart/form-data", which fills $_FILES. A JSON body does not populate $_POST: you read php://input and json_decode it. Validate and escape input from either array.
  5. Tie it to Laravel. Route::get and Route::post map the verbs, and POST routes in the web group pass through CSRF verification, so the form must include @csrf. HTML forms can only send GET and POST, so PUT, PATCH and DELETE are sent with @method spoofing.

Follow-up

  • How do you read a JSON request body in plain PHP, and why is $_POST empty for it?
  • Why is $_REQUEST risky to rely on, and what decides its contents?
  • How does Laravel let an HTML form send a DELETE request, and what protects that route from CSRF?

What are the advantages of using jQuery in modern web development?

Medium · Technical Fundamentals

Approach

  1. Give the real advantages with concrete APIs. jQuery offers short CSS-selector queries with chaining, $.ajax and $.getJSON for requests, built-in animations, and a large plugin ecosystem. Historically it also hid browser differences in the DOM and event APIs, which was its main selling point.
  2. Explain event delegation, which jQuery makes simple. $(document).on('click', '.row-delete', handler) binds one listener on an ancestor and matches descendants at event time. That listener also catches rows inserted later by AJAX, which a direct .click() binding misses.
  3. Give the modern counterweight. querySelectorAll, classList, addEventListener, closest() and fetch now cover most of what jQuery was used for in current browsers. jQuery adds a library download, and mixing direct jQuery DOM edits with React or Vue rendering puts the DOM out of sync with the framework's state.
  4. Say where it still pays off. It suits existing server-rendered PHP or Laravel pages that already depend on it and on its plugins, where a rewrite brings no user benefit. For new code, write native JavaScript or the project's framework.
  5. Cover one security point. .html(userInput) inserts raw markup and opens an XSS hole, while .text() escapes it. On Laravel POST requests made through $.ajax, send the CSRF token, for example with $.ajaxSetup and the X-CSRF-TOKEN header read from a meta tag.

Follow-up

  • Rewrite a $.ajax POST call using fetch and async/await. What changes in error handling?
  • Why does a handler bound with .click() miss elements added after page load, and how does .on() with a selector fix it?
  • How would you remove jQuery from a large legacy page step by step without breaking it?

Describe how you manage session and cookie data in a Laravel environment.

Medium · Technical Fundamentals

Approach

  1. Start with configuration. config/session.php (driven by SESSION_DRIVER) chooses where session data lives: file, cookie, database, redis, memcached or array. On more than one web server, use a shared store such as redis or database, or users get logged out when the load balancer switches servers. The database driver needs a sessions table migration.
  2. Show the API. Use $request->session()->put/get/forget/flush or the session() helper. flash() keeps a value for the next request only, which suits status messages after a redirect. On login, call $request->session()->regenerate() to prevent session fixation. On logout, call invalidate() and regenerateToken().
  3. Explain how Laravel handles cookies. The EncryptCookies middleware encrypts and signs cookies by default, so the client cannot read or tamper with them. Set cookies with Cookie::queue() or response()->withCookie(cookie('name', $value, $minutes)), and read them with $request->cookie('name'). If JavaScript must read a cookie, exclude it from encryption.
  4. Set the security flags. Keep the session cookie http_only so scripts cannot read it, set secure so it is sent only over HTTPS, and choose same_site (lax by default) to limit cross-site sending. SESSION_LIFETIME and expire_on_close control expiry.
  5. Know what goes where. Browsers cap a single cookie at about 4 KB, and it is sent with every request. Keep cookies small and store server-side state in the session. Never put secrets or full objects in a client-readable cookie, and remember the CSRF token itself lives in the session.

Follow-up

  • Users on a two-server deployment keep getting logged out at random. How do you diagnose and fix it?
  • What is session fixation, and which Laravel calls protect against it at login and logout?
  • How do you exclude one cookie from Laravel's encryption, and why would you need to?

Listing latency scales with page size, not with filters

Easy · n+1 · latency · query batching

The tenant listing endpoint reads resource filtered by tenant_id and status, ordered by updated_at DESC, and returns each row plus the owner's display name from app_user and the actor of that resource's latest resource_revision. p99 is 55 ms at 10 rows per page and 1.4 s at 200. Database telemetry shows 401 statements per request, each under 1 ms, and nothing in the slow-query log. Diagnose the cause and give the fix, stating the statement count per request and the p99 you expect afterwards.

Approach

  1. Read the counters before forming a theory. 401 statements for 200 rows is one driver query plus two per row, and sub-millisecond execution with an empty slow-query log rules out a bad plan. The time is round trips, which is why it is invisible in every per-query metric and scales with rows returned rather than with filter selectivity.
  2. Name the two per-row statements from their normalised text: a single-row app_user lookup by user_id, and a resource_revision lookup by resource_id ordered by version DESC LIMIT 1. Confirm by dropping those two response fields and watching the statement count fall to one. That locates the calls in the serialisation layer, not the repository.
  3. Check that the arithmetic accounts for the whole gap. Measure one round trip to the replica in isolation; 400 trips at roughly 3 ms of network plus 0.2 ms of execution is about 1.3 s on top of a 55 ms baseline, which matches. If the multiplication had fallen short, the N+1 would only be part of the story and you would keep looking.
  4. Batch both lookups. Collect owner_user_ids and resource_ids from the driver query, then issue WHERE tenant_id = $1 AND user_id = ANY($2) for the users, and PostgreSQL's SELECT DISTINCT ON (resource_id) ... WHERE resource_id = ANY($2) ORDER BY resource_id, version DESC for the latest revision, which the UNIQUE (resource_id, version) index serves directly. On an engine without DISTINCT ON, use a lateral join or a row_number window. Three statements per request at any page size.

Follow-up

  • The page size is capped at 200 today. What breaks first if it is raised to 2,000, and is it still this bug?
  • How do you stop the next N+1 from reaching production, given that no individual query is slow and the endpoint's tests pass?

Suggested practice checklist

The plan is built around the reported rounds and the PHP, Laravel, SQL, JavaScript and OOP topics candidates associate with them. Days 1 to 5 cover the fundamentals, day 6 simulates the practical System Test, and day 7 prepares the management discussion.

Day 1 — OOP and PHP design

  • Write a spoken answer to the OOP question that names the four principles and ties each to a PHP example
  • Build a small PaymentGateway interface with two implementations, constructor injection and a fake used in a test
  • Write a short note on when to use an abstract class, an interface or a trait

Deliverable: A written OOP answer, a working interface-based class example with a test, and a one-paragraph abstract class versus interface versus trait note.

Day 2 — HTTP methods, sessions and cookies

  • Write a plain PHP form that works with GET and POST and prints what $_GET, $_POST and php://input contain
  • In a Laravel app, build a login-style flow that regenerates the session ID, shows a flash message and sets one cookie with Cookie::queue
  • Read config/session.php and write down which driver you would use on several servers, and why

Deliverable: The two working form and Laravel examples, plus a half-page answer comparing GET and POST and describing session and cookie handling.

Day 3 — SQL performance and join fan-out

  • Take three slow queries on a sample schema, run EXPLAIN, add or change an index, and record plan and timing before and after
  • Create an N+1 list page in Laravel, count its queries, then fix it with eager loading

Deliverable: A before-and-after table of three queries with plan changes, the query counts for the N+1 page, and the written fan-out lesson.

Day 4 — Slow pages and SQL injection

  • Write and speak a debugging sequence for a slow-loading PHP page: measure, count queries, inspect the slowest, check caching, then check front-end weight
  • Demonstrate SQL injection against a deliberately naive query, then fix it with PDO prepared statements and again with the Laravel query builder

Deliverable: A one-page debugging checklist, a before-and-after injection demo with the fix, and notes comparing your checklist with the practice diagnosis.

Day 5 — JavaScript, jQuery and AI-service calls

  • Write a delegated click handler with jQuery that posts via AJAX with the Laravel CSRF token in a header, then rewrite it with plain JavaScript and fetch
  • Write an answer to the jQuery question that gives advantages and honest trade-offs against modern browser APIs
  • Prepare a short explanation of how front-end code and a PHP backend exchange data
  • Dispatch a call to a stubbed model service from a Laravel queued job with a timeout and retry, then explain how the result reaches the page without blocking the request

Deliverable: Both versions of the handler, a written jQuery answer with trade-offs, a short front-end to backend explanation, and a queued-job example with a spoken account of failure handling.

Day 6 — System Test rehearsal

  • Set your own time limit and build a small Laravel feature: migration, model, form request validation, controller and a list view
  • Add short comments and a README covering how to run it and the main design choices
  • Read the code back and explain aloud why it is structured as it is, then list the edge cases you did not cover

Deliverable: A committed mini-project with a README, plus a spoken walkthrough and a list of known gaps.

Day 7 — Managerial discussion and dry run

  • Write two project stories as problem, your decision, trade-off and result, with a number in each
  • Write two or three questions for the manager about the team's stack, code reviews and QA process, then run one full dry run of the day

Deliverable: Two written project stories, answers to the two practice prompts, a question list for the manager, and a completed dry run.

Behavioural preparation

The Managerial Assessment is described as a discussion of cultural fit and potential contributions. Candidates suggest preparing detailed stories about hard projects and how they were resolved, and being ready to talk through feedback, work with project managers and team goals. Requirements add explaining technical constraints to non-technical stakeholders and a habit of learning new technologies. No specific behavioral questions are reported, so rehearse these.

  1. Describe a complex project you worked on: the problem, what was hard, the decisions you made and the result.
  2. Tell me about a time you received critical feedback on your code and what you changed.
  3. Describe a time you had to explain a technical constraint to a project manager or other non-technical stakeholder.
  4. Tell me about a time you worked with QA or a reviewer to fix a problem before release.
  5. Describe a time you had to pick up a new technology quickly to deliver a feature.
  6. Tell me about a disagreement in a code review and how it was resolved.

Narrate an outage you owned from page to postmortem

Hard · incident response · blast radius · postmortems

Pick an incident you personally drove, ideally one where writes were affected rather than reads. In six to eight minutes: state the symptom as it first appeared on a dashboard, the blast radius you established before you knew the cause, the mitigation you applied and when, the mechanism you eventually proved, and the follow-up that would prevent a repeat. Bring numbers: error rate, tenants affected, minutes to mitigate, minutes to resolve. If you cannot name what you measured, choose a different incident.

Approach

  1. Open on the signal rather than the cause: which metric at which percentile moved, on which service, at what time, so the listener follows the same evidence you had rather than a conclusion you already reached.
  2. Separate mitigation from diagnosis out loud. State what you did to stop the bleeding (flag off, shed traffic, drain a lease, roll back a deploy) and say plainly that you did it before the mechanism was known, because those are two jobs with different deadlines.
  3. Establish blast radius in countable terms: how many tenants, how many writes, and crucially whether the effect was loss or only delay. An append-only revision table or a pending outbox row means the change survived and the projection was merely behind, which is a repair rather than a data-loss incident.
  4. Prove the mechanism instead of asserting it. Name the trace span that grew, the plan that flipped to a sequential scan, the lease that expired, plus one alternative you ruled out and the signal that stayed flat while you ruled it out.

Follow-up

  • What would you do differently in the first five minutes, given the same dashboard and no more information?
  • Which follow-up action did you deliberately not take, and why was dropping it the right call?

Estimate work you have never done and defend the range

Hard · estimation · backfills · expand-contract

You are asked to estimate a change you have never attempted: add a column to a 100-million-row table, populate it, move reads across, and drop the old shape. Give a range with the assumptions that generate it, including batch size, the signal your backfill throttles on, and wall-clock hours, and name the three unknowns that would move the number most. Then describe a real estimate you gave under comparable ignorance: how you expressed its uncertainty, what you committed to, and how wrong you turned out to be.

Approach

  1. Decompose into independently deployable steps before estimating anything: add the column nullable, write both shapes, backfill in batches, verify, move reads, stop writing the old shape, drop it. That is four deploys spread over days, and the calendar estimate is dominated by them rather than by the loop's runtime.
  2. Do the arithmetic aloud for the part that has arithmetic in it: batch size times number of batches times per-batch duration, at a write rate the primary can absorb alongside roughly 1.2k writes per second of production traffic. The loop is throttled by replication lag and lock waits, not by how fast it can issue statements.
  3. Price the schema step by its lock rather than its statement duration. In PostgreSQL an ALTER TABLE taking ACCESS EXCLUSIVE waits for every open transaction on that table while later queries queue behind it, so a millisecond change issued during a thirty-second analytics query stalls that table for thirty seconds. Adding a nullable column with a non-volatile default avoids a rewrite from version 11; a new index wants CREATE INDEX CONCURRENTLY, which cannot run inside a transaction block and leaves an invalid index behind if it fails.
  4. Express the answer as a range whose endpoints each trace to a stated assumption, then name the cheapest experiment that collapses it, which is almost always running one real batch against the real table and multiplying.

Follow-up

  • How do you verify the backfill genuinely finished, given rows written by production traffic while it ran?
  • Where does the backfill resume from after a worker is killed mid-batch, and what makes that resume point trustworthy?

Turn a code review disagreement into a decision

Easy · code review · optimistic concurrency · communication

A colleague's change updates a row with UPDATE resource SET version = version + 1 WHERE resource_id = $1 AND version = $2 and treats an affected-row count of zero as a successful no-op. You read that as a silently lost update; they think returning 200 is friendlier to clients than returning a conflict. Describe how you have handled a review disagreement of this shape: what goes in the comment, when you leave the thread, and who decides. Then write the comment you would leave here, in under 80 words.

Approach

  1. Sort the disagreement before writing anything. A silently discarded write is a correctness claim about data; the choice between 409 and 412 is taste. Only the first justifies blocking a merge, and saying which one you are doing is most of the value of the comment.
  2. Make the claim reproducible in the comment itself with an interleaving rather than a principle: A reads version 7, B reads version 7, B commits version 8, A's predicate matches zero rows, A is told it succeeded and A's edit is gone.
  3. Offer the alternative with its cost attached: return 409 carrying the current version and the revision that won, so the client can re-read and re-apply. Note that automatic retry is not the fix, because a retry re-reads the winner's state and reapplies an intent formed against data that no longer exists.
  4. Apply an escalation rule you can state: two round trips on the thread, then a call, and the service's owner decides rather than the reviewer. A reviewer who cannot be overruled is a bottleneck with extra steps.

Follow-up

  • Where would you put the test that fails if someone reintroduces the swallowed zero rowcount?
  • The author says clients cannot handle a 409. How do you check whether that is true?