Insights
Today I Learned
บันทึกสั้น ๆ ที่ใช้ได้จริงจากทีม Haliviq เป็นบทเรียนเล็ก ๆ จากงานวิศวกรรมและดีไซน์ในแต่ละวัน แต่ละเรื่องคือสิ่งที่เราพบระหว่างลงมือทำ พร้อมอธิบายว่าทำไมถึงเกิดขึ้น และตอนนี้เราแก้อย่างไร เลือกกรองตามหัวข้อได้ ทั้ง React, Next.js, PostgreSQL, Docker, Figma, security และอื่น ๆ
ใช้ useDeferredValue แทน debounce กับ list ที่ re-render หนัก
debounce แค่หน่วงเวลาอัปเดต พอถึงเวลา render หนัก ๆ ก็ยังทำให้หน้าค้างอยู่ดี แต่ useDeferredValue ให้ React หยุด render ตรงกลางได้เมื่อมีการพิมพ์ตัวใหม่เข้ามา พิมพ์เลยลื่นแม้บนตาราง 5,000 แถว เหมาะกับกรณีที่ช่องค้นหาคุม list ใหญ่ ๆ และเราไม่อยากตั้งเวลารอแบบเดาเอา
อ่านโน้ตkey={index} ทำให้ exit animation ของ Framer Motion พังโดยไม่รู้ตัว
เวลาเรียงลำดับหรือลบ item ใน list ที่ใช้ index เป็น key React จะจับคู่ node เก่ากับ item ใหม่ผิดตัว animation ตอนลบเลยไปเล่นที่ element ผิดอัน ให้ใช้ id ที่คงที่จากข้อมูลเป็น key แทน ไม่เสียอะไรเลย และช่วยแก้บั๊กแปลก ๆ เรื่อง focus ของช่องกรอกและ state ใน list เดียวกันด้วย
อ่านโน้ตuseEffect cleanup ทำงาน 2 รอบใน Strict Mode ไม่ใช่บั๊กที่ต้องแก้
ตอนพัฒนา React 18 จะ mount, unmount แล้ว mount ซ้ำให้ตั้งใจ เพื่อจับ effect ที่รันซ้ำแล้วให้ผลไม่เหมือนเดิม ถ้ารันสองรอบแล้วพัง เช่น subscribe ซ้ำหรือยิง request สองครั้ง แสดงว่า effect นั้นไม่ปลอดภัยอยู่แล้ว ให้แก้ cleanup ไม่ใช่ปิด Strict Mode
อ่านโน้ตServer Components render ใหม่ทุกครั้งที่เปลี่ยนหน้า ไม่ใช่แค่ตอน mount
เราเคยเข้าใจว่าผลลัพธ์ของ Server Component ถูก cache ข้ามหน้า แต่จริง ๆ Next.js ดึงใหม่ทุกครั้งที่กดลิงก์เปลี่ยนหน้า เว้นแต่เราตั้งค่า caching เอง ถ้าหน้าไหนช้าหลังกดลิงก์ ให้ดูว่า server component ดึงอะไรอยู่ แล้วตัดสินใจให้ชัดว่าอะไรควร cache และนานแค่ไหน
อ่านโน้ตMulti-stage build ลดขนาด Docker image ลง 71%
การ copy node_modules จาก build stage ตรง ๆ ทำให้ compiler และเครื่องมือสำหรับพัฒนาติดมาด้วย ทั้งที่ตอนรันจริงไม่มีใครใช้ พอเปลี่ยนเป็น multi-stage build ที่ใช้ COPY --from เอาเฉพาะไฟล์ที่ build แล้วกับ dependency สำหรับ production image ก็ลดจาก 1.2GB เหลือ 340MB image เล็กลงจึง pull เร็วขึ้นและมีแพ็กเกจให้ต้องอัปเดตแพตช์น้อยลง
อ่านโน้ตเครื่องมือเช็ก color contrast ให้ผลไม่ตรงกับข้อความที่ใช้ gradient
เครื่องมือ WCAG ส่วนใหญ่เช็กแค่สีเดียว หัวข้อที่ใช้ gradient จึงผ่านการตรวจ แต่ปลายฝั่งสีอ่อนอ่านยากสำหรับคนจริง ๆ ให้ทดสอบทั้งจุดที่อ่อนที่สุดและเข้มที่สุดของ gradient กับสีพื้นหลัง หรือเลี่ยง gradient กับข้อความเล็กและข้อความสำคัญไปเลย
อ่านโน้ตPartial index ทำให้ query ที่ใช้ soft-delete เร็วขึ้น 40 เท่า
เรา index แถวที่ถูก soft-delete ไว้กว่า 2 ล้านแถว ทั้งที่แอปไม่เคย query ถึง พอใช้ partial index แบบ WHERE deleted_at IS NULL มันครอบคลุมเฉพาะแถวที่ยังใช้งาน index เลยเล็กลงและอยู่ใน memory ได้ เวลา query ลดจาก 800ms เหลือ 20ms ตารางไหนที่ query ส่วนใหญ่ไปแตะแค่แถวกลุ่มเล็ก ๆ ที่ระบุได้ชัด ก็น่าลองวิธีนี้
อ่านโน้ตdebounce ช่องค้นหาอย่างเดียวไม่พอ ต้องยกเลิก request ด้วย
ต่อให้ debounce แล้ว response ที่ช้าอาจมาถึงหลัง response ใหม่กว่า แล้วเขียนทับด้วยผลเก่า การสร้าง AbortController ต่อ request และยกเลิกอันก่อนหน้า ช่วยแก้บั๊กผลค้นหาเก่ากะพริบขึ้นมาของคนที่พิมพ์เร็ว และยังช่วยให้ server ไม่ต้องทำงานที่ไม่มีใครเห็นผลจนจบ
อ่านโน้ตQuick Look ยังโชว์ thumbnail เก่าหลังไฟล์ถูกเขียนทับ
ใช้ qlmanage -r เพื่อล้าง cache ถ้าไม่ล้าง PNG ที่ export ใหม่อาจยังโชว์ preview ของเมื่อวานใน Finder อยู่หลายชั่วโมง ซึ่งทำให้งงตอนตรวจไฟล์ดีไซน์หรือส่งมอบ asset
อ่านโน้ตการเปลี่ยน API key ที่รั่วไม่เท่ากับการยกเลิก session
session ที่เซ็นด้วย key เก่ายังใช้ได้อีก 24 ชั่วโมงหลังเราเปลี่ยน key ผู้ไม่หวังดีจึงยังใช้ได้อยู่ เช็กลิสต์รับมือเหตุการณ์ของเรามีขั้นตอนยกเลิก session และ token ที่ยังเปิดอยู่เสมอ นอกจากเปลี่ยน key แล้ว ยังไล่ดู log ด้วยว่ามีใครใช้ key เก่าอยู่หรือไม่
อ่านโน้ตrevalidatePath ไม่ได้ล้าง router cache ฝั่ง client
ข้อมูลบน server ใหม่แล้ว แต่ผู้ใช้ยังเห็นข้อมูลเก่านานถึง 30 วินาที เพราะ browser มี router cache ของตัวเอง ซึ่ง revalidatePath ไปไม่ถึง ถ้าความสดของข้อมูลสำคัญ เช่น หลังส่งฟอร์ม ให้สั่ง refresh router ฝั่ง client ด้วย และทดสอบโดยคลิกเปลี่ยนหน้าจริง ๆ ไม่ใช่แค่ reload
อ่านโน้ตsatisfies เก็บ literal type ไว้ และตรวจ shape ไปพร้อมกัน
ถ้าเราระบุ type ให้ตัวแปรตรง ๆ type จะกว้างขึ้นและเสีย literal value จริงไป ส่วน satisfies ตรวจ object ตาม type แต่ยังเก็บ inference แบบแคบไว้ เหมาะกับ config หรือ route map ที่อยากได้ทั้ง autocomplete ของ key จริงและการตรวจ shape ให้ถูกต้อง
อ่านโน้ตEXPLAIN ANALYZE ให้ตัวเลขไม่ตรงเรื่อง buffer cache ในการรันครั้งแรก
การรันครั้งแรกต้องดึงข้อมูลที่ยังไม่อยู่ใน cache จาก disk ตัวเลขเลยดูช้ากว่าที่ผู้ใช้จริงจะพบ ให้รันสองรอบเสมอแล้วดูรอบที่สอง และใส่ BUFFERS เพื่อดูว่าอ่านจาก cache เท่าไร การเอาผลรันแรกมาเทียบกับรันหลังเป็นสาเหตุที่ทำให้คนประเมินผิดว่า index ช่วยหรือไม่
อ่านโน้ตline-height ภาษาไทยควรอยู่ที่ราว 1.7 ไม่ใช่ 1.5 แบบภาษาอังกฤษ
ตัวอักษรไทยมีสระและวรรณยุกต์ซ้อนทั้งบนและล่างบรรทัด line-height 1.5 ที่ใช้ได้กับภาษาอังกฤษจึงทำให้สระโดนตัดหรือไปชนบรรทัดถัดไป พอปรับ body text เป็นราว 1.7 ปัญหาสระโดนตัดก็หายไปทั้งเว็บไซต์ ให้ทดสอบกับคำที่มีสระและวรรณยุกต์ซ้อนกัน และเช็กหัวข้อแยกต่างหาก เพราะหัวข้อมักใช้ค่าน้อยกว่าได้
อ่านโน้ตOptional chaining ซ่อนคำสะกดผิดของชื่อ property จาก compiler
ถ้า type หลวมหรือเป็น any คำว่า user?.emial จะคืนค่า undefined เงียบ ๆ แทนที่จะ error คำสะกดผิดเลยหลุดไปถึงระบบจริงโดยไม่มีใครเห็น ให้ type ข้อมูลให้ถูกต้องเพื่อให้ compiler เตือนได้ และถ้าพบ optional chaining เต็มโค้ดไปหมด อาจแปลว่าโครงข้อมูลยังไม่ชัด
อ่านโน้ต.dockerignore ไม่มีผลกับ COPY --from จาก stage อื่น
เราพบไฟล์ .env หลุดเข้า image สุดท้ายซ้ำ ๆ เพราะ .dockerignore กรองแค่ build context ที่ส่งให้ Docker ไม่ได้กรองไฟล์ที่ copy ข้าม stage ให้ระบุชัดว่าจะ copy อะไรใน stage หลัง ๆ อย่า copy ทั้งโฟลเดอร์ และเปิดดู image สุดท้ายเพื่อเช็กว่าไม่มี secret ติดอยู่
อ่านโน้ตAuto layout แบบ 'Hug contents' พังเงียบ ๆ เมื่อข้อความว่าง
frame ที่ตั้งเป็น hug จะหดเหลือความกว้าง 0 ทันทีที่ข้อความที่ผูกไว้ว่างเปล่า และ layers panel ไม่เตือนอะไรเลย ให้ตั้งความกว้างขั้นต่ำให้ component หรือออกแบบ empty state ไว้ชัด ๆ เวลาข้อมูลจริงขาดค่าไป element จะได้ไม่หายไปเฉย ๆ
อ่านโน้ตfzf + ripgrep เร็วกว่า grep -r เมื่อทำงานใน monorepo
rg --files | fzf ใช้กระโดดไปไฟล์แบบ fuzzy และเอา rg ไปครอบด้วย fzf ที่มี preview จะได้การค้นเนื้อหาแบบเห็นผลทันที ใน monorepo ใหญ่ ๆ การค้นหาของเราเร็วจนแทบไม่ต้องรอ เหตุผลหลักคือ ripgrep เคารพ .gitignore และข้ามโฟลเดอร์ build ให้เองตั้งแต่ต้น
อ่านโน้ตมีโปรเจกต์ในใจแล้วใช่ไหม?
เราอยากฟังว่าคุณกำลังสร้างอะไรอยู่
บันทึกเหล่านี้มาจากงานออกแบบและพัฒนาที่ทีมเราทำกันทุกวันในกรุงเทพฯ ถ้าคุณกำลังวางแผนทำ website แอป หรือระบบ AI และอยากได้ทีมที่ใส่ใจรายละเอียดแบบนี้ เล่าให้เราฟังได้เลย