Interview guides · Anxion Webtech Private
Anxion Webtech Private Software Engineer Interview Guide
Anxion Webtech Private Software Engineer interview guide: reported PHP, SQL and HTML/CSS questions, the hands-on task, and a 7-day prep plan.
The 60-second brief
Candidates describe Anxion Webtech Private as a company that runs web-based services. The Software Engineer role builds and maintains the code behind those services: turning business requirements into working features, with work that mixes front-end pages and back-end database handling. Candidates list PHP or Python, SQL and HTML/CSS as the skills the role asks for, with Git, basic web security and familiarity with web frameworks as extras.
This guide is for a candidate applying as a Software Engineer at Anxion Webtech Private. It covers the two reported rounds, the nine reported questions on PHP sessions, SQL, client versus server scripting, database connectivity, HTML/CSS and form validation, and a week of preparation built around the hands-on task. It does not cover other roles or levels.
Candidates report two rounds, an Initial Assessment followed by a Hands-on Technical Task, spread over roughly 2-4 weeks.
Focus areas
- PHP
- SQL
- Python
Overview
Candidates describe the Software Engineer job as day-to-day web development: writing and testing code, deploying it, troubleshooting features that already exist, and building new ones from internal requirements. They also mention code reviews and talking to product colleagues to understand what users need. For interview purposes this means the reported questions are practical rather than abstract: how a PHP page starts a session, how a SQL query behaves under a web application, why a database connection fails, how a page rotates and resizes in a browser.
The stack candidates report is PHP or Python on the server, SQL for storage, and HTML/CSS in the browser. Git, basic web security and familiarity with web frameworks are listed as extras. Pick the server language you use daily and prepare every answer in it, because the hands-on stage asks you to produce working code, not to describe it. Have a local environment ready that can serve a page, talk to a SQL database and run your language's debugger or error output.
The nine reported questions fall into two groups. The first is server-side fundamentals: starting a session in PHP, how PHP interacts with the server, writing and optimizing SQL for a web application, client-side versus server-side scripting, and debugging a connection between an application and its database. The second is front-end and project work: the rotate tool in HTML/CSS, responsive components across browsers, a recent UI feature you built, and client-side versus server-side form validation. That last one is filed in the behavioral section, but its subject is technical, so answer it with a rule-by-rule explanation and then a project example.
Candidates also describe two scenario prompts to rehearse: securing a user login session, and a time you optimized a slow database query. Both connect to the SQL and session questions, so one set of well-practised answers covers several prompts. Because candidates describe the loop as centred on fundamentals, precision on small things (what a function does, where a check belongs, what an error message says) is where preparation pays off.
The Anxion Webtech Private interview process
Candidates describe two stages: an Initial Assessment that leans on spoken technical questions, then a Hands-on Technical Task where you write code. The order below is as reported.
Initial Assessment
Candidates report that the process opens with an initial assessment of foundational knowledge and skills, described as a conversational technical screening that moves from verbal questions toward a task-based stage. The kind of material this stage covers is the technical-fundamentals group: PHP or Python basics, session handling, how server-side code reaches a database, SQL for web applications, and what runs in the browser versus on the server. Approach it as a precise conversation. Give the direct answer first, then the reason you would choose that method, then one edge case. For the PHP session question, that means naming session_start(), saying it must run before any output is sent, explaining that it issues a session identifier cookie and loads the stored session data, and adding that you regenerate the identifier after login.
What to demonstrate
- Foundational knowledge of your main server language (PHP or Python) and its standard functions
- Understanding of session management and how application code connects to a SQL database
- Ability to explain why you chose a method, not only which one
- Clear, step-by-step spoken reasoning, including when you do not know an answer
How to prepare
- Answer aloud: which function starts a PHP session, what it does with the cookie and session storage, and why it must come before output
- Explain the request path from browser to web server to PHP or Python to database and back, naming what happens at each hop
- Write a short SQL optimization answer: select only needed columns, filter on indexed columns, check the plan with EXPLAIN, bind parameters, avoid one query per row
- Prepare a debugging order for a failing database connection: error text, host and port reachability, credentials, database name, privileges, connection limits
Hands-on Technical Task
Candidates report a hands-on evaluation of practical coding ability, where the task has specific requirements and you are observed while you work. The areas candidates list for it are completing the task as specified, keeping the code readable and organized, and finding out why code is not behaving as expected. Candidates describe user logins, database CRUD operations and basic UI manipulation as typical practice material for a practical test. Read the requirements fully before typing, build the smallest working version first, then tidy it, and say what you are doing as you go so a wrong turn is visible and recoverable.
What to demonstrate
- Following the written requirements of the task exactly
- Readable, maintainable code organization rather than a single tangled script
- Debugging: locating why a piece of code misbehaves
- Speed and accuracy while being observed
How to prepare
- Build a small app from scratch in your chosen language: registration, login with a session, and create/read/update/delete on one table, using parameterized SQL
- Practise a small UI task in HTML/CSS and a little JavaScript, such as a rotating element, a responsive layout and a form with validation
- Rehearse debugging out loud: reproduce, read the error, narrow the cause, fix, re-test
- Make small Git commits while you build so your work is organized and you can step back from a mistake
Where candidates lose points
Answering the PHP session question with only the function name
Say that session_start() must run before any output, that it sets the session identifier cookie and loads stored data, and that you call session_regenerate_id(true) on login and destroy the session on logout. The same answer then covers the securing-a-login-session scenario.
Calling SQL optimization 'add an index' without showing how you know
Describe the sequence: run EXPLAIN, check that the filter column is indexed and the predicate can use it, select only needed columns, replace per-row queries with a join, and add LIMIT for list pages. Practise one slow-query story with the before and after plan.
Trusting client-side checks, or building SQL by joining strings, in the hands-on task
Validate on the server every time, bind parameters in every query, escape output, and store passwords with a hashing function such as password_hash. Do this by habit in your practice builds so it shows up under time pressure.
Guessing at fixes for a database connection failure
Work through a fixed order and state it: read the exact error, confirm the host and port are reachable, check credentials and database name, check the account's privileges, then connection limits and the server's state. Say what each step rules out.
Coding silently, or starting before reading the task requirements fully
Restate the requirements, build the smallest working version, then tidy it, naming what you are doing as you go. Practise with your own timer so that checking requirements is part of your routine, not an afterthought.
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
- 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.
- 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.
- 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.
- 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?
Diff a projection against the primary without per-row point reads
Hard · reconciliation · range hashing · throttling
The listing projection has drifted and some rows show a stale version. The primary holds 40,000,000 resource rows across 12,000 tenants while serving 1,200 writes and 14,000 reads per second. The obvious repair, reading each resource row and comparing its version against the projection, is correct and would eventually finish. Explain precisely why it is unacceptable here, then give a diff that finds the differing rows, state its complexity, and make it safe to run against a live primary. Replication lag is usually under 100 ms and is not bounded.
Approach
- Quantify the naive cost rather than calling it slow: 40,000,000 point reads at even 0.5 ms each is over five hours serialised, and the only lever is concurrency, which is exactly what you cannot spend. The primary's pool is sized for the write path, and 40,000,000 random reads evict the buffer cache that sustains the 85 percent cache hit rate, so the audit degrades the system it is auditing.
- Replace random access with one ordered pass per side. Both sides can be read in (tenant_id, resource_id) order, which is a sequential scan on each and a merge join in O(n) time and O(1) memory. For a dense diff that is the whole answer, and it reads the primary once instead of 40,000,000 times.
- For the expected sparse case, compare range hashes instead of rows: partition the key space, compute per range an order-independent aggregate over hash(resource_id, version), compare aggregates, and descend only into ranges that differ. With d differing rows and branching factor B, at most d ranges mismatch per level, so the drill-down examines O(d log_B(n/d)) ranges and reads full rows only in mismatching leaves.
- Aggregate with a sum modulo 2^64 or a multiset hash, never XOR. XOR is order-independent but self-cancelling, so two rows wrong in the same way, or a row duplicated on one side, leave the range aggregate matching and the range is declared clean.
Follow-up
- The diff reports 900 stale rows. How do you decide between patching those rows and rebuilding the projection from resource_revision?
- Same job, but the projection lives in a search index that cannot be scanned in key order. What changes?
Track a rolling failure rate per destination for circuit decisions
Easy · sliding window · ring buffer · circuit breaker
The egress service delivers about 1,500 webhooks per second across roughly 40,000 destinations, each call bounded by a 10 second timeout. Maintain, per destination, the failure rate over the trailing 60 seconds so a caller can ask before dispatch whether the circuit should open. Attempts arrive as (destination_id, finished_at_ms, outcome). Requirement: amortised O(1) per attempt, with total memory bounded by the destination count rather than by traffic. Give the structure, its exact memory, and the rule that stops a destination with three attempts from opening a circuit.
Approach
- Name the exact-deque version and then reject it as the default. Holding timestamps and advancing a tail pointer past anything older than now minus 60 seconds is a correct two-pointer window at amortised O(1) per attempt, but its memory tracks in-window traffic, so one destination in a retry storm holds hundreds of thousands of entries while thousands of quiet destinations hold none.
- Use a ring of 60 one-second buckets per destination, each bucket a pair of counters for attempts and failures. On an attempt, advance the ring by the elapsed whole seconds, zeroing at most min(elapsed, 60) buckets, then increment the head. That is amortised O(1) with a fixed footprint per destination.
- State the footprint: 60 buckets times two 4-byte counters is 480 bytes of payload per destination, so 40,000 destinations is roughly 20 to 25 MB with per-entry overhead, bounded by the catalogue rather than by the rate. The cost is granularity, since the oldest bucket ages out in whole seconds, which is far tighter than the decision needs.
- Require a minimum sample before the circuit may open. A destination with three attempts and three failures reads as 100 percent and is not evidence; a floor of roughly 20 attempts in the window makes the ratio meaningful, and below that floor use a run of consecutive failures as the trigger instead.
Follow-up
- The fleet is 30 instances and each sees roughly a thirtieth of a destination's traffic. Where does the rate actually live, and what does a per-instance answer get wrong?
- A destination answers in 9.5 seconds and succeeds. It is not failing but it is consuming your per-destination concurrency. What signal should open the circuit here?
How do you approach writing and optimizing SQL queries for web applications?
Medium · Technical Fundamentals
Approach
- Start with correctness and safety: select only the columns the page needs instead of SELECT *, and pass every user value through a prepared statement with bound parameters (PDO or mysqli), never string concatenation, which prevents SQL injection and lets the database reuse the plan.
- Index for the access pattern: put indexes on the columns used in WHERE, JOIN and ORDER BY. For a composite index such as (user_id, created_at), a B-tree serves filters on its leftmost prefix, so a query filtering only on created_at cannot seek on it.
- Read the plan with EXPLAIN before and after a change. In MySQL, type = ALL means a full table scan, and the rows estimate and the key column show whether your index is used. Rewrite predicates that block index use: WHERE YEAR(created_at) = 2025 becomes a range on created_at, and LIKE '%term' with a leading wildcard cannot use a B-tree index.
- Fix the web-app-specific patterns: the N+1 problem (one query per item in a loop) becomes a single JOIN or an IN (...) batch, and deep OFFSET pagination, which still reads and discards every skipped row, becomes keyset pagination (WHERE id > :last_id ORDER BY id LIMIT 20).
- Name the trade-off: every index speeds reads but adds work and storage on each INSERT and UPDATE, and an index on a low-selectivity column such as a boolean flag often goes unused, so index for measured slow queries rather than every column.
Follow-up
- A product listing page runs one query per product to fetch its category name. How do you rewrite it, and how do you confirm the fix?
- Why does page 5,000 of a result set load far slower than page 1 with LIMIT/OFFSET, and what do you replace it with?
- When would adding an index make the application slower overall?
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
- 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.
- 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.
- 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.
- 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?
Which method is used to start a session in PHP?
Medium · Technical Fundamentals
Approach
- Name the function: session_start(). It either creates a new session or resumes the one whose ID arrives in the session cookie (PHPSESSID by default), then fills the $_SESSION superglobal so values set on one request are readable on the next.
- Say when it must run: before any output reaches the browser, because starting a new session sends a Set-Cookie header. Stray whitespace before <?php or an early echo triggers a 'headers already sent' warning unless output buffering is on. session_status() === PHP_SESSION_ACTIVE tells you whether one is already open.
- Explain where the data lives: the default handler writes a file per session under session.save_path, and the browser only holds the ID. A custom handler (database, Redis) is registered with session_set_save_handler() before session_start().
- Cover security: call session_regenerate_id(true) right after login to block session fixation, set session.cookie_httponly, session.cookie_secure and SameSite (cookie options accepted since PHP 7.3), and enable session.use_strict_mode so unknown IDs are rejected.
- Close with logout and concurrency: clear $_SESSION, expire the cookie and call session_destroy(); and note the file handler holds an exclusive lock for the whole request, so call session_write_close() once you are done writing if parallel AJAX requests from one user stall.
Follow-up
- Why does a page with several simultaneous AJAX calls become slow once each script calls session_start(), and how do you fix it?
- How would you store sessions so that two web servers behind a load balancer share them?
- What exactly happens if session_start() is called after the page has already printed HTML?
Can you explain the basics of PHP and how it interacts with the server?
Medium · Technical Fundamentals
Approach
- Define PHP as a server-side scripting language: the code runs on the server and only its output (usually HTML or JSON) is sent to the browser, which never sees the PHP source.
- Walk the request path: the browser sends an HTTP request, the web server hands .php requests to the interpreter (mod_php inside Apache, or Nginx passing them to PHP-FPM over FastCGI), PHP compiles the script to opcodes (cached by OPcache so later requests skip compilation), executes it, and the web server returns the output as the response.
- Name the request inputs and outputs: $_GET, $_POST, $_COOKIE, $_SERVER and $_FILES carry the request; echo and output buffering build the body; header() and setcookie() set headers and must run before any body is sent.
- Explain the shared-nothing model: each request starts with fresh memory and everything is torn down at the end, so state that must survive goes into sessions, a database or a cache. That is why PHP scales by adding workers, and why per-request work like opening DB connections matters.
- Tie it to the database layer: a script typically connects with PDO or mysqli, runs prepared statements with bound parameters, and renders the result, which leads naturally into the SQL and connectivity questions.
Follow-up
- What is PHP-FPM, and why would you run it behind Nginx instead of using mod_php?
- What does OPcache store, and what would you check if a deploy changed the code but the old behaviour persists?
- Since memory is reset every request, how would you avoid opening a new database connection on every page load, and what is the risk of persistent connections?
What is the difference between client-side and server-side scripting?
Medium · Technical Fundamentals
Approach
- Draw the line by where the code executes: server-side code (PHP, Python) runs on the server while the request is being answered and produces the response; client-side code (JavaScript in the browser) runs after the response arrives and manipulates the page the user already has.
- Contrast what each can reach: server code can talk to the database, read secrets and the file system; client code can touch the DOM, local storage and user events but nothing on the server directly. A JavaScript function cannot call a PHP function; it has to send an HTTP request (fetch or XMLHttpRequest) to a PHP endpoint that returns JSON or HTML.
- State the trust rule: client-side code and its inputs are fully visible to and editable by the user, so authentication, authorisation, pricing and validation must be enforced on the server; client checks are for responsiveness only.
- Give the performance trade-off: server rendering delivers complete HTML on the first response, which helps first paint and search indexing; client-side updates avoid full page reloads for interactions but cost JavaScript download and execution on the device.
- Show a concrete example: a PHP page echoes a list of orders from SQL, and JavaScript adds a filter box that hides rows instantly without another request, or calls an endpoint when the data set is too large to ship.
Follow-up
- A button on the page needs to save something to the database. Walk through every hop from the click to the row being written.
- If both sides can validate a form, why is the server-side check the one you cannot drop?
- What changes in this split when a page is rendered entirely by a JavaScript framework that fetches JSON from the server?
What is the rotate tool in HTML/CSS and how is it implemented?
Medium · Frontend and Practical Application
Approach
- Clarify the term first: HTML has no rotate tool; rotation is done in CSS with the transform property, for example transform: rotate(45deg). Angles accept deg, rad, grad and turn units, and a positive angle rotates clockwise. Modern browsers also support the standalone rotate property (rotate: 45deg).
- Explain the pivot: rotation happens around transform-origin, which defaults to the centre of the element (50% 50%). transform-origin: top left (or 0 0) pivots around the corner instead.
- State the layout behaviour: a transform does not change layout, so the element keeps its original box in the flow, neighbours do not move, and the rotated element can overlap them. A transform also creates a new stacking context and becomes the containing block for position: fixed descendants.
- Cover the gotchas and animation: transforms do not apply to non-replaced inline elements, so a span needs display: inline-block. Animate with transition: transform or @keyframes, which browsers can run on the compositor without reflow, unlike animating width or top.
- Show the scripted and 3D variants: element.style.transform =
rotate(${deg}deg)in JavaScript, rotateX/rotateY with perspective for 3D (rotate() is equivalent to rotateZ()), and on a canvas ctx.rotate(radians) turns around the current origin, so translate to the shape's centre first.
Follow-up
- How would you make an icon spin continuously, and how would you respect users who set prefers-reduced-motion?
- You rotate a card by 90 degrees and it overlaps the text below instead of pushing it down. Why, and how do you fix the layout?
- How do you combine a rotation with a translation, and why does the order of functions in transform change the result?
How do you ensure your web components are responsive across different browsers?
Medium · Frontend and Practical Application
Approach
- Start with the foundation: the viewport meta tag (<meta name="viewport" content="width=device-width, initial-scale=1">), without which mobile browsers render a desktop-width page and scale it down, then mobile-first CSS that adds layout with min-width media queries.
- Use layout tools that adapt by themselves: Flexbox and Grid (for example repeat(auto-fit, minmax(200px, 1fr))), relative units (rem, %, vw), max-width: 100% on images, and srcset/sizes so small screens download smaller images. Pick breakpoints where the content breaks, not by device model; container queries handle components that sit in columns of different widths.
- Handle browser differences deliberately: check support for a feature on caniuse before relying on it, use @supports feature queries for progressive enhancement, let Autoprefixer add vendor prefixes from a browserslist target, and normalise default styles so browsers start from the same baseline.
- Know the common traps: 100vh on mobile Safari includes the area behind the address bar (dvh/svh units address this), hover-only interactions do not exist on touch screens, and form controls render differently per browser.
- Test, do not assume: browser devtools device mode for quick checks, then real devices or a cloud device service for Safari on iOS and Android Chrome, and fix layout bugs by reducing them to the smallest failing example.
Follow-up
- A layout looks right in Chrome but breaks only in Safari. How do you find the cause?
- How do you choose breakpoints, and when would a container query be a better fit than a media query?
- How would you make a wide data table usable on a phone screen?
Can you walk us through a recent project where you had to implement a specific UI feature?
Medium · Frontend and Practical Application
Approach
- Pick one feature you built end to end, with a stack you can defend in detail (for example a PHP or Python backend with SQL and an HTML/CSS/JavaScript front end), and open with the requirement and the user problem in two sentences.
- Spend most of the answer on decisions: how you structured the component and its state, what the server endpoint returned and why, how the SQL query behind it was written and indexed, and one alternative you rejected with the reason.
- Show the states beyond the happy path: loading, empty, error and slow-network behaviour, validation on both client and server, and accessibility (labels, keyboard navigation, focus handling, ARIA only where native elements fall short).
- Explain how you verified it: browsers and screen sizes tested, how you debugged the hardest bug, and what you measured or observed after release.
- Avoid the failure that sinks this answer: listing technologies or describing a team's work in the plural without saying which parts you personally wrote and decided.
Follow-up
- If you rebuilt that feature today, what would you change and why?
- What was the hardest bug in that feature, and how did you track it down?
- How did the feature behave for a keyboard-only or screen-reader user?
How would you debug a common connectivity issue between your application and a database?
Medium · Technical Fundamentals
Approach
- Read the exact error first, because it splits the search space: 'Connection refused' or MySQL error 2002 points at the server process, host, port or socket; a timeout points at the network or firewall; 1045 'Access denied' means the server was reached and the credentials or grants are wrong; 1040 'Too many connections' and 2006 'MySQL server has gone away' point at limits and idle timeouts.
- Work the layers in order, from the same host or container as the application: is the database running, does the hostname resolve, is the port reachable (for example nc -vz db-host 3306), and is a firewall or security group in the way. Then check the server's bind-address, since a database bound to 127.0.0.1 refuses remote clients.
- Check identity and configuration: the credentials and database name the app actually loads (print which config or environment file is read, never the password), and the user's host-based grant, since 'app'@'localhost' and 'app'@'%' are different accounts in MySQL.
- Know the localhost trap: in PHP's mysqli and PDO MySQL drivers, 'localhost' means a Unix socket while '127.0.0.1' means TCP, and inside a Docker container localhost is the container itself, so the host must be the database service name.
- For intermittent failures, look at connection counts against max_connections, connections left open by code that never closes them, and idle connections dropped after wait_timeout; then confirm the fix by reproducing the original error and showing it gone.
Follow-up
- The app connects fine on your laptop but fails after being deployed in a container. What do you check first?
- Connections fail only during traffic peaks. How do you tell a connection leak from a limit that is simply too low?
- How do you make the application report a database outage clearly to users without exposing hostnames or credentials?
Suggested practice checklist
The plan is built around the two reported rounds: Days 1 to 5 cover the spoken fundamentals (sessions, SQL, database debugging, HTML/CSS, form validation), Day 6 is a full hands-on build, and Day 7 rehearses project stories and a spoken run-through.
Day 1 — Map the two rounds and set up your environment
- List the nine reported questions and sort them into server-side fundamentals and front-end/project groups
- Choose PHP or Python as your single answer language for the whole week
- Set up a local environment that serves a page, connects to a SQL database and shows errors clearly
Deliverable: A one-page map of the two rounds with your question groups, and a working hello-world page that reads one row from a database table.
Day 2 — Sessions, server interaction and client versus server scripting
- Write a login and logout flow using sessions: start the session, store the user id, regenerate the identifier on login, destroy it on logout
- Draw the request path from browser to web server to your language runtime to database and back, and label what runs where
- Write a short contrast of client-side and server-side scripting with an example of work that must stay on the server
Deliverable: A working login/logout script, a labelled request-path diagram, and a written contrast of client-side and server-side scripting you can say aloud.
Day 3 — SQL for web applications
- Create a small users and orders schema and write list, detail and search queries with bound parameters
- Run EXPLAIN on a slow query, add an index, run it again and record what changed
- Work through the join fan-out practice question and explain why a one-to-many join inflates totals
Deliverable: A schema, six parameterized queries, and a before-and-after EXPLAIN note for one optimized query.
Day 4 — Debugging a database connection
- Break your own connection in turn: wrong host, wrong port, wrong password, missing database, missing privilege, stopped server
- Record the exact error text each failure produces in your language's driver
- Turn the results into a diagnosis order and say it aloud as a spoken answer
Deliverable: A table of six connection failures with their error messages and a spoken diagnosis sequence.
Day 5 — HTML/CSS: rotate, responsive layout and form validation
- Rotate an element with the CSS transform property, set the transform origin, and animate it with a transition
- Build a card layout that adapts with flexbox or grid and media queries, then check it in at least two browsers
- Build a sign-up form with client-side validation and matching server-side validation, and test it with JavaScript turned off
Deliverable: One page containing a rotating element, a responsive layout and a form validated on both layers, with a note on what each layer catches.
Day 6 — Hands-on build under your own timer
- Write a requirement sheet for a small app: registration, login, and CRUD on one table, with a form and a list page
- Build it from scratch, committing to Git in small steps, and speak your reasoning as you work
- Debug one deliberately introduced fault and describe how you located it
Deliverable: A working repository with small commits, parameterized queries, server-side validation and a short list of what you would tidy next.
Day 7 — Project stories and a spoken run-through
- Prepare a walkthrough of one UI feature you built: the requirement, your choices, the bug and the result
- Prepare answers for 'Explain how you would secure a user login session' and 'Describe a time you had to optimize a slow database query'
- Practise the estimation and reversed-decision stories from the behavioral practice set, then run through the reported fundamentals questions aloud
Deliverable: Two written project stories, two scenario answers, and one recorded spoken run-through of the reported questions.
Project stories and form-validation reasoning
Candidates describe a collaborative setting where you explain your own projects and the reasons behind your technical choices, and where saying how you would find an answer you do not know is acceptable. For a web developer this means being ready to walk through a feature you built, a query you tuned, and a decision about where a rule belongs.
- Explain how you would secure a user login session.
- Describe a time you had to optimize a slow database query.
- Walk through a recent project in which you built a specific UI feature: the requirement, your options, what you chose, and what you would change.
- Tell me about a question or bug you could not solve right away, and describe how you went about finding the answer.
How do you handle form validation on the client side versus the server side?
Medium · Frontend and Practical Application
Approach
- Answer as two layers with different jobs. Client-side validation gives fast feedback: required, type=email, minlength and pattern attributes, the Constraint Validation API (checkValidity, setCustomValidity) or a small script showing inline messages. Server-side validation is the enforcement point, because a browser check can be bypassed with disabled JavaScript, developer tools or a direct HTTP request.
- Make the server layer concrete. In PHP: check presence, type, length, format and allowed values (filter_var with FILTER_VALIDATE_EMAIL, in_array), trim and normalize, bind parameters in SQL, and htmlspecialchars on output. In Python, use the framework's form or schema validation. On failure, re-render the form with errors and earlier input, or return a 422 with one error per field.
- Cover what only the server can check: uniqueness (an email already registered), authorization, values derived from other records such as a price, real file type and size, and a CSRF token on state-changing forms. For uniqueness, add a unique index as well as a pre-check, since two simultaneous sign-ups can both pass the pre-check.
- Keep the layers in step: reuse one rule list or schema where the stack allows, and treat the server rules as the source of truth. Avoid over-strict client patterns, such as a complicated email regex that rejects valid addresses.
- Finish with a real example from your own work: a form you built, which rule lived where, and a bug or bypass that taught you why the server check mattered. Pick a case where you made the decision yourself and say what changed afterwards.
Follow-up
- A user has JavaScript disabled or posts directly with curl. What happens to your form?
- How do you stop the client rules and server rules from drifting apart as the form changes?
- Where do you check that an email is unused, and how do you handle two simultaneous sign-ups for the same address?
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
- 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.
- 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.
- 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.
- 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?
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
- 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.
- 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.
- 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.
- 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?