Những cạm bẫy mà lập trình viên cần cẩn trọng
(qouteall.fun)- 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-widthmặc định làautomin-width: autođược quyết định bởi kích thước nội dung và được ưu tiên hơnflex-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: autocố gắng lấp đầy không gian của phần tử cha, cònheight: autothì khớp theo nội dungwidth: autocủa các phần tử inline, inline-block, float sẽ không giãn ramargin: 0 autocăn giữa theo chiều ngang, cònmargin: auto 0không thể căn giữa theo chiều dọc (tuy nhiên trongflex-direction: columnthì 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-rlthì hành vi cũng đảo ngược
-
Block Formatting Context (BFC)
- Tạo BFC bằng
display: flow-root(ngoài raoverflow: hidden/auto/scroll,display: tablev.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ó
borderhoặcpaddingthì sẽ không xảy ra gộp margin
- Tạo BFC bằng
-
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: fixedhoặcsticky- 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ác thuộc tính render như
- Đặc điểm
z-indexchỉ có hiệu lực bên trong stacking context- Tọa độ của
position: absolute/fixedlấy phần tử tổ tiên gần nhất có định vị làm mốc stickykhông hoạt động vượt qua stacking context- Ngay cả
overflow: visiblecũng bị cắt bởi stacking context background-attachment: fixedđược bố trí dựa trên stacking context
- Các điều kiện tạo stacking context mới
-
Đơ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ị
100vhsẽ thay đổi - Cách giải quyết hiện đại: dùng
100dvh
- 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ị
-
Mốc tham chiếu của Absolute Position
position: absolutekhông lấy phần tử cha làm mốc, mà lấy tổ tiên gần nhất córelative/absolutehoặc tổ tiên tạo stacking context làm chuẩn
-
Cách hoạt động của Blur
backdrop-filter: blurkhô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à
flexhoặcgridthìfloatcủa phần tử con không có tác dụng
- Nếu phần tử cha là
-
Đơ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: inlinebỏ quawidth,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-blocksẽ đượ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
- Giá trị mặc định là
-
Cumulative Layout Shift
- Nếu không chỉ định thuộc tính
widthvàheightcho<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
- Nếu không chỉ định thuộc tính
-
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
- Với trường hợp như
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::stringkhô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
- Rust: chuỗi nội bộ dùng UTF-8,
-
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 characters và invisible 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 == NaNluôn là false) NaN != NaNluô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
- NaN không bằng bất kỳ giá trị nào, kể cả chính nó (
-
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.stringifychuyển NaN, Inf thànhnull - 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 sinhValueError
- Khi dùng tùy chọn
- Golang
json.Marshaltrả về lỗi nếu tồn tại NaN/Inf
- JS
- Chuẩn JSON không cho phép NaN và 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
BigIntcho 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.parsecó 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 đề
- Phạm vi số nguyên an toàn là
- 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
-
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
equalsvàhashcode, 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ềArrayListmutable hoặcCollections.emptyList()immutable; sửa đối tượng sau sẽ gây raUnsupportedOperationException - 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
returntrong khốifinally, ngoại lệ phát sinh ởtryhoặccatchsẽ bị bỏ qua và giá trị trả về củafinallysẽ đượ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ụ
scheduleAtFixedRatesẽ âm thầm dừng khi phát sinh ngoại lệ
- Tác vụ
- 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 chadeferđược thực thi khi hàm return, không phải khi kết thúc block scopedefercapture 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ọic_str()sẽ nguy hiểm- Khi sửa container trong lúc lặp, iterator sẽ bị mất hiệu lực
std::removekhông thực sự xóa mà chỉ sắp xếp lại phần tử, muốn xóa cầnerase- 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ằngmemcpy - 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
memcpyhoặcstd::bit_castcho ép kiểu cưỡng bức
- Ngoại lệ: 1) kiểu có quan hệ kế thừa 2) chuyển đổi qua
- 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 = nullkhông hoạt động, phải dùngx 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 distinctkhác nhau tùy DB count(x)và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
utf8mb4thì 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 byvà cộtselectkhông khớp thì có thể trả về kết quả không xác định - Trong SQLite, nếu không bật
strictthì 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
mysqldumpkhông có tùy chọn--single-transactionthì sẽ đặt read lock lên toàn bộ bảng - Trong PostgreSQL,
create unique indexhoặcalter table ... add foreign keysẽ 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 validrồivalidate constraint
- Cách tránh: dùng
-
Truy vấn Range
- Phạm vi không chồng lấp:
- Điều kiện đơn giản
p >= start and p <= endkhông hiệu quả (kể cả khi có chỉ mục tổng hợp) - Cách hiệu quả:
(chỉ cần chỉ mục trên cột start)select * from (select ... from ranges where start <= p order by start desc limit 1) where end >= p
- Điều kiện đơn giản
- 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
- Phạm vi không chồng lấp:
Concurrency and Parallelism
-
volatile
volatilekhô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++:
volatilechỉ ngăn một phần tối ưu hóa, không thêm memory barrier - Java: truy cập
volatilecung cấp sequentially-consistent ordering (JVM sẽ chèn memory barrier nếu cần) - C#: truy cập
volatilecung 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 updaterồ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
- Giải pháp:
- MySQL(InnoDB): ở mức repeatable read, sau
- 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:
-
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
- Với
-
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] * 10không phải là cách tạo mảng 2D đúng (low + high) / 2có 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,
pwdlà đườ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
DIRchưa được set thìrm -rf $DIR/→ có nguy cơ chạyrm -rf /→ có thể phòng tránh bằngset -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
$PATHcó thể phát sinhENOENT→ làm mới cache bằnghash -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-leasetrong 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_Storedo macOS tự động tạo → khuyến nghị thêm**/.DS_Storevà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ợptcptraceroutehữ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
Hostvà SNI 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ắng →
key:valuelà lỗi,key: valuemới đúng - Mã quốc gia
NOnếu viết không có dấu ngoặc kép có thể bị diễn giải thànhfalse - Git commit hash nếu viết không có dấu ngoặc kép có thể bị chuyển thành số
- YAML nhạy cảm với khoảng trắng →
-
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-2→2-Jan - Chuyển đổi sai với số lớn:
12345678901234567890→12345678901234500000
- Chuyển đổi ngày tháng:
- 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
- Khi mở CSV, Excel sẽ tự động chuyển đổi
Chưa có bình luận nào.