Unit 6: Web Application Development and Deployment

CSE326 — Internet Programming 10 min read

I. Orientation: The Development-to-Deployment Lifecycle

Modern client-side web development treats a website as a software product that must be written, inspected, corrected, organised, measured, versioned and published. This unit covers the tooling and discipline surrounding that cycle: the browser as a debugging instrument, the interactive UI as the artefact, Git/GitHub as the version and delivery mechanism, and performance and best practices as the quality criteria.

Defining properties and conventions assumed throughout:

  • Client-side execution: HTML, CSS and JavaScript are parsed and run inside the user's browser; there is no server process to inspect, so diagnosis happens in the browser itself.
  • Single-threaded JavaScript: one call stack plus an event loop with task and microtask queues; blocking code freezes rendering and input.
  • The DOM as shared state: the Document Object Model is the live tree the browser renders; all UI interactivity is DOM mutation.
  • Static hosting model: GitHub Pages serves files as-is over HTTPS — no PHP, no Node runtime, no database. Anything dynamic must be client-side or an external API.
  • Git's unit of work: the commit — an immutable snapshot with a parent pointer, author, timestamp and SHA-1 hash.
  • Progressive enhancement and graceful degradation: core content works without JS; enhancements layer on top.

II. Diagnosing Faults in Client-Side Code

A. Debugging Techniques

Debugging is the systematic reduction of the search space between a symptom and its cause; guessing is not debugging.

  • Reproduce first: fix the exact inputs, browser and steps. A bug that appears only after a form submit is a different bug from one on page load.
  • Bisection (divide and conquer): comment out or disable half the suspect code; the half that still fails contains the defect. git bisect applies the same logic across commit history.
  • Print/trace debugging: console.log with labels, plus richer variants:
    JS
      console.table(users);            // array of objects as a grid
      console.group('cart update');   // indented, collapsible block
      console.time('render'); /* ... */ console.timeEnd('render'); // ms elapsed
      console.assert(total >= 0, 'negative total', total);
  • Breakpoint debugging: pause execution and inspect live scope rather than guessing values. debugger; in source pauses when DevTools is open.
  • Rubber-duck and minimal reproduction: rewrite the failure as the smallest possible snippet; the act of stripping context usually exposes the cause.
  • Common client-side fault classes: undefined from a mistyped property, null from querying the DOM before it exists, this rebinding in callbacks, off-by-one in loops, stale closures inside setTimeout, and race conditions between two fetch calls.

B. Browser Developer Tools

DevTools (F12 / Ctrl+Shift+I) is the primary instrument; each panel answers a different question.

  • Elements: live DOM tree and computed CSS. Answers "why is this box the wrong size?" — check the box-model diagram and the strikethrough overridden rules in Styles.
  • Console: REPL against the page. Type $0 for the currently selected element, $$('a') for a querySelectorAll shorthand.
  • Sources: set line breakpoints, conditional breakpoints (i === 5), DOM-mutation and event-listener breakpoints; then Step over (F10), Step into (F11), and read the Scope and Call Stack panes. Watch expressions evaluate on every pause.
  • Network: each request's status, type, size, and waterfall timing. Diagnose 404s on a wrong asset path, CORS failures, and payload sizes; "Disable cache" and throttling to "Slow 3G" simulate real users.
  • Performance: records a flame chart of scripting, rendering and painting; long yellow bars are long tasks (>50 ms).
  • Application: localStorage, sessionStorage, cookies, service workers and cache.
  • Lighthouse: audits Performance, Accessibility, Best Practices and SEO, scoring 0–100.

C. Error Handling

