
Web Development Essentials 032.1: Cấu trúc Tài liệu HTML
Tạo tài liệu HTML tiếng Việt; hiểu doctype, head, body, title, metadata và UTF-8; thử ba biến thể trong trình duyệt để nhận diện lỗi cấu trúc, thuộc tính.

Hiểu mã nguồn, compiler, runtime, thư viện và mô hình lập trình; thực hành Node.js đếm yêu cầu, bắt lỗi logic bằng test và xem thay đổi bằng Git cục bộ.
Cổng hỗ trợ có bốn yêu cầu: hai đang mở, một đã đóng và một đã lưu trữ. Nhóm muốn hiển thị số yêu cầu đang mở. Người viết mã dùng điều kiện “chưa đóng” và nhận kết quả ba. Chương trình chạy bình thường nhưng trả lời sai câu hỏi nghiệp vụ. Tình huống giả lập này cho thấy phát triển phần mềm cần cả yêu cầu, mã nguồn, môi trường thực thi và cách kiểm tra kết quả.
Bài học thuộc mục tiêu 031.1, dành cho người mới học web và IT Help Desk muốn hiểu chương trình mình đang hỗ trợ. Bạn cần biết tạo thư mục, lưu tệp văn bản và chạy lệnh trong môi trường được phép. Sau bài, bạn nên phân biệt editor với runtime, giải thích thư viện và mô hình lập trình, rồi kiểm tra một thay đổi nhỏ bằng test và Git.
Khung tham chiếu là PDF 030-100-vi, mục 031.1, trang in 2–16, tương ứng trang vật lý 10–24. Nội dung, tình huống và mã dưới đây được viết mới. Phần cập nhật được đối chiếu ngày 23/10/2026; các ví dụ về runtime không phải kết quả benchmark giữa những ngôn ngữ.
Mã nguồn là phần mô tả chương trình bằng ngôn ngữ có cú pháp và quy tắc xác định. Trong ví dụ, quy tắc đếm yêu cầu nằm trong một hàm, còn danh sách trạng thái là dữ liệu đầu vào. Tách hai phần giúp ta dùng cùng quy tắc với nhiều bộ dữ liệu và biết lỗi xuất hiện ở đâu.
Tệp .js, .py hay .c chỉ gợi ý cách sử dụng; đổi tên không tự chuyển đổi ngôn ngữ. Mã cần công cụ phù hợp để được kiểm tra, dịch hoặc thực thi. Một file JavaScript hợp lệ cũng có thể lỗi khi gọi API chỉ tồn tại trong trình duyệt mà bạn lại chạy bằng Node.js.
Đừng coi mã nguồn không có lỗi cú pháp là chương trình đúng. Cú pháp đúng giúp công cụ hiểu câu lệnh, nhưng yêu cầu “đang mở” vẫn phải được chuyển thành điều kiện chính xác. Một số lỗi chỉ xuất hiện với dữ liệu biên, quyền truy cập khác hoặc phiên bản phụ thuộc khác.
Editor cho phép viết văn bản, thường có tô màu cú pháp, gợi ý, tìm kiếm và định dạng. IDE tích hợp thêm các khả năng như quản lý dự án, build, debugger và test runner. Ranh giới có thể mờ khi editor được mở rộng bằng plugin; hãy nhìn chức năng đang dùng thay vì chọn công cụ chỉ theo nhãn.
Debugger giúp dừng chương trình và quan sát giá trị tại một thời điểm. Test runner chạy các trường hợp đã viết và báo đạt hoặc thất bại. Hai công cụ trả lời những câu hỏi khác nhau: debugger giúp điều tra một lần chạy, còn test giúp lặp lại kiểm tra sau thay đổi.
Một gợi ý tự động vẫn cần được đọc và kiểm tra. Plugin hoặc công cụ AI có thể đề xuất API không tồn tại ở phiên bản đang dùng. Trước khi chấp nhận mã, bạn cần giải thích đầu vào, đầu ra, quyền mà mã yêu cầu và cách xác nhận hành vi. Không đưa log hay bí mật công việc vào dịch vụ gợi ý khi chưa có quyền.

