66 điểm bởi GN⁺ 2025-08-18 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tổng hợp những cạm bẫy không trực quan mà lập trình viên thường mắc phải, đồng thời giới thiệu nguyên nhân của các lỗi dễ phát sinh
  • Đề cập đến các vấn đề thường gặp trong nhiều công nghệ như HTML, CSS, Unicode/mã hóa văn bản, số thực dấu chấm động, thời gian
  • Nhấn mạnh rằng những khác biệt tinh vi về cú pháp và hành vi giữa từng ngôn ngữ và framework có thể gây ra hiểu nhầm hoặc lỗi
  • Giải thích bằng ví dụ các cạm bẫy có thể xuất hiện trong môi trường vận hành thực tế ở những lĩnh vực cốt lõi của backend như đồng thời, mạng, cơ sở dữ liệu
  • Thông qua nhiều ví dụ và liên kết tham khảo, hướng dẫn tình huống sự cố, cách khắc phục và cách cải thiện các hành vi ngoài dự kiến

HTML và CSS

  • Giá trị mặc định của min-width trong Flexbox/Grid

    • min-width mặc định là auto
    • min-width: auto được quyết định bởi kích thước nội dung và được ưu tiên hơn flex-shrink, overflow: hidden, width: 0, max-width: 100%
    • Khuyến nghị: khai báo rõ min-width: 0
  • Sự khác biệt giữa chiều ngang và chiều dọc trong CSS

    • width: auto cố gắng lấp đầy không gian của phần tử cha, còn height: auto thì khớp theo nội dung
    • width: auto của các phần tử inline, inline-block, float sẽ không giãn ra
    • margin: 0 auto căn giữa theo chiều ngang, còn margin: auto 0 không thể căn giữa theo chiều dọc (tuy nhiên trong flex-direction: column thì có thể căn giữa theo chiều dọc)
    • Gộp margin chỉ xảy ra theo chiều dọc
    • Nếu hướng bố cục thay đổi như writing-mode: vertical-rl thì hành vi cũng đảo ngược
  • Block Formatting Context (BFC)

    • Tạo BFC bằng display: flow-root (ngoài ra overflow: hidden/auto/scroll, display: table v.v. cũng có thể tạo nhưng có tác dụng phụ)
    • Có thể dùng BFC để ngăn hiện tượng margin dọc của các phần tử anh em liền kề chồng lên nhau hoặc margin của phần tử con tràn ra ngoài phần tử cha
    • Nếu phần tử cha chỉ chứa các phần tử con float thì chiều cao có thể sụp về 0 → có thể sửa bằng BFC
    • Nếu có border hoặc padding thì sẽ không xảy ra gộp margin
  • Stacking Context

    • Các điều kiện tạo stacking context mới
      • Các thuộc tính render như transform, filter, perspective, mask, opacity
      • position: fixed hoặc sticky
      • Có chỉ định z-index + định vị absolute/relative
      • Có chỉ định z-index + phần tử bên trong flexbox/grid
      • isolation: isolate
    • Đặc điểm
      • z-index chỉ có hiệu lực bên trong stacking context
      • Tọa độ của position: absolute/fixed lấy phần tử tổ tiên gần nhất có định vị làm mốc
      • sticky không hoạt động vượt qua stacking context
      • Ngay cả overflow: visible cũng bị cắt bởi stacking context
      • background-attachment: fixed được bố trí dựa trên stacking context
  • Đơn vị viewport

    • Trên trình duyệt di động, khi thanh địa chỉ/thanh điều hướng biến mất khỏi màn hình lúc cuộn, giá trị 100vh sẽ thay đổi
    • Cách giải quyết hiện đại: dùng 100dvh
  • Mốc tham chiếu của Absolute Position

    • position: absolute không lấy phần tử cha làm mốc, mà lấy tổ tiên gần nhất có relative/absolute hoặc tổ tiên tạo stacking context làm chuẩn
  • Cách hoạt động của Blur

    • backdrop-filter: blur không xét đến các phần tử xung quanh
  • Float bị vô hiệu hóa

    • Nếu phần tử cha là flex hoặc grid thì float của phần tử con không có tác dụng
  • Đơn vị phần trăm cho width/height

    • Sẽ không hoạt động nếu kích thước của phần tử cha chưa được xác định trước (để tránh tham chiếu vòng lặp)
  • Đặc tính của phần tử Inline

    • display: inline bỏ qua width, height, margin-top, margin-bottom
  • Xử lý khoảng trắng

    • Mặc định, xuống dòng trong HTML được xem là khoảng trắng, và nhiều khoảng trắng liên tiếp sẽ bị rút gọn thành một
    • `` ngăn việc rút gọn khoảng trắng nhưng có hành vi đặc biệt ở phần đầu/cuối
    • Khoảng trắng ở đầu/cuối của hầu hết nội dung sẽ bị bỏ qua, nhưng `` là ngoại lệ
    • Khoảng trắng/xuống dòng giữa các inline-block sẽ được hiển thị thành khoảng cách thực tế (không xảy ra với flex/grid)
  • text-align

    • Có áp dụng cho văn bản và phần tử inline nhưng không áp dụng để căn chỉnh phần tử block
  • box-sizing

    • Giá trị mặc định là content-box → không bao gồm padding/border
    • Khi đặt width: 100% + padding, phần tử có thể tràn ra ngoài vùng của phần tử cha
    • Cách khắc phục: box-sizing: border-box
  • Cumulative Layout Shift

    • Nếu không chỉ định thuộc tính widthheight cho ``, việc tải ảnh chậm có thể làm bố cục bị xô lệch
    • Khuyến nghị: chỉ định các thuộc tính này để tránh CLS
  • Yêu cầu mạng khi tải tệp trong Chrome

    • Không hiển thị trong bảng Network của DevTools (được xử lý ở tab khác)
    • Nếu cần phân tích, dùng chrome://net-export/
  • Vấn đề phân tích cú pháp JavaScript trong HTML

    • Với trường hợp như console.log(''), thẻ `` đầu tiên sẽ được nhận diện là thẻ đóng
    • Tham khảo: Safe JSON in script tags