Error handling converts unexpected states into controlled, reportable outcomes instead of a blank screen.

  • try…catch…finally: guards synchronous throwing code; finally runs regardless, for cleanup such as hiding a spinner.
    JS
      try {
        const data = JSON.parse(raw);
      } catch (err) {
        console.error(err.name, err.message);   // e.g. SyntaxError
        showMessage('Could not read saved data.');
      } finally {
        spinner.hidden = true;
      }
  • Error objects: name, message, stack. Built-ins include SyntaxError, TypeError, ReferenceError, RangeError. Custom types extend Error:
    JS
      class ValidationError extends Error {
        constructor(field) { super(`Invalid ${field}`); this.name = 'ValidationError'; }
      }
  • Asynchronous errors: a rejected promise is not caught by an enclosing try unless awaited. Use .catch() or try/await/catch. Note that fetch rejects only on network failure — a 404 resolves, so test response.ok explicitly.
  • Global safety nets: window.onerror / window.addEventListener('error') for uncaught exceptions and unhandledrejection for orphaned promises — used to log to a monitoring service.
  • Fail-soft UI: validate input at the boundary, show human-readable messages next to the offending field, keep technical detail in the console, and never silently swallow with an empty catch {}.

III. Building the Interactive Client

A. Interactive User Interface Development

Interactivity is the loop: user event → state change → DOM update → visible feedback.

  • Event-driven core: element.addEventListener('click', handler); the event object carries target, currentTarget, preventDefault() and stopPropagation().
  • Event delegation: attach one listener to a static parent and test e.target.closest('.item'). Essential for lists whose rows are created dynamically — one listener instead of 500.
  • Controlled feedback: every action needs visible acknowledgement — disabled button plus "Saving…", skeleton placeholders during fetch, inline validation on blur.
  • Accessible interaction: keyboard reachability, aria-live="polite" for status messages, focus management after opening a modal, and visible :focus-visible outlines.
  • Throttle vs debounce:
    1. Debounce — run once after activity stops (search-as-you-type, 300 ms).
    2. Throttle — run at most once per interval (scroll/resize handlers, 100 ms).

B. Client-Side Application Design

A client-side application separates what is true (state) from what is shown (view).

  • Single source of truth: hold state in one object; render is a function of it.
    JS
      let state = { todos: [], filter: 'all' };
      function setState(patch) { state = { ...state, ...patch }; render(state); }
  • Unidirectional data flow: events call setState; only render touches the DOM. Removes the "who changed this element?" class of bug.
  • Component thinking: each UI unit owns its markup template, its handlers and its slice of state; communicates via arguments and callbacks, not globals.
  • Persistence and routing: localStorage.setItem('todos', JSON.stringify(...)) for survival across reloads; history.pushState plus a hashchange/popstate handler for client-side routing.
  • Separation of concerns: data access (fetch layer) → logic (pure functions) → presentation (render). Pure functions are unit-testable without a browser.

C. Code Organization

Organisation is what keeps a working app modifiable six months later.

  • File structure: index.html, /css, /js, /assets, /data; one concern per file (api.js, ui.js, state.js, main.js).
  • ES modules: explicit dependencies, own scope, no globals.
    HTML
      <script type="module" src="js/main.js"></script>

    JS
      export function formatPrice(n) { return `₹${n.toFixed(2)}`; }
      import { formatPrice } from './utils.js';
  • Naming and style: camelCase for JS identifiers, PascalCase for classes, kebab-case for files and CSS classes; BEM (card__title--active) to keep CSS specificity flat.
  • DRY and single responsibility: extract a repeated block into a named function; a function that both fetches and renders should be split.
  • Tooling: Prettier for formatting, ESLint for defect-prone patterns, .editorconfig for consistency, JSDoc comments explaining why rather than what.

IV. Performance

A. Website Performance Fundamentals

Performance is measured, not asserted, and is dominated by bytes transferred and main-thread work.

  • Core Web Vitals: LCP (Largest Contentful Paint) ≤ 2.5 s — perceived load; INP (Interaction to Next Paint) ≤ 200 ms — responsiveness; CLS (Cumulative Layout Shift) ≤ 0.1 — visual stability.
  • The critical rendering path: HTML → DOM, CSS → CSSOM, then Render Tree → Layout → Paint → Composite. CSS blocks rendering; a plain <script> in <head> blocks parsing — use defer (execute after parse, in order) or async.
  • Reduce bytes: minify CSS/JS, gzip/Brotli compression, WebP/AVIF images with srcset, subset fonts with font-display: swap, tree-shake unused code.
  • Reduce requests and defer work: bundle small files, loading="lazy" on below-fold images, code-split with dynamic import().
  • Avoid layout thrash: batch DOM reads then writes; reading offsetHeight after a write forces synchronous reflow. Animate transform/opacity only — they skip layout and paint.
  • Caching: long Cache-Control lifetimes plus filename hashing (app.8f3a2.js) so a new deploy invalidates cleanly.