Với một chương trình C thông thường, compiler có thể tạo mã máy cho nền tảng đích trước khi chạy. Hệ điều hành và kiến trúc đích ảnh hưởng khả năng dùng binary đó. Có mã nguồn không đồng nghĩa cùng một binary sẽ chạy trên mọi máy; xây dựng lại cũng có thể cần thư viện và cấu hình phù hợp.
Các runtime hiện đại thường kết hợp nhiều giai đoạn. Tài liệu Python về bytecode mô tả dạng trung gian được Python sử dụng. Trong .NET, mã managed và Intermediate Language cho thấy biên dịch có thể tạo mã trung gian rồi runtime tiếp tục xử lý.
JavaScript cũng không nên được giải thích là luôn dịch từng dòng trực tiếp sang mã máy. V8 có trình thông dịch Ignition và compiler tối ưu TurboFan. Đây là ví dụ về triển khai cụ thể, không phải yêu cầu mọi engine JavaScript có cùng kiến trúc nội bộ.
Vì vậy, tránh xếp hạng hiệu năng chỉ từ nhãn “biên dịch” hay “thông dịch”. Thuật toán, dữ liệu, I/O, runtime và điều kiện đo đều ảnh hưởng. Bài này chỉ cần bạn phân biệt mã nguồn, sản phẩm dịch và môi trường chạy; không cần tự xây compiler.
C và C++ thường xuất hiện ở phần mềm hệ thống, thư viện và thành phần cần kiểm soát tài nguyên. C# gắn với hệ sinh thái .NET; Java thường đi cùng JVM. Java và JavaScript là hai ngôn ngữ khác nhau, không phải một bản rút gọn và một bản đầy đủ.
JavaScript được dùng cho tương tác trong trình duyệt và có thể chạy phía server bằng Node.js. Python xuất hiện trong tự động hóa, xử lý dữ liệu và ứng dụng server. PHP cũng dùng để xử lý ứng dụng web phía server. Đây là những cách sử dụng điển hình, không phải danh sách độc quyền hoặc bảng xếp hạng độ phổ biến.
Chọn ngôn ngữ phải xét môi trường được hỗ trợ, thư viện, khả năng bảo trì và kỹ năng nhóm. Một portal nhỏ chưa cần kết hợp nhiều ngôn ngữ. Đưa thêm công cụ có thể tăng cả việc triển khai, cập nhật và chẩn đoán khi có lỗi.
Cách viết thủ tục tổ chức các bước và lời gọi hàm để thực hiện công việc. Với hàng đợi hỗ trợ, bạn có thể đọc danh sách, chọn các yêu cầu đang mở rồi hiển thị số lượng. Cần nhìn dữ liệu và trạng thái thay đổi qua từng bước.
Hướng đối tượng gom trạng thái và hành vi vào đối tượng. Ví dụ, một đối tượng yêu cầu có trạng thái và phương thức chuyển trạng thái theo quy tắc. Dùng class không tự làm thiết kế tốt hơn; nếu các phần vẫn thay đổi dữ liệu của nhau tùy ý, việc hiểu và kiểm tra còn khó.
Phong cách hàm khuyến khích biểu diễn việc biến đổi dữ liệu bằng hàm. Một hàm thuần trả cùng kết quả cho cùng đầu vào và không tạo tác động bên ngoài. countOpen trong lab không sửa danh sách đầu vào. Một hàm có tham số chưa đủ để kết luận nó thuần, vì bên trong vẫn có thể đọc trạng thái chung hoặc ghi tệp.
Nhiều ngôn ngữ hỗ trợ nhiều mô hình. Một chương trình có thể dùng đối tượng ở ranh giới ứng dụng và hàm nhỏ để tính toán. Chọn cấu trúc giúp giải thích quy tắc và hạn chế phụ thuộc, không cần ép mọi bài toán thành cùng một mô hình.
Thư viện cung cấp chức năng để chương trình dùng lại. Module là đơn vị tổ chức và cung cấp mã theo cơ chế của môi trường. Chúng có liên quan nhưng không đồng nghĩa: một thư viện có thể gồm nhiều module, còn module tự viết chưa phải gói đã phát hành.
Lab dùng module cục bộ và module dựng sẵn của Node.js, không tải gói bên ngoài. Tài liệu ES modules của Node.js giải thích cơ chế import và cách nhận diện module. Đuôi .mjs giúp làm rõ lựa chọn ở ví dụ này.
Khi cần dependency, kiểm tra chức năng, phiên bản hỗ trợ, giấy phép và nguồn phát hành. Đừng chọn một gói chỉ vì lượt tải hoặc đoạn mẫu chạy được. Cập nhật dependency cần kiểm tra thay đổi hành vi; lockfile giúp ghi lại lựa chọn phiên bản nhưng không chứng minh gói không có lỗi.
Môi trường đã kiểm chứng là Node.js 24.22.0 trong container Alpine, quyền user node, filesystem gốc chỉ đọc, thư mục tạm riêng và tắt mạng. Git được thử riêng bằng Apple Git 2.39.5 trong thư mục tạm trên macOS. Không dùng repository công việc, không kết nối remote và không cài npm package.
Trong thư mục lab trống, tạo queue.mjs:
export function countOpen(tickets) {
return tickets.filter(ticket => ticket.status === 'open').length;
}
Tạo queue.test.mjs. Hai phép kiểm trong ca đầu vừa xác nhận kết quả vừa kiểm tra đầu vào không đổi:
import test from 'node:test';
import assert from 'node:assert/strict';
import { countOpen } from './queue.mjs';
test('mixed statuses count only open', () => {
const rows = [{status:'open'}, {status:'closed'},
{status:'open'}, {status:'archived'}];
const before = JSON.stringify(rows);
assert.equal(countOpen(rows), 2);
assert.equal(JSON.stringify(rows), before);
});
test('empty queue is zero', () => assert.equal(countOpen([]), 0));
test('archived is not open', () =>
assert.equal(countOpen([{status:'archived'}]), 0));
Chạy lệnh sau, mong đợi ba test đạt và không có test thất bại:
node --version
node --test queue.test.mjs
Node.js test runner là nguồn đối chiếu cho lệnh và API. Tiếp theo, chỉ sửa điều kiện trong queue.mjs thành ticket.status !== 'closed', chạy lại test rồi trả điều kiện về bản đúng. Bản sai phải làm test thất bại vì đã tính cả archived; nếu vẫn báo đạt, cần xem lại file vừa lưu và lệnh thực chạy.
Kiểm chứng thực tế đã cho ba test đạt ở bản đúng, phát hiện bản sai và đạt lại sau khi phục hồi. Đây là test cho hành vi đã chọn, chưa kiểm tra dữ liệu thiếu trường, hiệu năng, database hay giao diện. Ba dấu đạt không chứng minh chương trình đúng với mọi đầu vào.