Unicode và mã hóa văn bản

  • Code point và grapheme cluster

    • Grapheme cluster là “đơn vị ký tự” ở cấp độ GUI
    • Ký tự ASCII hiển thị được có 1 code point = 1 grapheme cluster
    • Emoji có thể là một grapheme cluster duy nhất được tạo từ nhiều code point
    • Trong UTF-8, code point chiếm từ 1~4 byte, và số byte không trùng với số code point
    • Trong UTF-16, code point chiếm 2 byte hoặc 4 byte (surrogate pair)
    • Tiêu chuẩn không giới hạn số code point trong một cluster, nhưng trong triển khai thực tế thường có giới hạn vì hiệu năng
  • Khác biệt về cách chuỗi hoạt động theo từng ngôn ngữ

    • Rust: chuỗi nội bộ dùng UTF-8, len() là số byte, không thể index trực tiếp, chars().count() là số code point, kiểm tra tính hợp lệ UTF-8 rất nghiêm ngặt
    • Golang: chuỗi về bản chất là mảng byte, độ dài và indexing tính theo byte, thường dùng UTF-8
    • Java, C#, JS: dựa trên UTF-16, đo độ dài theo đơn vị 2 byte, indexing cũng theo đơn vị 2 byte, có surrogate pair
    • Python: len() trả về số code point, indexing trả về một chuỗi chứa một code point
    • C++: std::string không có ràng buộc về mã hóa, hoạt động như một vector byte, độ dài/indexing đều tính theo byte
    • Trong các ngôn ngữ được nhắc đến, không có ngôn ngữ nào đo độ dài/indexing theo đơn vị grapheme cluster
  • BOM (Byte Order Mark)

    • Một số tệp văn bản có BOM, ví dụ: EF BB BF → biểu thị mã hóa UTF-8
    • Chủ yếu được dùng trên Windows, và phần mềm ngoài Windows có thể không xử lý được BOM
  • Các lưu ý khác

    • Khi chuyển dữ liệu nhị phân thành chuỗi, phần không hợp lệ sẽ được thay bằng � (U+FFFD)
    • Có tồn tại confusable characters (các ký tự trông giống nhau)
    • Chuẩn hóa (Normalization): ví dụ, é có thể được biểu diễn bằng U+00E9 (một code point) hoặc U+0065+U+0301 (hai code point)
    • Có tồn tại zero-width charactersinvisible characters
    • Khác biệt về xuống dòng: Windows dùng CRLF \r\n, Linux/MacOS dùng LF \n
    • Hợp nhất Hán tự (Han unification): các ký tự có hình dạng hơi khác nhau theo từng ngôn ngữ nhưng dùng cùng một code point
      • Font sẽ render phù hợp bằng cách bao gồm các biến thể theo ngôn ngữ
      • Khi quốc tế hóa, cần chọn đúng biến thể font

