Posts

Web Deployment Checklist: Release and Roll Back a Course App

Practical Web Development — Lesson 10 of 10 · Senior A successful deployment command does not prove a course application works. A release is complete when important user journeys work in the target environment and the team can recognize and recover from a bad change. This lesson turns a small course app into a practical release exercise with explicit acceptance and rollback criteria. Describe the change and its risk Suppose a release adds saved course shortlists. Separate the new browser interface, the server endpoint and any database change. Identify what happens when old browser code talks to the new server, and when the previous server version reads newly written data. Rolling back code is not the same as reversing a data migration. Write a short release record before deploying. Use real commit and build identifiers in your own project; the values below are placeholders for the exercise. Change: Add saved course shortlists Build: <immutable build identifier> Owner: <person ...

Web Deployment Checklist: Release and Roll Back a Course App

Practical Web Development — Lesson 10 of 10 · Senior A successful deployment command does not prove a course application works. A release is complete when important user journeys work in the target environment and the team can recognize and recover from a bad change. This lesson turns a small course app into a practical release exercise with explicit acceptance and rollback criteria. Describe the change and its risk Suppose a release adds saved course shortlists. Separate the new browser interface, the server endpoint and any database change. Identify what happens when old browser code talks to the new server, and when the previous server version reads newly written data. Rolling back code is not the same as reversing a data migration. Write a short release record before deploying. Use real commit and build identifiers in your own project; the values below are placeholders for the exercise. Change: Add saved course shortlists Build: <immutable build identifier> Owner: <person ...

Structured Logging in Node.js: Trace a Failed Course Request

Practical Web Development — Lesson 9 of 10 · Advanced “The API failed” is difficult to investigate without a link between a user's error and a server event. A request identifier gives you that link. Structured logs make fields such as route, status and duration easier to search than an inconsistent paragraph of text. This example creates a small local server using only built-in Node modules. Log the completed response Save as server.mjs , run node server.mjs and open http://localhost:3000/api/courses . The server binds to the local machine for this learning exercise. import { createServer } from 'node:http'; import { randomUUID } from 'node:crypto'; import { performance } from 'node:perf_hooks'; createServer((req, res) => { const id = randomUUID(); const started = performance.now(); const path = new URL(req.url, 'http://localhost').pathname; const route = path === '/api/courses' ? '/api/courses' : 'unmatched'; re...

HTTP ETags Explained: Revalidate a Public Course Catalogue

Practical Web Development — Lesson 8 of 10 · Advanced A public course catalogue may change only a few times a day, but visitors can request it repeatedly. HTTP validators let a client ask whether its cached representation is still current. An ETag identifies a version of a representation; a conditional request can avoid downloading the same body again. Follow one revalidation exchange These are illustrative HTTP messages, not JavaScript to paste into Blogger. The server must generate a validator that changes when the selected representation changes. GET /api/courses HTTP/1.1 Host: example.test HTTP/1.1 200 OK Content-Type: application/json Cache-Control: public, max-age=60 ETag: "catalogue-v7" [{"id":"html","title":"HTML fundamentals"}] # After the cached response becomes stale: GET /api/courses HTTP/1.1 Host: example.test If-None-Match: "catalogue-v7" HTTP/1.1 304 Not Modified Cache-Control: public, max-age=60 ETag: ...

Node.js Test Runner: Test Course Filters Without a Framework

Practical Web Development — Lesson 7 of 10 · Intermediate A small pure function is a useful first testing target: it takes data and returns a result without network or browser dependencies. This lesson separates course filtering from the interface and tests the behaviour using Node's built-in test runner. Use a currently supported Node.js release and save the files with the .mjs extension. Define the behaviour first An empty or whitespace-only query returns every course. Matching ignores case. A query that matches nothing returns an empty array. The function should not reorder or change the caller's input array. These rules are a contract a user can observe, rather than a list of implementation details. Create filter.mjs : export function filterCourses(courses, query) { const term = query.trim().toLowerCase(); return courses.filter(course => course.title.toLowerCase().includes(term)); } Create filter.test.mjs in the same folder: import test from 'node:test'; imp...

Fetch Timeouts and Retries: Make Read Requests Predictable