V. Version Control and Publication

A. GitHub Repository Management

A repository is the project's history plus its collaboration surface.

  • Core cycle:
    BASH
      git init
      git add .
      git commit -m "feat: add cart total calculation"
      git remote add origin https://github.com/user/repo.git
      git push -u origin main
  • Branching: git switch -c feature/cart isolates work; merge via a Pull Request so changes are reviewed and diffed before reaching main.
  • Repository hygiene: README.md (purpose, setup, live link), .gitignore (node_modules/, .env, .DS_Store), LICENSE, meaningful commit messages in the imperative mood (Conventional Commits: feat:, fix:, docs:).
  • Collaboration features: Issues for tracked defects, labels and milestones, Fixes #12 in a commit to auto-close, protected branches, and Actions for CI on every push.

B. Website Hosting using GitHub Pages

GitHub Pages serves static files directly from a repository over HTTPS at no cost.

  • Enable: Settings → Pages → Source: Deploy from a branch → branch main, folder / (root) or /docs. URL becomes https://<user>.github.io/<repo>/. A repo named <user>.github.io serves at the root domain.
  • Requirements and gotchas: an index.html at the chosen root; relative asset paths (./css/style.css, not /css/style.css) because the site sits in a subpath; a .nojekyll file to stop Jekyll ignoring folders beginning with _; propagation delay of a minute or two after each push.
  • Custom domains: add a CNAME file and point DNS at GitHub; then enable "Enforce HTTPS".
  • Limitations: no server-side code, no secrets (everything is public), soft limits around 1 GB site size and 100 GB/month bandwidth — so API keys must live behind a proxy, never in the repo.

VI. Discipline and Assistance

A. Web Development Best Practices

Best practices are the defaults that prevent whole categories of defect.

  • Semantic, valid HTML: <header> <nav> <main> <article> <footer>, one <h1>, alt on every meaningful image — this is also the accessibility and SEO baseline.
  • Responsive by default: <meta name="viewport" content="width=device-width, initial-scale=1">, mobile-first media queries, fluid units (rem, %, clamp()), Flexbox/Grid over floats.
  • Security: escape user input with textContent rather than innerHTML to block XSS; HTTPS everywhere; rel="noopener noreferrer" on target="_blank".
  • Accessibility: contrast ratio ≥ 4.5:1 for body text, labels tied to inputs via for/id, logical tab order.
  • Testing and cross-browser checks: verify in at least one Chromium, one Firefox and one Safari/WebKit engine; test with cache disabled and on a throttled connection.

B. Improving Web Applications using GitHub Copilot

Copilot is an AI pair programmer that suggests code inline from the surrounding context; it accelerates typing, not thinking.

  • How it is driven: open files, function names, type signatures and comments form the prompt. A precise comment yields precise code:
    JS
      // debounce fn by delay ms, cancel previous timer on each call
  • Productive uses: boilerplate (fetch wrappers, form validators, ARIA attributes), regex, test cases, JSDoc, converting callbacks to async/await, and Copilot Chat for /explain on unfamiliar code or /fix on a stack trace.
  • Refactoring and review: ask for an extraction of duplicated logic into a helper, or a performance rewrite (delegation instead of per-row listeners), then diff the suggestion.
  • Limits and responsibilities: suggestions may be subtly wrong, insecure (unescaped innerHTML), outdated (deprecated APIs) or licence-encumbered. Every accepted line must be read, run and tested; never accept generated code that handles secrets or authentication unreviewed.
  • Interaction with the rest of the unit: Copilot writes candidate code, but DevTools, error handling, Lighthouse scores and Git history remain the means of verifying that the code is actually correct.