Số thực dấu chấm động (Floating point)

  • Đặc tính của NaN

    • NaN không bằng bất kỳ giá trị nào, kể cả chính nó (NaN == NaN luôn là false)
    • NaN != NaN luôn là true
    • Kết quả của phép toán có chứa NaN phần lớn sẽ lan truyền thành NaN
  • Các giá trị đặc biệt

    • Có tồn tại +Inf và -Inf, khác với NaN
    • -0.0 là giá trị phân biệt với +0.0
      • Trong phép so sánh thì chúng giống nhau, nhưng trong một số phép tính sẽ hoạt động khác nhau
      • Ví dụ: 1.0 / +0.0 == +Inf, 1.0 / -0.0 == -Inf
  • Khả năng tương thích với JSON

    • Chuẩn JSON không cho phép NaN và Inf
      • JS JSON.stringify chuyển NaN, Inf thành null
      • Python json.dumps(...) in nguyên NaN, Infinity (vi phạm chuẩn)
        • Khi dùng tùy chọn allow_nan=False, nếu có NaN/Inf sẽ phát sinh ValueError
      • Golang json.Marshal trả về lỗi nếu tồn tại NaN/Inf
  • Vấn đề về độ chính xác

    • So sánh trực tiếp số dấu chấm động có thể thất bại → khuyến nghị dùng dạng abs(a - b) < ε
    • JS xử lý mọi số dưới dạng dấu chấm động
      • Phạm vi số nguyên an toàn là -(2^53 - 1) ~ 2^53 - 1
      • Vượt ra ngoài phạm vi này thì biểu diễn số nguyên sẽ không chính xác
      • Nên dùng BigInt cho số nguyên lớn
      • Nếu JSON chứa số nguyên vượt ngoài phạm vi an toàn thì giá trị từ JSON.parse có thể không chính xác
      • Timestamp tính bằng mili giây an toàn tới 287.396 năm, còn đơn vị nano giây sẽ gặp vấn đề
  • Các quy luật phép toán không áp dụng

    • Tùy theo thứ tự tính toán, do mất mát độ chính xác nên tính kết hợp và tính phân phối không còn đúng một cách nghiêm ngặt
    • Tính toán song song (nhân ma trận, tính tổng, v.v.) có thể tạo ra kết quả không tất định
  • Hiệu năng

    • Phép chia chậm hơn rất nhiều so với phép nhân
    • Khi chia nhiều lần cho cùng một số, có thể tối ưu bằng cách tính nghịch đảo trước rồi nhân
  • Khác biệt theo phần cứng

    • Có hỗ trợ FMA (Fused Multiply-Add) hay không: một số phần cứng tính trung gian với độ chính xác cao hơn
    • Xử lý dải subnormal: phần cứng hiện đại hỗ trợ nhưng một số đời cũ sẽ xử lý thành 0
    • Khác biệt về chế độ làm tròn
      • Có các kiểu như RNTE (làm tròn về số chẵn gần nhất), RTZ (cắt về 0)
      • x86/ARM có thể thiết lập dưới dạng trạng thái mutable cục bộ theo thread
      • GPU có chế độ làm tròn khác nhau theo từng lệnh
    • Sự khác biệt trong hành vi của các hàm toán học như lượng giác, log
    • x86 có FPU 80-bit kiểu legacy và per-core rounding mode → không khuyến nghị sử dụng
    • Ngoài ra còn nhiều yếu tố khác khiến kết quả số dấu chấm động khác nhau giữa các phần cứng
  • Cách cải thiện độ chính xác

    • Tổ chức đồ thị tính toán theo chiều sâu nông hơn (giảm chuỗi phép nhân liên tiếp)
    • Tránh các trường hợp giá trị trung gian quá lớn hoặc quá nhỏ
    • Tận dụng các phép toán phần cứng như FMA

Thời gian (Time)

  • Giây nhuận (Leap second)

    • Unix timestamp bỏ qua giây nhuận
    • Khi xảy ra giây nhuận, thời gian ở vùng lân cận có thể bị kéo dài hoặc rút ngắn (Leap smear)
  • Múi giờ (Time zone)

    • UTC và Unix timestamp là thống nhất trên toàn thế giới
    • Thời gian con người đọc được phụ thuộc vào múi giờ của từng khu vực
    • Khuyến nghị lưu timestamp trong DB rồi chuyển đổi ở UI
  • Giờ mùa hè (DST)

    • Một số khu vực sẽ điều chỉnh đồng hồ thêm 1 giờ vào mùa hè
  • Đồng bộ NTP

    • Trong quá trình đồng bộ có thể xảy ra tình huống thời gian "chạy lùi"
  • Thiết lập múi giờ máy chủ

    • Khuyến nghị đặt máy chủ ở UTC
    • Trong hệ thống phân tán, nếu mỗi node dùng múi giờ khác nhau sẽ phát sinh vấn đề
    • Sau khi đổi múi giờ hệ thống, cần cấu hình lại hoặc khởi động lại DB
  • Đồng hồ phần cứng vs đồng hồ hệ thống

    • Đồng hồ phần cứng không có khái niệm múi giờ
    • Linux: xử lý đồng hồ phần cứng theo UTC
    • Windows: xử lý đồng hồ phần cứng theo giờ địa phương

