Web Development Interview Questions for 5 Years Experience
40 questions with answers for candidates with 5 years of experience — 22 medium, 18 hard.
What interviewers expect: 5 years experience
At five years, interviews move towards architecture and leadership. You may design a complete system such as a booking site, discuss scaling with caching and read replicas, zero-downtime deployments and security reviews. Behavioural rounds probe how you handled outages, disagreements and mentoring.
This set is weighted towards CSS Layout and Responsive Design (8), DOM and Browser Events (6), PHP Programming (6), JavaScript Core (5).
System design Scalability Security reviews Team leadership
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
✓ 3 questions free • Unlock the rest in seconds
01
Fetch a list of products and render them safely into the page.
The modal is most likely inside a parent that creates its own stacking context, for example through transform, opacity, filter or a positioned element with a lower z-index. Inside that context the 9999 is compared only with siblings. I would inspect ancestors in DevTools, then either render the modal directly under body, which is what portals do, or remove the property creating the context. The native dialog element with showModal also places it in the top layer.
04
A results website expects ten times normal traffic on announcement day. How do you prepare?
I would load test with tools like k6 or JMeter to find the breaking point, then cache result pages aggressively using a CDN and Redis. The database should have proper indexes, and read-heavy queries could go to replicas. On cloud, I would configure auto-scaling and pre-warm extra instances before the announcement. A lightweight static fallback page and a queue for heavy tasks keep the site responsive under peak load.
05
Validate a signup form with JavaScript before submission.
Check fields on submit and show inline messages:
form.addEventListener('submit', (e) => {
const errors = [];
if (form.name.value.trim().length < 3) errors.push('Name too short');
if (!/^[6-9]\d{9}$/.test(form.mobile.value)) errors.push('Invalid mobile');
if (form.password.value !== form.confirm.value) errors.push('Passwords do not match');
if (errors.length) {
e.preventDefault();
errorBox.textContent = errors.join('. ');
}
});
Client validation improves experience, but the server must validate again.
06
What are container queries and how do they differ from media queries?
Media queries respond to the viewport size, while container queries respond to the size of a parent element. You mark a parent with container-type: inline-size and then write @container (min-width: 400px) rules for its children. This lets a card component change layout depending on whether it sits in a narrow sidebar or a wide main area, making components truly reusable. They are supported in all modern browsers.
07
How do indexes work and when can they hurt performance?
InnoDB indexes are B+ trees that let MySQL find rows without scanning the whole table, which speeds up WHERE, JOIN and ORDER BY on indexed columns. A composite index follows the leftmost prefix rule. Indexes cost extra disk space and slow down INSERT, UPDATE and DELETE because each index must be updated. Low-selectivity columns like gender rarely benefit, and functions on columns in WHERE can stop index use.
08
Users click the Pay button twice and get charged twice. How do you prevent it?
On the front end, I would disable the button and show a loading state as soon as the first click is handled, and re-enable it only on error. But the real fix must be on the server: generate an idempotency key or order id per checkout, and reject or ignore a second request with the same key. Payment gateways support idempotent requests for exactly this reason.
By default PHP coerces scalar types, so passing '5' to a function expecting int silently works. With declare(strict_types=1) at the top of a file, calls made from that file must pass exactly the declared types, or a TypeError is thrown. It catches bugs early, especially in APIs where values arrive as strings. It applies per file, to calls made from the file, not to the whole project.
10
How have you helped a junior developer learn deployment?
Situation: a new joiner was nervous about deploying to production. Task: I was asked to make them independent within a month. Action: I wrote a step-by-step deployment checklist, let them deploy to staging while I watched, then paired on two production releases with them driving. Result: they handled releases alone after three weeks and later improved our checklist with a rollback section.
11
A contact form is receiving hundreds of spam submissions daily. What do you do?
I would layer defences rather than rely on one trick. On the front end, add a hidden honeypot field that bots fill and humans do not, and add an invisible CAPTCHA such as reCAPTCHA v3 or Turnstile. On the server, validate every field, apply rate limiting per IP, reject submissions that arrive too fast after page load, and log patterns. Real protection must be server side because bots skip JavaScript.
clamp(min, preferred, max) scales text smoothly between limits:
h1 {
font-size: clamp(1.75rem, 1rem + 3vw, 3.5rem);
}
p {
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
}
The heading never goes below 28 pixels on phones or above 56 pixels on large screens. Mixing rem with vw in the middle value keeps text zoomable for accessibility, unlike pure vw values.
Show the same message whether or not the email exists to avoid account enumeration. Generate a long random token with random_bytes, store only its hash with a short expiry such as 30 minutes, and email a one-time link. On reset, verify the hash and expiry, set the new password with password_hash, delete the token and invalidate existing sessions. Rate-limit the request endpoint to prevent email flooding.
14
What is the difference between Promises and async/await?
A Promise represents a value that will be available later, handled with then and catch chains. async/await is syntax built on promises: an async function always returns a promise, and await pauses that function until the promise settles, making code read top to bottom. Errors are handled with try and catch. For parallel work you still use Promise.all, because awaiting calls one by one runs them sequentially.
When an event fires, it travels in three phases: capturing from window down to the target, the target phase, and bubbling back up through ancestors. By default, addEventListener handles the bubbling phase, so a click on a button also triggers click handlers on its parent div and body. Passing { capture: true } runs the handler during the capturing phase. Some events like focus do not bubble, but focusin does.
Flexbox is one-dimensional: it arranges items in a row or a column and is great for navbars, button groups and centering. Grid is two-dimensional: it controls rows and columns together, which suits page layouts, dashboards and card galleries. A common pattern is Grid for the overall page structure and Flexbox inside each component. Both are fully supported in all modern browsers.
17
What are data-* attributes and when do you use them?
Custom data attributes such as data-product-id or data-price let you store extra information on an element without misusing class or id. JavaScript reads them through element.dataset, for example button.dataset.productId, and CSS can target them with attribute selectors. They are ideal for passing small values from server-rendered HTML to scripts, but sensitive data should never be stored there because anyone can read the page source.
18
A client wants to move a growing site from shared hosting to a VPS. How do you plan the migration?
I would set up the VPS with Nginx, PHP-FPM, MySQL, firewall and SSL, matching PHP versions and extensions. Then I would copy files with rsync, export and import the database, and test the site using a hosts file entry before touching DNS. After lowering the TTL, I would do a final data sync during low traffic, switch DNS, monitor logs, and keep the old hosting live for a few days as fallback.
19
Before launching a new web application, how would you run a basic security review?
I would walk through the OWASP Top 10: access control on every route, injection, authentication, session settings, misconfiguration and outdated components. I would run automated scanners like OWASP ZAP, check dependencies with composer audit and npm audit, verify HTTPS and security headers, and confirm error pages do not leak stack traces. Finally, I would test key flows manually as a normal user trying to access admin pages.
An iterator is an object with a next() method returning { value, done }, and any object with a Symbol.iterator method can be used in for...of loops and spread syntax. A generator function, written function*, creates iterators easily: each yield pauses execution and returns a value, resuming on the next call. Generators are useful for lazy sequences, such as paginated API results, without building huge arrays in memory.
MVC separates an application into Models for data and business rules, Views for presentation, and Controllers that handle requests and connect the two. In Laravel, routes send a request to a controller, the controller uses Eloquent models to query the database, and returns a Blade view or JSON. This separation makes code testable and lets frontend and backend developers work on different layers without conflicts.
22
Tell me about a time you had to work with messy legacy code.
Situation: I joined a project with a 10-year-old PHP codebase full of mixed HTML and SQL. Task: I needed to add online payments without breaking existing features. Action: I wrote tests around the checkout flow first, extracted database code into a small class using PDO, and added the payment module separately. Result: payments launched with no regressions, and the team adopted the same approach for later changes.
23
Users get logged out immediately after login when redirected. What could be wrong?
Likely causes are session_start() missing on the next page, output sent before session_start so headers fail, or the session cookie being dropped. Cookie issues include a Secure flag on an HTTP site, a domain mismatch between www and non-www, or SameSite settings during cross-site redirects. I would check the Set-Cookie header in DevTools, verify session.save_path is writable, and redirect to a single canonical domain.
24
Implement infinite scroll using IntersectionObserver.
Observe a sentinel element at the bottom of the list:
const sentinel = document.querySelector('#sentinel');
let page = 1, loading = false;
const observer = new IntersectionObserver(async ([entry]) => {
if (!entry.isIntersecting || loading) return;
loading = true;
const res = await fetch('/api/posts?page=' + (++page));
const posts = await res.json();
if (!posts.length) observer.disconnect();
posts.forEach(renderPost);
loading = false;
}, { rootMargin: '200px' });
observer.observe(sentinel);
It is far cheaper than listening to every scroll event.
25
How do you test a website for cross-browser compatibility?
I check feature support on Can I Use before using new CSS, and use Autoprefixer through the build for vendor prefixes. I test on Chrome, Firefox, Safari and Edge, plus real Android and iPhone devices because Safari on iOS behaves differently. Cloud testing platforms help when devices are missing. I use @supports for progressive enhancement, so older browsers still get a working, simpler layout.
Define colour tokens and override them for dark preference:
:root {
--bg: #ffffff;
--text: #1f2937;
}
@media (prefers-color-scheme: dark) {
:root { --bg: #111827; --text: #f3f4f6; }
}
[data-theme='dark'] { --bg: #111827; --text: #f3f4f6; }
body { background: var(--bg); color: var(--text); }
The media query follows the system setting, and the data-theme attribute lets a toggle button override it with JavaScript.
27
Show HTML5 built-in validation for a registration form.
HTML5 attributes validate input before any JavaScript runs:
<input type='text' name='name' required minlength='3' maxlength='50'>
<input type='email' name='email' required>
<input type='tel' name='mobile' pattern='[6-9][0-9]{9}' title='10 digit mobile number'>
<input type='number' name='age' min='18' max='60'>
<input type='url' name='portfolio'>
The browser blocks submission and shows a message when rules fail. Server-side validation is still mandatory because client checks can be bypassed.
28
A dashboard page becomes slower the longer it stays open. What could cause it?
This points to a memory leak. Typical causes are setInterval timers never cleared, event listeners added repeatedly on re-render, detached DOM nodes still referenced in arrays, and growing caches. I would take heap snapshots in the DevTools Memory tab a few minutes apart and compare retained objects. Fixes include clearInterval on cleanup, removeEventListener or AbortController for listeners, and limiting cached data size.
29
How do srcset and sizes help with responsive images?
srcset lists several versions of the same image with their widths, for example hero-480.jpg 480w and hero-1200.jpg 1200w. sizes tells the browser how wide the image will be shown at different viewport widths. The browser then picks the smallest file that still looks sharp for that screen and pixel density. This saves a lot of mobile data compared to sending one large image to every device.
30
A PHP page takes 8 seconds to load. How do you find the bottleneck?
I would measure before guessing, using a profiler like Xdebug or Blackfire, or simple microtime logs around sections. Usually the cause is database queries, such as an N+1 pattern running a query inside a loop, missing indexes, or slow external API calls. Fixes include joining or eager loading, adding indexes, caching results in Redis, making API calls async with a queue, and enabling OPcache.
31
What is CORS and why does the browser block some API calls?
The same-origin policy stops scripts on one origin from reading responses from another origin. CORS is how a server opts in: it returns headers like Access-Control-Allow-Origin naming allowed origins. For non-simple requests, such as JSON with custom headers, the browser first sends an OPTIONS preflight. CORS is enforced only by browsers, so it is not a security control for the server itself, and Allow-Origin * should never be combined with credentials.
32
What are CSS custom properties and how are they different from Sass variables?
Custom properties like --brand: #2563eb are defined in CSS and read with var(--brand). They cascade, can be changed per element or media query, and can be updated at runtime with JavaScript, which makes themes and dark mode easy. Sass variables are replaced at compile time and do not exist in the browser. Many teams use both: Sass for build-time logic and custom properties for runtime theming.
READ UNCOMMITTED allows dirty reads of uncommitted data. READ COMMITTED sees only committed data but the same query can return different results within a transaction. REPEATABLE READ, the InnoDB default, gives a consistent snapshot for the whole transaction and uses gap locks to prevent most phantom rows. SERIALIZABLE is the strictest and slowest. Higher isolation means fewer anomalies but more locking and contention.
34
What is a stacking context and why does z-index sometimes not work?
A stacking context is an independent layer group in which z-index values are compared. It is created by the root, positioned elements with a z-index, and properties like opacity below 1, transform, filter and isolation: isolate. A child can never appear above something outside its parent's stacking context, no matter how high its z-index is. Fixing it usually means moving the element or removing the property that created the context.
localStorage stores about 5 MB per origin and persists until cleared. sessionStorage has similar size but is cleared when the tab closes and is separate per tab. Cookies are small, around 4 KB, are sent to the server with every request, and can be HttpOnly, Secure and SameSite. Use cookies for session identifiers, and storage for UI preferences. Never store tokens or passwords in localStorage, since any XSS can read it.
36
Write .htaccess rules to force HTTPS and the non-www domain.
On Apache with mod_rewrite enabled:
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
A 301 redirect tells search engines the change is permanent, so ranking signals move to one canonical URL. Test carefully, because a wrong rule can cause a redirect loop behind a proxy or load balancer.
A transaction groups several SQL statements so they succeed or fail together. ACID means Atomicity, all or nothing; Consistency, rules and constraints stay valid; Isolation, concurrent transactions do not see each other's partial changes; and Durability, committed data survives crashes. For example, transferring wallet balance debits one row and credits another inside START TRANSACTION and COMMIT, with ROLLBACK if anything fails.
38
The server's disk is full and the site has stopped saving data. What do you do?
I would check usage with df -h and find large folders using du -sh /var/* | sort -h. Usual culprits are huge log files, old backups, cached uploads or MySQL binary logs. I would safely clear or compress logs, purge old binlogs with PURGE BINARY LOGS, and move backups off the server. To prevent repeats, set up logrotate, backup retention and disk usage alerts at about 80 percent.
Check errors, size and real MIME type, then store with a random name:
if ($_FILES['photo']['error'] === UPLOAD_ERR_OK && $_FILES['photo']['size'] < 2_000_000) {
$mime = (new finfo(FILEINFO_MIME_TYPE))->file($_FILES['photo']['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp'];
if (isset($allowed[$mime])) {
$name = bin2hex(random_bytes(16)) . '.' . $allowed[$mime];
move_uploaded_file($_FILES['photo']['tmp_name'], __DIR__ . '/uploads/' . $name);
}
}
Never trust the original filename or extension.
40
Fetch data from an API with async/await and handle errors properly.
fetch only rejects on network failure, so check response.ok yourself:
async function getCourses() {
try {
const res = await fetch('/api/courses');
if (!res.ok) throw new Error('HTTP ' + res.status);
const data = await res.json();
renderCourses(data);
} catch (err) {
console.error(err);
showMessage('Could not load courses. Please retry.');
}
}
A 404 or 500 response would otherwise be treated as success.
Frequently asked questions
What is asked in Web Development interviews for 5 years of experience?
At five years, interviews move towards architecture and leadership. You may design a complete system such as a booking site, discuss scaling with caching and read replicas, zero-downtime deployments and security reviews. Behavioural rounds probe how you handled outages, disagreements and mentoring.
How many questions should I practise before a Web Development interview?
Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.
Which topics matter most for 5 years of experience?
Focus first on System design, Scalability, Security reviews, Team leadership.