66 điểm bởi GN⁺ 2025-08-18 | Chưa có bình luận nào. | 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
    • <pre> 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 <a> 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 <img>, 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ư <script>console.log('</script>')</script>, thẻ </script> đầ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<T> 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<T> 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 <= end không hiệu quả (kể cả khi có chỉ mục tổng hợp)
      • Cách hiệu quả:
        select *   
        from (select ... from ranges where start <= p order by start desc limit 1)   
        where end >= p  
        
        (chỉ cần chỉ mục trên cột start)
    • Phạm vi có thể chồng lấp:
      • Chỉ mục B-tree thông thường sẽ kém hiệu quả
      • Khuyến nghị dùng spatial index trong MySQL, GiST trong PostgreSQL

Concurrency and Parallelism

  • volatile

    • volatile không thể thay thế lock và không cung cấp atomicity
    • Dữ liệu đã được bảo vệ bằng lock thì không cần volatile (lock đã đảm bảo memory order)
    • C/C++: volatile chỉ ngăn một phần tối ưu hóa, không thêm memory barrier
    • Java: truy cập volatile cung cấp sequentially-consistent ordering (JVM sẽ chèn memory barrier nếu cần)
    • C#: truy cập volatile cung cấp release-acquire ordering (CLR sẽ chèn memory barrier nếu cần)
    • Có thể ngăn các tối ưu hóa sai liên quan đến việc sắp xếp lại thao tác đọc/ghi bộ nhớ
  • Vấn đề TOCTOU (Time-of-check to time-of-use)

  • Xử lý ràng buộc ở tầng ứng dụng với SQL DB

    • Khi ép buộc các ràng buộc không thể biểu diễn bằng unique index đơn giản (ví dụ: unique giữa hai bảng, unique có điều kiện, unique trong một khoảng thời gian) ở phía ứng dụng:
      • MySQL(InnoDB): ở mức repeatable read, sau select ... for update rồi insert, nếu cột unique có index thì vẫn hợp lệ nhờ gap lock (nhưng gap lock có thể gây deadlock khi tải cao → cần deadlock detection và retry)
      • PostgreSQL: cùng logic ở mức repeatable read là không đủ trong môi trường đồng thời (vấn đề write skew)
        • Giải pháp:
          • Dùng serializable isolation level
          • Dùng ràng buộc DB thay vì xử lý ở ứng dụng
            • unique có điều kiện → partial unique index
            • unique giữa hai bảng → chèn dữ liệu trùng sang một bảng riêng rồi đặt unique index
            • tính loại trừ theo khoảng thời gian → range type + exclude constraint
  • Atomic reference counting

    • Với Arc, shared_ptr, nếu nhiều luồng thường xuyên thay đổi cùng một bộ đếm thì hiệu năng sẽ giảm
  • Read-write lock

    • Một số cách triển khai không hỗ trợ nâng cấp từ read lock lên write lock
    • Nếu đang giữ read lock mà lại thử lấy write lock thì có thể xảy ra deadlock

Common in many languages

  • Bỏ sót kiểm tra Null/None/nil là nguyên nhân lỗi phổ biến
  • Khi sửa container trong lúc lặp, có thể phát sinh tranh chấp dữ liệu trong luồng đơn
  • Chia sẻ dữ liệu có thể thay đổi một cách sai lầm: ví dụ, trong Python [[0] * 10] * 10 không phải là cách tạo mảng 2D đúng
  • (low + high) / 2 có thể bị overflow → cách an toàn là low + (high - low) / 2
  • Đánh giá ngắn mạch (short circuit): a() || b() thì nếu a là true sẽ không chạy b, a() && b() thì nếu a là false sẽ không chạy b
  • Mặc định profiler chỉ bao gồm CPU time → thời gian chờ DB v.v. không xuất hiện trong flamegraph, dễ gây hiểu nhầm
  • Dialect của regular expression khác nhau tùy ngôn ngữ → regex chạy trong JS có thể không chạy trong Java

Linux and bash

  • Sau khi chuyển thư mục, pwd là đường dẫn gốc, còn đường dẫn thực là pwd -P
  • cmd > 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

Chưa có bình luận nào.

Chưa có bình luận nào.