Java

  • == dùng để so sánh tham chiếu đối tượng, muốn so sánh nội dung đối tượng thì cần dùng .equals
  • Nếu không override equalshashcode, map/set sẽ xác định tính đồng nhất của đối tượng dựa trên tham chiếu
  • Nếu thay đổi nội dung của đối tượng key trong map hoặc phần tử trong set thì hành vi của container sẽ bị hỏng
  • Phương thức trả về List trong một số trường hợp có thể trả về ArrayList mutable hoặc Collections.emptyList() immutable; sửa đối tượng sau sẽ gây ra UnsupportedOperationException
  • Có trường hợp phương thức trả về Optional lại trả về null (không được khuyến nghị)
  • Nếu return trong khối finally, ngoại lệ phát sinh ở try hoặc catch sẽ bị bỏ qua và giá trị trả về của finally sẽ được áp dụng
  • Có thư viện bỏ qua interrupt, và quá trình khởi tạo class bao gồm IO có thể bị interrupt làm hỏng
  • Ngoại lệ của task được truyền bằng .submit() trong thread pool theo mặc định sẽ không được in ra log mà chỉ có thể kiểm tra qua future; nếu bỏ qua future thì không thể biết có ngoại lệ hay không
    • Tác vụ scheduleAtFixedRate sẽ âm thầm dừng khi phát sinh ngoại lệ
  • Nếu literal số bắt đầu bằng 0 thì sẽ được xử lý là bát phân (0123 → 83)
  • Debugger sẽ gọi .toString() của biến cục bộ; một số class có toString() gây tác dụng phụ nên hành vi code khi debug có thể khác đi (có thể tắt trong IDE)

Golang

  • append() sẽ tái sử dụng bộ nhớ nếu còn capacity; append vào subslice có thể ghi đè cả vùng nhớ của mảng cha
  • defer được thực thi khi hàm return, không phải khi kết thúc block scope
  • defer capture biến mutable
  • Liên quan đến nil
    • nil slice và empty slice là khác nhau
    • string không thể là nil, chỉ có chuỗi rỗng
    • nil map có thể đọc nhưng không thể ghi
    • Hành vi đặc biệt của interface nil: nếu data pointer là null nhưng type info không null thì vẫn không bằng nil
  • Dead wait: có những trường hợp bug đồng thời thực sự trong Go
  • Có nhiều loại timeout khác nhau, được bàn chi tiết trong net/http

C/C++

  • Nếu lưu con trỏ tới phần tử của std::vector, khi vector tăng kích thước sẽ xảy ra cấp phát lại, làm con trỏ mất hiệu lực
  • std::string được tạo từ chuỗi literal có thể là đối tượng tạm thời, gọi c_str() sẽ nguy hiểm
  • Khi sửa container trong lúc lặp, iterator sẽ bị mất hiệu lực
  • std::remove không thực sự xóa mà chỉ sắp xếp lại phần tử, muốn xóa cần erase
  • Nếu literal số bắt đầu bằng 0 thì sẽ được xử lý là số bát phân (0123 → 83)
  • Undefined behavior (UB): trong quá trình tối ưu hóa, UB có thể bị biến đổi tùy ý nên rất nguy hiểm nếu phụ thuộc vào nó
    • Truy cập bộ nhớ chưa được khởi tạo là UB
    • Chuyển char* thành con trỏ struct rồi truy cập trước khi vòng đời đối tượng bắt đầu là UB, nên khởi tạo bằng memcpy
    • Truy cập bộ nhớ không hợp lệ (như con trỏ null) là UB
    • Tràn số nguyên/underflow là UB (với unsigned thì có thể underflow xuống dưới 0)
    • Aliasing: nếu con trỏ của các kiểu khác nhau cùng tham chiếu một vùng nhớ thì có thể phát sinh UB theo strict aliasing rule
      • Ngoại lệ: 1) kiểu có quan hệ kế thừa 2) chuyển đổi qua char*, unsigned char*, std::byte* (không áp dụng cho chuyển đổi ngược)
      • Nên dùng memcpy hoặc std::bit_cast cho ép kiểu cưỡng bức
    • Truy cập bộ nhớ không thẳng hàng là UB
  • Căn chỉnh bộ nhớ (memory alignment)
    • Số nguyên 64-bit phải có địa chỉ chia hết cho 8
    • Trên ARM, truy cập unaligned có thể gây crash
    • Khi diễn giải trực tiếp byte buffer thành struct có thể phát sinh vấn đề alignment
    • alignment có thể tạo ra struct padding, gây lãng phí bộ nhớ
    • Một số lệnh SIMD (như AVX) chỉ xử lý được dữ liệu đã căn chỉnh, thường cần alignment 32 byte

Python

  • Tham số mặc định của hàm không được tạo mới ở mỗi lần gọi mà giá trị ban đầu sẽ được giữ nguyên