Practical Web Development — Lesson 6 of 10 · Intermediate A loading spinner needs an exit path. A network request can stall, fail before a response arrives or return an HTTP error. This tutorial adds a per-attempt timeout and at most one retry for a read-only JSON request. It deliberately does not retry writes such as enrolment payments or progress changes. Build a bounded read helper Put this function in a browser module. Call it with a public same-origin JSON endpoint. A local test server with a delayed endpoint is useful for exercising timeout behaviour. async function readJson(url, timeoutMs = 5000) { for (let attempt = 0; attempt < 2; attempt++) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); let retry = false; try { const response = await fetch(url, { signal: controller.signal }); if (!response.ok) { const error = new Error('HTTP ' + response.status); error.retry...

JavaScript Event Delegation: Handle Dynamic Course Buttons

Practical Web Development — Lesson 5 of 10 · Intermediate When a catalogue adds new rows after loading, attaching a separate click handler to each initial button can leave the new buttons inactive. Event delegation places one handler on a stable parent and identifies which descendant initiated the event. This example uses real buttons, so keyboard activation keeps its normal browser behaviour. Create the list and one parent listener <ul id="catalogue"> <li>HTML <button type="button" data-course="html"><span>Shortlist</span></button></li> </ul> <button id="add" type="button">Add CSS row</button> <p id="status" role="status"></p> <script> const catalogue = document.querySelector('#catalogue'); const selected = new Set(); catalogue.addEventListener('click', event => { if (!(event.target instanceof Element)) return; con...

localStorage Tutorial: Save a Course Shortlist Safely

Practical Web Development — Lesson 4 of 10 · Intermediate A shortlist can help a learner remember courses between visits on the same browser. For a small non-sensitive list, localStorage is convenient. It is not a database shared across devices and it should not be the authority for enrolment or paid access. This exercise stores only known course IDs and treats everything read back as untrusted data. Use a versioned key and validate the shape Create shortlist.js with the following functions. Run the example on a local HTTP page, not by depending on file-URL storage behaviour. const KEY = 'course-shortlist-v1'; const allowed = new Set(['html', 'css', 'js']); function readShortlist() { try { const parsed = JSON.parse(localStorage.getItem(KEY) || '[]'); if (!Array.isArray(parsed)) return []; return [...new Set(parsed.filter(id => typeof id === 'string' && allowed.has(id)))]; } catch { return []; } } function...

JavaScript URLSearchParams: Share a Course Filter in a Link

Practical Web Development — Lesson 3 of 10 · Intermediate A search box that resets after reload is frustrating when someone wants to share a filtered list. Store the public search term in the URL so a link can recreate the same view. This exercise uses URL , URLSearchParams and the History API without a framework. Run it from a local HTTP server so the page has a normal web origin. Build the complete small example <label for="query">Find a course</label> <input id="query" type="search"> <p id="count" role="status"></p><ul id="courses"></ul> <script> const courses = ['HTML fundamentals', 'CSS layout', 'JavaScript DOM']; const input = document.querySelector('#query'); const list = document.querySelector('#courses'); const count = document.querySelector('#count'); const initial = new URL(location.href); input.value = (initial.searchPa...

CSS Container Queries: Make Course Cards Fit Any Column

Practical Web Development — Lesson 2 of 10 · Beginner to intermediate A course card may appear in a wide catalogue and a narrow sidebar on the same screen. A viewport media query sees the same screen width in both locations. A container query lets the card respond to the space its parent actually provides. This exercise uses a named inline-size container and keeps the default layout readable without the query. Separate the measuring box from the card The wrapper is the query container. Its descendant changes layout. Keeping those responsibilities separate avoids trying to make an element query its own size. Copy this into an HTML file; the two wrappers intentionally have different maximum widths. <style> .course-slot { container: course / inline-size; max-width: 48rem; margin: 1rem 0; } .course-slot.narrow { max-width: 20rem; } .course-card { display: grid; gap: 1rem; padding: 1rem; border: 1px solid #888; border-radius: .5rem; } .course-card h2 { margin: 0; overflow-wrap: anyw...

HTML Form Validation: Build an Accessible Course Enquiry Form

Practical Web Development — Lesson 1 of 10 · Beginner A course enquiry form should tell a learner what information to enter, catch simple mistakes and explain whether anything was sent. This tutorial builds a local demonstration with native HTML controls. It does not send enquiries to a real inbox. Save the example as index.html and open it in a browser. Start with labels and useful constraints Each visible label points to one input through matching for and id values. A placeholder is not a substitute: it disappears while someone types. Required fields are identified in the label, and the optional message has a length limit. Only collect information you need to answer the question. <!doctype html> <html lang="en"><meta charset="utf-8"> <title>Course enquiry demo</title> <form id="enquiry"> <p><label for="email">Email (required)</label> <input id="email" name="email...

Web App Architecture: Design a Reliable Course Platform

Web Development Learning Series — Part 10 of 10 · Senior Senior web development involves choosing tradeoffs that a team can operate and maintain. A course platform needs more than a frontend framework: it needs clear rules about content, enrollment, identity, failures, and releases. This article turns those concerns into a concrete design exercise. Assume a modest platform with public course pages, signed-in learners, and saved progress. The team is small. Start with that situation rather than copying the architecture of a much larger company.

Playwright Tutorial: Test a Course Filter and Empty State

Web Development Learning Series — Part 9 of 10 · Advanced A useful browser test protects behavior that matters to a visitor. For a course catalog, that might be finding a beginner lesson and understanding when a filter has no results. A test that only proves a button exists gives less confidence. This guide assumes an application with a visible Difficulty label, a select control, course cards, and a status message. Use the official Playwright setup instructions for a project that does not already have the test runner installed.

Core Web Vitals: Diagnose LCP, INP and CLS

Web Development Learning Series — Part 8 of 10 · Advanced A fast website helps learners reach the lesson and use its controls without unnecessary waiting. Performance work begins by identifying the slow experience, not by applying every optimization tip at once. This guide uses a course landing page as a practical example. Imagine the page opens with a large banner image, a signup widget, and a video lesson. Those features can affect loading, interaction, and stability in different ways. Measure each problem separately before changing the design.

Node.js API Tutorial: Build a Read-Only Course Endpoint

Web Development Learning Series — Part 7 of 10 · Advanced A backend gives a web application a place to apply trusted rules and deliver data. In this lesson, create a small read-only course API using Node.js and its built-in HTTP module. Keeping the first endpoint simple makes routing and response behavior easier to understand. Use a currently supported Node.js release. Save the code as server.mjs so its module syntax is explicit. This demonstration has no authentication, database, or write route; it is a local learning server.

React State Tutorial: Build a Course Filter

Web Development Learning Series — Part 6 of 10 · Intermediate to Advanced React is useful when a page has several connected interactive views. The challenge is often deciding what the application needs to remember. This lesson builds a difficulty filter for a course catalog and keeps the state small. Use an existing React learning project or the official documentation's interactive editor. This example is a component, not a complete installation guide. You should already know JavaScript arrays, functions, and basic JSX.

TypeScript Tutorial: Validate API Data at the Boundary

Web Development Learning Series — Part 5 of 10 · Intermediate TypeScript can catch many mistakes before a program runs, but an API response is still outside your control. A server can send an unexpected value even if your editor says the variable is a Course. This lesson shows where static types help and where a runtime check is necessary. You need basic JavaScript objects, arrays, and functions. Run the examples in the TypeScript Playground or a project configured with the current TypeScript compiler and strict checking.

JavaScript Fetch API: Loading, Errors and Cancellation

Web Development Learning Series — Part 4 of 10 · Intermediate A page that loads remote data needs more than a successful request. It needs to explain what happens while waiting, when the server fails, and when the response is not the shape you expected. This lesson adds a small API-driven course panel. You should already understand functions and DOM updates. The example uses a same-origin courses.json file so you can practice without an external service or an API key.

JavaScript DOM Tutorial: Build a Searchable Course List

Web Development Learning Series — Part 3 of 10 · Intermediate JavaScript becomes easier to learn when each interaction has a clear job. This project filters a course list as the reader types. It teaches events, arrays, and DOM updates without a framework. The finished feature should handle three situations: a matching query, an empty query that shows everything, and a query with no matches. Decide those behaviors before writing the event handler.

CSS Grid Tutorial: Build Responsive Course Cards

Web Development Learning Series — Part 2 of 10 · Beginner to Intermediate A course catalog should remain readable when the browser is narrow, the text is enlarged, or a card appears in a sidebar. In this lesson, build a small catalog without hard-coding a separate layout for every device. You need basic HTML and a stylesheet. Start with three cards containing a title, a short description, and a lesson link. Use real course names so you can notice when a longer title breaks the layout.

Semantic HTML Tutorial: Build a Course Landing Page

Web Development Learning Series — Part 1 of 10 · Beginner HTML gives a webpage its structure. In this lesson, you will build a course landing page that answers three questions: what will I learn, what do I need, and where should I begin? You do not need a framework or a build tool to complete the first version. Our example is a small course called Web Foundations. Replace the sample content with your own topic, but keep the structure clear. A successful first page is useful before you add animations or a complex design.