Git lưu các snapshot được commit cùng lịch sử liên quan. Phần chưa commit chưa tự được đưa vào lịch sử đó. Trong một thư mục lab riêng, bạn có thể tạo một mốc của tệp ghi chú, sửa một dòng rồi dùng git diff để xem phần khác biệt. Không thực hiện bài tập trong checkout công việc đang có thay đổi.
Theo tài liệu git diff, cần hiểu hai trạng thái đang được so sánh. Lệnh không thêm lựa chọn thường giúp nhìn thay đổi chưa stage so với index; nó không tự chứng minh thay đổi đã commit hay đã được sao lưu ở nơi khác.
Trong kiểm chứng riêng của bài, một repository tạm được khởi tạo, commit tệp DEMO rồi sửa dòng và đọc diff thành công. Không thay cấu hình Git toàn cục hay gửi dữ liệu ra dịch vụ lưu trữ. Danh tính tác giả trong commit cũng không tự là bằng chứng xác minh người đó ngoài đời.
Nếu gặp lỗi không tìm thấy module, kiểm tra tên, đường dẫn tương đối, đuôi file và thư mục hiện tại. Nếu import bị từ chối, kiểm tra runtime và cách nhận diện module. Nếu test chạy nhưng số lượng sai, đọc yêu cầu và dữ liệu trước khi đổi công cụ.
Bạn đạt mục tiêu khi giải thích được vì sao bản “chưa đóng” sai, chỉ ra test nào phát hiện nó và phân biệt sửa mã với sửa kỳ vọng để che lỗi. Hãy nêu thêm một trường hợp chưa kiểm tra và cách kiểm tra phù hợp. Giữ mã đúng, output và ghi chú phiên bản để sang bài kiến trúc ứng dụng web.

Tạo tài liệu HTML tiếng Việt; hiểu doctype, head, body, title, metadata và UTF-8; thử ba biến thể trong trình duyệt để nhận diện lỗi cấu trúc, thuộc tính.

Đọc URL, phương thức, header và trạng thái HTTP; phân biệt cookie, phiên, HTTPS và HTTP/3; thực hành ETag với chuỗi 200, 304, 200 trên tệp DEMO cục bộ.

Phân biệt trình duyệt, web server, API và database; hiểu REST, GraphQL, SPA, PWA và WebAssembly; thực hành tách phản hồi HTML, JSON cùng lỗi HTTP cục bộ.

Học web qua 18 mục tiêu: phần mềm, HTTP, HTML, CSS, JavaScript, Node.js và database; xây sản phẩm nhỏ, kiểm tra dữ liệu và ghi rõ giới hạn thực hành.