SQL Databases

  • Xử lý Null

    • x = null không hoạt động, phải dùng x is null
    • Null không bằng chính nó (tương tự NaN)
    • Unique index cho phép trùng Null (trừ Microsoft SQL Server)
    • Cách xử lý Null trong select distinct khác nhau tùy DB
    • count(x)count(distinct x) bỏ qua các hàng có giá trị Null
  • Hành vi chung

    • Việc chuyển đổi ngày tháng ngầm định có thể phụ thuộc vào timezone
    • Join phức tạp + distinct có thể chậm hơn truy vấn lồng nhau
    • Trong MySQL(InnoDB), nếu trường chuỗi không dùng utf8mb4 thì sẽ lỗi khi chèn ký tự UTF-8 4 byte
    • MySQL(InnoDB) mặc định không phân biệt chữ hoa chữ thường
    • MySQL(InnoDB) cho phép chuyển đổi ngầm định: select '123abc' + 1; → 124
    • Gap lock của MySQL(InnoDB) có thể gây deadlock
    • Trong MySQL(InnoDB), nếu group by và cột select không khớp thì có thể trả về kết quả không xác định
    • Trong SQLite, nếu không bật strict thì kiểu trường gần như không có nhiều ý nghĩa
    • Foreign key có thể tạo lock ngầm và gây deadlock
    • Locking có thể phá vỡ repeatable read isolation tùy DB
    • SQL DB phân tán có thể không hỗ trợ locking hoặc có hành vi đặc thù (khác nhau tùy DB)
  • Hiệu năng/vận hành

    • Vấn đề N+1 query không xuất hiện trong slow query log vì từng truy vấn riêng lẻ đều nhanh
    • Giao dịch chạy lâu có thể gây vấn đề về lock, v.v. → nên kết thúc transaction càng nhanh càng tốt
    • Các trường hợp lock toàn bộ bảng
      • Trong MySQL 8.0+, khi thêm unique index/foreign key thì phần lớn có thể xử lý đồng thời
      • MySQL phiên bản cũ có thể lock toàn bộ bảng
      • Nếu mysqldump không có tùy chọn --single-transaction thì sẽ đặt read lock lên toàn bộ bảng
      • Trong PostgreSQL, create unique index hoặc alter table ... add foreign key sẽ gây read lock toàn bộ bảng
        • Cách tránh: dùng create unique index concurrently
        • Với foreign key thì dùng ... not valid rồi validate constraint
  • Truy vấn Range

    • Phạm vi không chồng lấp:
      • Điều kiện đơn giản p >= start and p file 2>&1 → cả stdout+stderr vào file, cmd 2>&1 > file → chỉ stdout vào file, stderr giữ nguyên
  • Tên file phân biệt chữ hoa chữ thường (khác Windows)

  • File thực thi có hệ thống capability (getcap để kiểm tra)

  • Rủi ro với biến unset: nếu DIR chưa được set thì rm -rf $DIR/ → có nguy cơ chạy rm -rf / → có thể phòng tránh bằng set -u

  • Áp dụng môi trường: để áp dụng script vào shell hiện tại thì dùng source script.sh → muốn áp dụng lâu dài thì thêm vào ~/.bashrc

  • Bash có cơ chế cache lệnh: khi di chuyển file trong $PATH có thể phát sinh ENOENT → làm mới cache bằng hash -r

  • Nếu dùng biến mà không đặt trong dấu ngoặc kép, xuống dòng sẽ bị xử lý thành khoảng trắng

  • set -e: script sẽ thoát ngay khi lỗi, nhưng bên trong biểu thức điều kiện (||, &&, if) thì không hoạt động

  • Xung đột giữa K8s livenessProbe và debugger: debugger đặt breakpoint có thể dừng toàn bộ app, làm health check không phản hồi → Pod có thể bị kết thúc

React

  • Sửa trực tiếp state trong code render
  • Dùng Hook bên trong if/loop → vi phạm quy tắc
  • Thiếu các giá trị cần thiết trong dependency array của useEffect
  • Thiếu code dọn dẹp (clean up) trong useEffect
  • Bẫy closure: bug xảy ra do capture state cũ
  • Thay đổi dữ liệu ở vị trí không phù hợp → component không thuần
  • Không dùng useCallback → gây re-render không cần thiết
  • Truyền giá trị không được memo hóa vào component đã memo → làm vô hiệu tối ưu hóa memo

Git

  • Rebase là viết lại lịch sử

    • Sau rebase, push thường sẽ bị xung đột → bắt buộc phải force push
    • Khi lịch sử của remote branch thay đổi thì pull cũng nên dùng --rebase
    • --force-with-lease trong một số trường hợp có thể ngăn ghi đè commit của người khác, nhưng nếu chỉ fetch mà không pull thì vẫn không được bảo vệ
  • Vấn đề khi revert merge

    • Revert merge cho hiệu quả không hoàn toàn → khi merge lại cùng branch thì sẽ không có thay đổi nào
    • Cách giải quyết: thực hiện revert của lần revert, hoặc dùng cách sạch hơn (backup → reset → cherry-pick → force push)
  • Các lưu ý liên quan đến GitHub

    • Dù có commit secret như API key rồi ghi đè bằng force push, GitHub vẫn còn lưu vết
    • Nếu B là fork private của repo A private, thì khi A chuyển thành public, nội dung của B cũng bị công khai (ngay cả sau khi xóa vẫn có thể truy cập)
  • git stash pop: nếu phát sinh conflict thì stash sẽ không bị drop

  • .DS_Store do macOS tự động tạo → khuyến nghị thêm **/.DS_Store vào .gitignore

Networking

  • Một số router và firewall âm thầm ngắt kết nối TCP nhàn rỗi → có thể làm vô hiệu connection pool của HTTP client và DB client → cách xử lý: cấu hình TCP keepalive
  • Kết quả traceroute độ tin cậy thấp → trong một số trường hợp tcptraceroute hữu ích hơn
  • TCP slow start có thể làm tăng độ trễ → có thể khắc phục bằng cách tắt tcp_slow_start_after_idle
  • Vấn đề sticky packet của TCP: thuật toán Nagle làm chậm việc gửi packet → có thể khắc phục bằng cách bật TCP_NODELAY
  • Khi đặt backend phía sau Nginx cần cấu hình tái sử dụng kết nối → nếu không cấu hình, trong môi trường tải cao có thể thiếu cổng nội bộ và gây lỗi kết nối
  • Nginx mặc định buffer packet → gây trễ cho SSE(EventSource)
  • Chuẩn HTTP không cấm body trong request GET và DELETE → một số nơi có dùng body nhưng nhiều thư viện và server không hỗ trợ
  • Có thể host nhiều website trên cùng một IP → việc phân biệt do header HTTP HostSNI của TLS đảm nhiệm → có những site không thể truy cập chỉ bằng IP đơn thuần
  • CORS: khi request sang origin khác, trình duyệt sẽ chặn việc truy cập response → cần cấu hình header Access-Control-Allow-Origin ở server
    • Nếu có kèm gửi cookie thì cần cấu hình bổ sung
    • Nếu frontend và backend cùng domain và cổng thì không có vấn đề CORS

Other

  • Lưu ý về YAML

    • YAML nhạy cảm với khoảng trắngkey:value là lỗi, key: value mới đúng
    • Mã quốc gia NO nếu viết không có dấu ngoặc kép có thể bị diễn giải thành false
    • Git commit hash nếu viết không có dấu ngoặc kép có thể bị chuyển thành số
  • Vấn đề CSV trong Excel

    • Khi mở CSV, Excel sẽ tự động chuyển đổi
      • Chuyển đổi ngày tháng: 1/2, 1-22-Jan
      • Chuyển đổi sai với số lớn: 1234567890123456789012345678901234500000
    • Nguyên nhân là Excel nội bộ xử lý số bằng floating point
    • Đã từng có trường hợp tên gene SEPT1 bị thay đổi sai vì vấn đề này

1 bình luận

 
GN⁺ 2025-08-18
Ý kiến Hacker News
  • Một số router và firewall có thể âm thầm ngắt kết nối TCP nhàn rỗi mà không gửi bất kỳ tín hiệu nào cho ứng dụng; vì vậy, các đoạn mã duy trì TCP connection pool như thư viện HTTP client hay database client có thể gặp tình trạng kết nối bị vô hiệu hóa mà không có cảnh báo. Để xử lý, có thể cấu hình TCP keepalive ở mức hệ thống hoặc, với HTTP, dùng các header Connection: keep-alive, Keep-Alive: timeout=30, max=1000. Khi một TCP connection đã được thiết lập thì router ở giữa không giữ trạng thái; vấn đề nằm ở timeout của firewall hoặc phiên NAT. Lúc này cũng sẽ không có gói RST nào quay về. Trong môi trường K8s, đã từng gặp trường hợp cấu hình module conntrack đặt quá thấp. Dù dùng HTTP Keep-Alive thì nó chỉ giúp tái sử dụng kết nối chứ không duy trì kết nối trên mạng(link giải thích); HTTP Keep-Alive thực ra không phát sinh packet mà chỉ trì hoãn việc đóng kết nối, còn TCP Keep-Alive thì định kỳ phát sinh packet để reset timer

    • TCP Keep-Alive có thể không hoạt động tốt với thiết bị di động; mobile OS chỉ có thể theo dõi hoặc quản lý riêng các gói keep-alive phát sinh ở tầng ứng dụng. Nhưng TCP Keep-Alive hoạt động bên dưới tầng ứng dụng nên đôi khi có thể bị vô hiệu hóa, trong khi ứng dụng vẫn vẫn có thể truy cập được
  • Một method trả về Optional<T> vẫn có thể trả về null; cách làm này quá dễ gây nhầm lẫn. Nếu còn đủ bình tĩnh thì có lẽ đã đề xuất một JEP đưa vào annotation như @java.lang.NonNullReference. Nếu khai báo kiểu bằng annotation này thì muốn compiler xử lý việc gán null như lỗi; ví dụ Alpha thì có thể gán null, còn Beta thì báo lỗi. Cũng có cảm giác cần phải xem kỹ đặc tả xem dead code elimination trong javac thực sự hoạt động ra sao; chẳng hạn trong if (true), phần b = null thực tế có thể bị elided, và về mặt đặc tả có lẽ vẫn là code hợp lệ

    • Trong Kotlin thì trường hợp này đã là lỗi biên dịch rồi, không cần thêm annotation làm gì

    • Cũng hơi nghi ngờ việc có thật sự cần Optional<T> trong một ngôn ngữ vốn đã có null; giống như trong Python, giá trị trả về của hàm chỉ cần là T | None chứ không phải một object Optional riêng. Nếu cuối cùng vẫn phải kiểm tra thì khác biệt của framework không còn rõ ràng; trừ khi dùng kiểu monad đặc biệt, còn không thì việc kiểm tra rốt cuộc vẫn như nhau

  • Có người nói Java, C#, JS mã hóa string trong bộ nhớ theo kiểu UTF-16, nhưng với Java thì không đúng; C#, JS có thể cũng vậy. Miễn là kiểu string của ngôn ngữ đủ opaque thì cách biểu diễn trong bộ nhớ là chi tiết triển khai, và với Java điều đó càng rõ từ phiên bản 9 trở đi(JEP liên quan). Đây cũng là lý do khi có FFI thì việc thay đổi chi tiết triển khai trở nên khó hơn. Ngoài ra, về số trong JS, có người nói max accurate integer là 2^53−1, nhưng thực ra vẫn có thể biểu diễn chính xác những số nguyên lớn hơn như 2^100. Ý nghĩa của 2^53−1 là n-1, n, n+1 đều được biểu diễn chính xác trong IEEE double; vì thế n == n-1n == n+1 đều cho ra false

    • C# có biểu diễn trong bộ nhớ khá cố định; nhiều trường hợp truy cập trực tiếp buffer bằng ReadOnlySpan<char> hoặc raw char*, nên char là kiểu code point UTF-16. Còn JS thì có lẽ vẫn còn cách lách được

    • Thay vì nói max accurate integer thì thích dùng max safe integer hơn

    • (liên quan đến Base64 encoding) Về chuyện string trong bộ nhớ của Java, C#, JS không phải kiểu UTF-16, điều đó có thể đúng về mặt kỹ thuật, nhưng trên thực tế vẫn có những trường hợp phát sinh vấn đề do biểu diễn UTF-16 của Java, ví dụ khi string từ một ngôn ngữ dựa trên UTF-8 được Base64-encode rồi decode trong Java

  • Có cảm giác các tip hay thông tin kiểu manual chỉ giúp hiểu nhanh hơn những gì mình đã biết hoặc gần như đã biết sẵn. Phần lớn manual hiệu quả cho việc hệ thống hóa và ôn lại hơn là cho chính việc học, nên khá kém hiệu quả nếu muốn dạy cho người hoàn toàn chưa biết

    • Bản chất của manual là để lưu lại ghi chép thay vì chỉ phụ thuộc vào trí nhớ; phần lớn manual Unix đều theo dạng đó. Ta thường tìm manual khi đã biết phần mềm đó làm gì nhưng quên chi tiết cách dùng. Ngược lại, người mới hoàn toàn cần tutorial hoặc guide để nắm khái niệm. Manual giống như thứ chuẩn bị trước để có thể đặt câu hỏi tốt hơn
  • Đã đọc bài “Traceroute Isn’t Real” và thấy thật sự rất thú vị. Từ trước đến giờ vẫn cảm nhận dữ liệu traceroute thường rất thiếu chính xác hoặc gần như vô nghĩa, nên biết được lý do như vậy khá hữu ích(link gốc). Nếu ai có thông tin cập nhật hơn thì rất mong được chia sẻ

  • Bài này đúng hơn là một danh sách các mẹo nhỏ mà tác giả học được từ kinh nghiệm, chứ không hẳn là các cạm bẫy hay bẫy thật sự. Nhiều nội dung chỉ áp dụng trong những ngữ cảnh rất hẹp nhưng lại không nói rõ ngữ cảnh, và một số chỗ còn có vẻ sai. Vì vậy không cần tiếp nhận toàn bộ bài viết quá nguyên xi; xem nó như một dòng suy nghĩ hay vài ghi chú thì hợp hơn

  • Chuyện giá trị tham số mặc định trong Python không phải được tạo mới ở mỗi lần gọi mà là giá trị đã lưu sẵn, đây là điểm nhất định phải biết khi dùng biến datetime

    • Tôi không phải kiểu lập trình viên dùng Python suốt ngày, nhưng tuần này đã khổ sở khá nhiều vì chuyện giá trị tham số mặc định bị lưu lại. Tôi định nếu không truyền set vào thì sẽ gán một set rỗng, nhưng vì set đó bị tái sử dụng nên sinh ra bug. Mất khá lâu mới lần ra nguyên nhân
  • Ở “bẫy” đầu tiên của trang, có nói min-width: auto sẽ quyết định độ rộng tối thiểu theo nội dung, nhưng nếu không phải flex/grid thì điều đó thực ra không đúng. Theo MDN, với block, inline, table v.v. thì auto sẽ được quy về 0(tài liệu chính thức)

    • Bẫy đầu tiên thật sự là “không thể đọc riêng lẻ bất kỳ thuộc tính CSS nào”. Đúng như cái tên cascading, giá trị mặc định và kết quả của nhiều rule sẽ được gộp lại ở đâu đó, nên ngữ cảnh của toàn bộ tài liệu là rất quan trọng

    • Phần cascade của CSS text properties thì còn có thể hiểu được phần nào, nhưng CSS layout thì quá khó hiểu từ góc nhìn của nhà thiết kế trang, người triển khai lẫn người dùng. Thật sự khó đồng cảm nổi là nó được thiết kế cho ai

  • Nhìn chung là một bài list khá ổn, có vài ý kiến thêm.

    • Về Han unification trong Unicode, chuyện cùng một ký tự mang cùng một code point ở nhiều ngôn ngữ rồi được hiển thị khác nhau theo font không phải là một cái bẫy. Những chữ Hán như ví dụ được dùng gần như giống nhau ở cả Trung và Nhật, và người dùng của cả hai ngôn ngữ đều nhận ra các biến thể khác nhau đó là cùng một khái niệm ký tự. Tác giả đang nói như thể chữ A phải được định nghĩa khác nhau trong tiếng Anh và tiếng Pháp, nhưng thực tế không phải vậy. Xem mục Han unification
    • -0.0+0.0 (âm 0, dương 0) được coi là bằng nhau trong so sánh số thực dấu chấm động, nhưng vẫn có cách phân biệt; có thể kiểm tra bằng bit pattern hoặc qua kết quả 1.0/-0.0 = -vô cực, 1.0/0.0 = +vô cực
    • Rất đồng tình với lời khuyên nên đặt timezone của server là UTC; dùng UTC cho server, log, lưu ảnh và mọi nơi có giá trị lưu trữ lâu dài hoặc cần timestamp chính xác, còn giờ địa phương chỉ nên dùng cho giao tiếp
    • Có nói rằng với số nguyên nên dùng low + (high - low) / 2 thay vì (low + high) / 2 để tránh overflow, nhưng nếu low hoặc high có thể là số âm thì có khi chỉ là chuyển vùng overflow sang chỗ khác. Đây là vấn đề quan trọng trong binary search nói chung
    • Việc dùng đúng kiểu số nguyên và phép toán trong C/C++ cũng là một cái bẫy lớn, xem hướng dẫn liên quan
    • Có nói rebase có thể làm thay đổi history, nhưng rebase vốn dĩ là command dùng để rewrite history
    • Dù người dùng của cả hai ngôn ngữ đều nhận ra về mặt khái niệm đó là cùng một ký tự, nhưng cũng không nên đơn giản coi nó chỉ là “biến thể font”. Chỉ vì Unicode code point giống nhau không có nghĩa là có thể thay thế ký tự một cách an toàn; với người dùng Nhật, kiểu thay thế như vậy thậm chí có thể là lý do từ chối dùng sản phẩm

    • Thực ra tiếng Anh A và tiếng Pháp A không cần phân biệt, nhưng như А (chữ Kirin) và A (chữ Latin) thì dù nhìn giống nhau vẫn là các code point khác nhau. Han unification cũng thường gom cả những ký tự có hình dáng khá khác nhau vào một nhóm, nên người học tiếng Nhật hoặc tiếng Trung hoàn toàn có thể thấy rối thật. Ví dụ chữ '喝' (drink) có thể hiển thị khá khác nhau như ở link; thậm chí chỉ cần copy thôi mà hình dạng cũng có thể đổi ngay, cho thấy cách xử lý phức tạp đến mức nào. Han unification thực tế là một chủ đề khá đau đầu

  • Có nhắc đến sự khác biệt tinh vi giữa numpypytorch, nhưng nếu không có ví dụ cụ thể thì không mấy hữu ích và cũng thiếu thông tin, nên chưa thấy đây là một cạm bẫy thực tế