Videos Web App Optimization at EarnIn

Description

เล่าประสบการณ์จริงในการ Optimize EarnIn Web App ด้วยเทคนิคต่างๆ

Chapters

  • แนะนำตัวและหัวข้อการบรรยาย 0:00
  • ความเป็นมาและความสำคัญของการ Optimize Web App 1:25
  • เมตริกซ์ Web Vitals และ LCP 2:36
  • เครื่องมือในการวัดผลและตรวจสอบประสิทธิภาพ 3:21
  • วิธีการปรับปรุง FCP และ LCP 5:05
  • การใช้ Lazy Loading ใน React 8:46
  • การจัดการกับ Third-party Scripts 13:13
  • การใช้ Partytown เพื่อ Optimize Third-party Scripts 15:18
  • ผลลัพธ์จากการ Optimize และปัญหาที่พบ 17:50
  • แผนการพัฒนาในอนาคตและข้อจำกัด 20:58
  • คำถาม-ตอบเกี่ยวกับการ Optimize และการจัดการ Dependencies 22:33
  • การทำ Regression Testing และ CI/CD Pipeline 24:38
  • การใช้ Lighthouse และ Real User Monitoring 27:05

Transcript

คำบรรยายต่อไปนี้อาจไม่ถูกต้องทั้งหมด หากคุณพบข้อผิดพลาดใดๆ คุณสามารถช่วยแก้ไขข้อผิดพลาดได้บน GitHub

แนะนำตัวและหัวข้อการบรรยาย0:00

ก็เซชชั่นนี้นะครับ web app optimization นะครับ จากประสบการณ์จริงที่ EarnIn นะครับ เดี๋ยวผมขอแนะนำตัวก่อนนะครับ ผมศิวัส ขาวเหลือง นะครับ หรือว่าเจมส์นะครับ เป็นซีเนียร์ซอฟต์แวร์อินจิเนียร์อยู่ที่ EarnIn ครับ

ครับผมแม็กกี้นะครับ กฤษฎิ์โภคิน งามสมศักดิ์สกุล เป็นซีเนียร์ซอฟต์แวร์อินจิเนียร์ที่ EarnIn ครับ

อันนี้เป็นประสบการณ์จริงบน production นะครับ

แล้วก็ผมมี disclaimer นิดหน่อย ก็คือ optimization เนี่ยมันทำไปสักพักแล้ว

แล้วบางข้อมูลเนี่ยมันอาจจะ outdated เรื่องผลลัพธ์

ซึ่งโดยรวมแล้ววิธีการ เทคนิคต่างๆ ที่ใช้เนี่ย ผมคิดว่ามันยัง valid แล้วก็สามารถช่วย ประสบการณ์ผู้ใช้ได้ดีขึ้นได้อยู่ ก็แค่เป็น disclaimer ไว้ก่อนนะครับ อย่างที่ทุกคนรู้นะครับว่าการ optimize เนี่ย

มันเป็นรอบๆ ครับ ก็คือเราทำไปเรื่อย เรามี feature ใหม่ มีอะไรเพิ่มเข้ามา มี library ใหม่ เว็บเราก็ช้าลง เราก็ต้อง optimize เป็นรอบๆ ไป หรือถ้าดีหน่อยเราก็มี regression test แล้วก็เราได้เห็นปัญหาที่เกิดขึ้นเวลาเราเพิ่ม feature ครับ ก็อันนี้ก็เป็น cycle เหมือนกันที่เกิดขึ้นที่ผมได้ทำเข้าไปนะครับ ก็คือ optimize แล้วปุ๊บ มี feature ใหม่ ก็ค่อย optimize นะครับ

ความเป็นมาและความสำคัญของการ Optimize Web App1:25

ซึ่ง agenda วันนี้นะครับ ก็จะมีเท้าความ 9 เดือนที่แล้วนะครับ นิดหน่อย ไม่เป็นไร สไลด์เดียว แล้วก็จะมีเรื่องเมตริกซ์

ของ web vitals นิดหน่อยนะครับ แล้วก็เครื่องมือที่ใช้ ในการวัดผล optimize solution ต่างๆ แล้วก็ผลลัพธ์ แล้วก็มี road map ในอนาคตที่ตั้งใจจะทำด้วย จุดเริ่มต้นนะครับ 9 เดือนที่แล้ว ผมมี report มา จากทีม product ครับว่าคะแนนไม่ดีส่วนหนึ่ง

แล้วก็เวลาที่เมตริกซ์ที่ได้จาก web vitals เนี่ย ก็ไม่ค่อยดีเท่าไหร่ ก็เลยเกิดกระบวนการ optimize นี้ขึ้นนะครับ ผมก็เลยลองใช้ Lighthouse วัดนะครับ ก็คะแนนไม่ค่อยดีครับ

ซึ่งมันมี research จาก Google อยู่นะครับว่า ยิ่งเว็บเราช้าเนี่ย คนก็จะ drop ออกไป เราก็ต้องแก้ปัญหานี้เพื่อให้คนไม่ drop ออกจาก funnel นะครับ เราก็ challenge accepted ครับ ผมพร้อมจะ optimize ละ หลังจากที่เพิ่ม feature มามากมาย

เมตริกซ์ Web Vitals และ LCP2:36

ก็เริ่มต้นที่เมตริกซ์เบื้องต้นก่อนนะครับ ซึ่งผมสนใจเมตริกซ์ตัวนึงเป็นหลักนะครับ ซึ่งก็คือ LCP LCP เนี่ยเป็น Largest Contentful Paint นะครับ เป็นใช้เวลาเท่าไหร่ในการที่จะ render เห็น content ที่ใหญ่ที่สุดของหน้านะครับ ซึ่งอาจจะเป็นรูปหรือเป็นข้อความก็ได้ ซึ่งเค้าก็มีตัวเลขที่ดีอยู่นะครับ ก็คือประมาณดีก็คือน้อยกว่า 2.5 นะครับ

อันนี้ก็เป็นตัวอย่างตัวเมตริกนะครับ ถ้า FCP เนี่ย จะเห็นแค่

ข้อความแรกนะครับ ซึ่งมันก็ไม่ได้ meaningful มาก สิ่งที่ meaningful ผมมองว่าเป็น LCP นะครับ ซึ่งก็คือ Largest Contentful Paint ก็คือจะเห็นว่า user เห็น content ทั้งหลายแล้ว เหมือนพร้อมเว็บเราพร้อมที่จะใช้งานจริงๆ แล้ว

เครื่องมือในการวัดผลและตรวจสอบประสิทธิภาพ3:21

แล้วก็พูดถึงเครื่องมือในการวัดผลแล้วก็ตรวจสอบนะครับ แล้วก็มี Google Chrome DevTools ครับที่ใช้วัดผล เราจะเห็น performance ต่างๆ นะครับ แล้วก็มีตัว Lighthouse ที่อยู่ใน Google Chrome DevTools เอง ที่บอก score แล้วก็บอกปัญหาต่างๆ ที่เกิดขึ้นจากเว็บเราด้วย อันนี้ก็เป็นตัวอย่างหน้าตา Lighthouse นะครับ

ซึ่งผมมีคำแนะนำนึงครับ ก็คือเวลาเราใช้ Lighthouse วัด score เนี่ย เราควรจะใช้ Google Chrome Canary ในการวัดครับ เราจะได้ Lighthouse version ล่าสุด แล้วก็จะได้คะแนนที่ version ล่าสุดกว่า version stable นะครับ

แล้วก็มีอีกตัวนะครับ เป็น PageSpeed Insights ซึ่งก็ใช้ Lighthouse วัดเหมือนกัน แต่ก็คือ server ตัวที่วัดเนี่ย อาจจะไม่ได้อยู่ที่ไทย แต่อยู่ที่อเมริกาในการไป call เว็บเรานะครับ ก็อาจจะมี implication ไม่เหมือนกับเว็บที่ไทยเนาะ เพราะว่า user จริงมันไม่ได้อยู่ ถ้าเว็บเราเป็นเว็บไทย แล้วเค้าใช้ เราใช้ Lighthouse วัดเนี่ย อาจจะมี latency ที่มันไม่เหมือนเราเปิดที่ไทย แล้วก็มี tool อีกตัวนึงที่ใช้ดูตัว JavaScript ได้นะครับ ก็คือ Source Map Explorer ก็สามารถเห็น bundle size ของเรานะครับ มัน tool ที่ใกล้เคียงกัน ซึ่งก็คือ Webpack Bundle Analyzer ครับ มันดูเรื่อง JavaScript เหมือนกัน แต่ถ้าไปดูใน GitHub thread อะไรเงี้ยครับ เค้าบอกว่า ตัวเนี้ย จะเป็นตัวที่ Source Map Explorer เนี่ย จะเป็นตัวที่แม่นยำมากกว่าตัว Webpack Bundle Analyzer นะครับ คือ Webpack Bundle Analyzer เนี่ย เอาไว้ดูคร่าวๆ ได้ แต่ถ้าเกิดอยากดูเป๊ะๆ เนี่ย อาจจะใช้ตัว Source Map Explorer ดีกว่า แล้วก็ไปต่อ

วิธีการปรับปรุง FCP และ LCP5:05

ครับ วิธีการที่เราจะ improve ตัว FCP หรือ LCP นะครับ ส่วนใหญ่ของตัวฝั่ง JavaScript เนี่ย คือวิธีการที่เรา ต้องลดขนาดของ bundle size นะครับ แล้วเราก็เอาเมตริกพวกนี้มาวัดว่า เราสามารถ improve ตัว web performance หรือตัว bundle size ในแต่ละโหลด

ของเราเนี่ย มันลดลงหรือเปล่านะครับ โดยวิธี solution นะครับ ที่เราเอาเข้ามาใช้ อย่างแรกเลยก็คือตัว explicit import ตัว explicit import นะครับ คือ การที่เราจะพยายามจะ import ไปยัง path ที่มันลึกที่สุด เฉพาะ function นั้นที่มันใช้ ตัวอย่างของ slide นี้

นะครับ คือเรามีตัว Headless UI ที่เรามาใช้เป็น base component เสร็จแล้วตัว Headless UI เนี่ย เราไปดูมาว่า ตัวมัน export ตัว index entry ออกมา ซึ่งตัว index entry ของมันเนี่ย import ทุก component file ข้างใน แล้วเวลาเราดึงออกมาใช้แค่ 1 component หรือ 2 component มันไปดึงข้างในออกมาหมดเลย ทำให้ bundle size ที่เราเอามาใช้กับตัว Headless UI React มันมีขนาดค่อนข้างใหญ่ วิธีที่เราใช้นะครับ ก็คือ explicit import ซึ่งในเคสนี้เรา explicit import ไปที่ตัว node module หรือ path ของมันตรงๆ เลย มันก็จะช่วยให้ลด bundle size ของมัน แล้วก็เอาตัว component เฉพาะที่เราต้องการใช้ออกมา ตัว library อื่นก็จะมีเหมือนกัน อย่างบาง library

อย่างเช่น ตัว React-use มันก็จะมีตัว ES module ซึ่งสามารถเจาะ path เข้าไปใน specific path อย่างเคสของเราก็คือ import ตัว useCopyToClipboard

แล้วก็ useNetworkState ที่เป็น explicit import เข้ามานะครับ อีกเคสหนึ่งที่เรา improve ตรงนี้เข้ามาเหมือนกัน คือ iMask เราใช้ตัว iMask ในการ mask พวก text field input ไม่ว่าจะเป็น phone number หรือตัว debit card อะไรพวกนี้ เราก็จะมีการ improve ก็คือ เอา import เข้ามาตรงๆ เหมือนกัน มีการใช้ Braze web SDK นะครับ ของเราใช้ web Braze SDK

ในการ push user notification ในการที่ user subscribe ตัว SMS พอเรา improve ตรงนี้เนี่ย เราก็ลด bundle size ไปได้เหมือนกัน ก็จะมีนิดนึงนะครับ ในกรณีที่เราเปลี่ยน path หรือไม่ได้ import จาก path ที่มันเป็น path ปกติเนี่ย มันอาจจะมีปัญหาเรื่อง auto complete ซึ่งเราอาจจะต้องมา weight กันว่า ระหว่างเรื่องของการทำ performance กับเรื่องของการที่เราจะมี DX ที่ดี

developer experience ที่ดีเนี่ย มี trade-off กันยังไง อย่างในเคสนี้เราก็ต้องมานั่ง เขียนตัว module mapper เพิ่มใน unit test ของเรานะครับ ก็คือเราต้อง map ตัว react-use กับตัว headless-ui ที่เป็น explicit import เข้ากับ ตัว import ปกตินะครับ

อันนี้ก็จะเป็นตัวอย่างของ ตัว Braze SDK ที่เรา improve ไป เมื่อกี้นะครับ เราลด bundle size จาก 205 KB เหลือ 85 KB เพราะเรา import แค่ฟังก์ชัน ที่เราใช้เท่านั้น ไม่ได้ import จากตัว index entry file ของมัน

ก็อันนี้เป็น lesson learned นะครับ ของเราก็คือ เวลาที่ เราจะดึง library เข้ามาใช้เนี่ย หรือว่าเราจะดู library ไหน และเราจะเข้าไปศึกษา library นั้นว่า library นั้น เขา export index entry js ออกมา และข้างในมันน่ะ

ไป import ตัว component ทุก ไฟล์หรือเปล่า หรือเราสามารถ มี explicit import ที่เข้าไป ใช้ได้เหมือนกันครับ

การใช้ Lazy Loading ใน React8:46

อีก use case นึงที่เรา มีการ improve นะครับ ก็คือ เราแก้ library ต่างๆ ที่เรามาใช้ ก็จะมีหลายๆ library ด้วยกัน ที่เราจำเป็นต้องใช้ อย่างเช่น พวก react-hot-toast หรือว่า ตัว datadog monitoring ซึ่ง ในเคสนี้เราก็เป็น RUM หลายๆ ตัวมันก็จะมีตัว javascript bundle size ที่ ค่อนข้างใหญ่ เรา ก็จะพยายามดูว่า library ต่างๆ พวกเนี่ย มันสามารถ ลดขนาดของ bundle size ที่เราดึงมาใช้ได้หรือเปล่า

อันนี้เป็นตัวอย่างของตัว react-hot-toast นะครับ เบื้องหลังการทำงานของมัน ก็คือตัว CSS in JS มันเอาตัว CSS เนี่ย ไปแปะไว้ ใน javascript แล้วก็จังหวะ ที่เราโหลดครั้งแรกอ่ะ มันจะ ทำให้จังหวะที่เรา parse javascript มันนานขึ้น เพราะว่า bundle size มันใหญ่ขึ้น วิธีที่เราแก้ก็คือ เราใช้ ตัว headless mode ของตัว react-hot-toast นะครับ

ก็คือ ตัว headless เนี่ย จะ ไม่ได้มีการแปะ style ไว้ใน first time load แต่เราจะเอา style เนี่ย ไปแปะไว้ใน runtime แทน ซึ่งเราก็จะมีการ เขียน style ที่แปะเข้าไป ในโค้ดของเรานะครับ

แล้วก็ตัว Datadog เนี่ย ที่เราดึงมาใช้เพราะว่า มันต้องมีการทำ user monitoring เราจะต้องมีการเก็บ log ต้องดู log ของ user ว่า user คลิก แล้วได้ flow ตามที่เราต้องการ หรือเปล่า ก็จะมีการ monitoring พวกนี้ ซึ่งขนาดของ Datadog RUM ก็จะมีขนาด ใหญ่ถึง 151.7 KB ในเคสนี้ แล้วก็มีการเข้าไปดู library ว่า ตัว library เนี่ย มันสามารถมี ขนาดที่มันเล็กลงได้หรือเปล่า อย่างตัวเราไปดู library ที่เป็น เพื่อนบ้าน ก็คือ Datadog เหมือนกัน ที่เป็น browser RUM slim ซึ่งมันเป็น library ที่ อาจจะมี feature ไม่เท่าของเดิม แต่ว่ามี feature เพียงพอเท่าที่เราใช้ งาน ก็มีขนาดเล็กลงเหมือนกัน

ครับ และตัวอย่างของอันนี้ก็คือ react-hot-toast ที่เราเปลี่ยนจาก การใช้ import ปกติ ไปเป็นการ import แบบ headless UI แล้วก็ตัว style ที่เราต้อง เขียนเนี่ย แล้วก็เอามาเขียนใน runtime แทนนะครับ

แล้วก็อีกเทคนิคหนึ่งที่เรา ใช้ค่อนข้างเยอะเลย ก็คือการทำ lazy loading การทำ lazy loading ใน React เนี่ย ก็คือเป็นการบอกว่า เราจะโหลด resource เฉพาะที่เราต้อง ใช้งานในหน้านั้นๆ หรือว่าใน path นั้นๆ นั้นเอง ซึ่งเบื้องหลังของการทำงาน มันคือการที่ React ไป lazy แล้วก็ ตัว webpack หรือตัว bundler จะรู้ตัวเองว่า path ที่เราต้องโหลดเนี่ย

เป็น asynchronous dynamic import

ซึ่งการทำงานของตัว dynamic import เนี่ย มันก็จะ work กับตัว suspense ของ React ที่เรา import เข้ามา นะครับ ก็ช่วยลด bundle size แล้วก็ มีการแตก chunk ก็คือเป็นการทำ code splitting ยกตัวอย่างเช่น เรามีหน้า 10 หน้า เราไม่จำเป็นต้องโหลดทั้ง 10 หน้า มาพร้อมกัน ในจังหวะแรก เราสามารถโหลด มาเฉพาะแค่หน้าแรกก่อน แล้วเรา ค่อยๆ โหลดหน้าที่เหลือเอามา เวลาที่ user เล่นทีละ flow นะครับ ก็จะมีการ optimize หลายแบบ พาร์ทของเมื่อกี้ก็คือเป็น first-party script ที่เราสามารถ control ได้ แล้วก็ยังมีตัว third-party script เหมือนกัน อย่างพาร์ทนี้ก็คือเรามีตัว lazy with retry นะครับ อันนี้เป็น on top function ของเรา เพราะว่าเราเขียนตัว function เพื่อให้มัน retry

เวลาที่เราโหลด dynamic import เข้ามาอีกที อย่างนี้เป็นการทำ dynamic import ใน level route

ซึ่งเราสามารถ split code เป็นแบบหลาย level ได้ ไม่ว่าจะเป็นการทำ level routing หรือว่าการทำ level component อย่างตัวอย่างของอันนี้ก็คือเราทำ download earn in dialog โดยใช้ท่าปกติก็คือ lazy แล้วก็ import ตัวผ่านนั้นเข้ามา webpack ก็จะรู้ว่าตัว import ตรงนี้เป็น dynamic import แล้วก็เราเอามา work กับ suspense เวลาที่ user เปิดตัว dialog ขึ้นมา ก็จะมีการโหลดตัว component นี้เข้ามาครับ ก็ช่วยให้เราลด bundle size ไปได้เยอะมาก

การจัดการกับ Third-party Scripts13:13

นอกจาก first-party script แล้ว เรายังมีตัว third-party script อยู่เหมือนกัน ตัว third-party script ไม่ว่าจะเป็นเรื่องของการทำ analytics การวัด metrics ต่างๆ หรือการทำ A/B testing บางแอปอาจจะต้องมี A/B testing ต้องมี feature flag เปิดปิด หรือว่าต้องมีการโชว์ date formatting นะครับ

หรือการทำ animation ก็เหมือนกัน ซึ่งวิธีการ control first-party lazy loading กับ third-party lazy loading ก็จะต่างกันเหมือนกันครับ อย่างตัว third-party lazy load เนี่ย ที่เราเอามาใช้ก็คือเรามี Optimizely ในการทำพวก experiment ทำ feature flag ตัว segment เราทำ analytics เสร็จแล้วตัว segment เนี่ย มันก็จะไปดึง third-party อีกที ซึ่งตัว segment ก็จะไปดึงพวก Facebook Pixel ดึง Google Ads หรือเราก็จะมีตัว Datadog ที่มีไซส์ค่อนข้างใหญ่ แล้วมีพวกกันบอทก็คือ Turnstile reCAPTCHA มี Google Tag Manager ที่ไปดึง TikTok analytics ไปดึง Hotjar เข้าไปอีก

ซึ่งวิธีที่เราใช้นะครับเราก็ lazy load ตัว segment แล้วก็เพื่อนของมัน

แล้วก็มีการใส่ flag ในการโหลดของ segment เข้าไป อย่างตัว library บางตัวเนี่ย มันจะสามารถมาโหลดทีหลัง เขาจะไม่ได้ init ครั้งแรกตอน import แต่เขาจะมี function ในการโหลดทีหลังได้ แล้วก็ใส่พวก lazy ใส่เข้า hooks ของเรา หรือว่าใส่พวก function ต่างๆ ที่เราเอามาโหลดเมื่อเราต้องใช้งานนะครับ

แล้วเราก็จะมีการโหลดพวก recaptcha เพื่อให้มันโหลดเร็วขึ้นใน first time โหลดของเราครับ

แล้วก็มาถึงพระเอกของเรานะครับ ตัว Partytown อันนี้ก็จะเป็น library อีกตัวนึงที่ทำจาก builder.io ซึ่ง Partytown จะช่วย optimize ตัวเว็บโหลด เปลี่ยนจาก main thread ไปใช้ใน web worker แทน อันนี้เดี๋ยวจะเป็นเซสชั่นที่พี่เจมส์จะ cover ตรงส่วนนี้ต่อนะครับ

การใช้ Partytown เพื่อ Optimize Third-party Scripts15:18

ครับ Partytown นะครับ เป็น tool ของ builder.io ก็ on production แล้ว แล้วก็เจ็บนิดหน่อยด้วยวันนี้

Partytown นะครับ เป็นสิ่งที่มันทำนะครับ ก็คือมันพยายามถ้าเราเอา script third party ของเราเนี่ย ไป wrap ด้วย Partytown มัน script third party เราอาจจะไปอยู่อีก thread นึง ซึ่งก็คือ web worker thread ซึ่งจะทำให้ main thread เราฟรีขึ้น ให้สามารถ render ตัว first party ของเราหรือโค้ด react ของเรานะครับ ที่ทำให้ render หน้าของเราได้เร็วขึ้น เพราะว่าถ้าเกิดเราไม่มี Partytown เนี่ย ทั้งโค้ด first party เรา และ third party เราจะอยู่ใน thread เดียวกัน ทำให้มัน block ตัว parsing JavaScript นะครับ เราจะต้องรอ third party run ด้วย ทำให้กว่าที่เราจะได้หน้าที่ render ขึ้นมาเนี่ย ไม่ว่าจะเป็นตัว FCP หรือ LCP เนี่ย มันช้าลง Partytown ก็จะมาแก้ปัญหานี้ก็คือ แยกส่วนของเราออกจากกันให้ชัดเจนนะครับ ซึ่งมันก็มี mechanism บางอย่างที่มันใช้คุยกันระหว่าง thread แล้วเดี๋ยวผมเล่าต่อนะครับ

ก็มันจะมีตัว main thread นะครับ ก็เป็น thread หลักที่เราใช้ run ตัวหน้าเว็บของเรา แล้วก็มีตัว service worker ที่เอาไว้คุยกัน ผ่านกันระหว่าง worker thread นะครับ ซึ่งตรงนี้เนี่ย ก็ต้องมี service worker เป็นตัวกลางที่คอยคุยนะครับ

แล้วอันนี้เป็นโค้ดตัวอย่างนะครับ ที่เราใช้ตัว Partytown ครับ ก็คือมันจะมี use case third party บางตัวเนี่ย ไม่อนุญาต call หรือ cross origin นะครับ ซึ่งเราต้องเขียน script เพื่อทำการ proxy ตัว third party บางตัวที่เขาไม่อนุญาต call มัน

ก็จะมีบางตัวเช่น TikTok อะไรอย่างเงี้ย ที่ไม่อนุญาต cross origin เราก็ต้องทำ proxy บางอย่าง ที่ให้ไป call ผ่าน proxy เราก่อน แล้วก็ proxy เราไปคุยกับตัว third party javascript ไฟล์นะครับ ก็นี่ก็เป็นตัวอย่างปัญหาที่เกิดขึ้นตอนทำขึ้นมาจริง ซึ่งเราใช้ตัว edge function ของ Netlify นะครับ เป็นตัวทำ proxy ซึ่งอันนี้ก็เป็นตัวอย่าง request นะครับ ที่ใช้ proxy ตัว netlify function นะครับ เพื่อไป ขอ javascript file ของ third-party นั้น มันจะเป็น analytics ของ TikTok นะครับ หรือว่าตัว bat.bing นะครับ ที่ third-party เราที่เราใช้นะครับ

ผลลัพธ์จากการ Optimize และปัญหาที่พบ17:50

ซึ่งพอเราทำทุกอย่าง

ที่พูดมานะครับ รวมกันแล้วเนี่ย ก็ ผลลัพธ์ อันนี้เป็นกราฟ ที่อยู่ใน real user monitoring ของ Datadog นะครับ ก็ได้เวลา ถ้าเราทำไป 2 หน้าเป็นหลัก ทำทั้ง หลายอย่างในทุกหน้า แล้วก็ทำตัว เรซที่สูง ตัว 2 หน้าเป็นหลัก ที่เราวัดที่สนใจนะครับ ก็คือหน้า login page กับหน้า questionnaire เราได้เวลา จากประมาณ 4 วินาที

หรือ 3 วินาที ก็คือได้มา 1 วินาที ซึ่งผมว่าก็ดี ดีระดับนึง แต่คิดว่ามี

ส่วนที่ improve ได้มากกว่านี้ ก็ lighthouse ตัว 11.5 ใน Google Chrome Canary ก็ ได้คะแนนเขียวมา แต่ผมสนใจตัวเวลา ซะมากกว่า เพราะตอนนี้ web app ของเราเนี่ย ไม่ได้มี requirement เรื่อง SEO อะไร เพราะมันเป็น point หลักคือหลัง login flow ครับ business flow มันคือหลัง login แล้วก็ตัว หรือ register ก็เลยสนใจเรื่องเวลา ซะมากกว่า ซึ่งมันมีส่วนที่ improvement

เพิ่มได้นะครับ แล้วก็เรา ก็ทำไปบ้างแล้วด้วย มีทีมเราทำ ก็ตัว Partytown ครับ มัน มีอีกโหมดนึง ซึ่งก็คือใช้ตัว Atomic หรือว่า shared buffer ซึ่งเป็น feature browser นึงแล้วกัน ที่ ทำให้มันคุยกัน ระหว่าง worker thread กับ main thread โดยไม่ผ่าน service worker ได้นะ ทำให้มันเร็ว ขึ้น เขาเคลมว่า 10 เท่า แต่เรามีปัญหาอยู่อย่างนึงว่า มันต้องไป config ตัว COOP ครับ

ตัว header เพื่อเป็นการบอก browser ว่า เรื่อง ถ้าทุกคนเคยจำได้ มันเคยมี ปัญหาเรื่อง Spectre hack เกิดขึ้น ก็คือ CPU มัน โดน เขาเรียกอะไรนะ

เมื่อนานแล้วมันมีปัญหาเรื่อง security ของ browser ครับ แล้วก็ browser ก็เลยออก API ตัว config ระหว่าง thread ได้ว่า ไม่ให้สามารถ share ข้อมูลระหว่างกัน ซึ่งการที่จะ config feature อันนี้ ตัว COOP เนี่ย มันต้องรู้ว่า third party พวก iframe ของหน้าเราเนี่ย

ต้องถูก config ไปด้วย แต่เรามี iframe บางตัวที่เราใช้ เป็น third party อีกทีนึงที่เราไม่สามารถไปควบคุมเขาได้ ทำให้ feature นี้เราไม่สามารถใช้ได้ ก็เลยไม่ได้ใช้โหมด atomic ของตัว Partytown ไป แล้วก็เราใช้ Datadog real user monitoring เป็นตัวทำ real user monitoring นะครับ จริงๆ พยายามลองเอามันไปออกจาก main thread เหมือนกัน แต่ก็ไม่ work ก็เลยถอยไป แล้วก็มีตัว form library ที่เราใช้ตอนแรกเป็น Zod ครับ ซึ่งก็ tree shake ไม่ได้ ก็ไปลองใช้ ValiBot อันนี้เสร็จไปแล้ว ทำไปแล้ว มีคนช่วยทำ

แล้วก็มีอีกปัญหาหนึ่งนะครับ ก็คือเราใช้ module federation หรือว่าเทคนิค micro front end ประเภทหนึ่งนะครับ ก็มันก็มี limitation บางอย่าง เรื่อง request waterfall problem นะครับ

แผนการพัฒนาในอนาคตและข้อจำกัด20:58

ที่มันต้องรอโหลด main.js ก่อน, main.js ไปโหลด micro frontend นึง remote entry แล้ว remote entry ไปโหลด javascript ของ micro frontend นั้นน่ะครับ ก็มีความ waterfall ซึ่งตอนนี้ก็รอ feature prefetch ของ module federation อยู่นะครับ 2.0 เค้าก็เขียนไว้ว่าเดี๋ยวจะมี เค้าน่าจะช่วยแก้ปัญหาตัว waterfall problem นี้ได้ แล้วก็มีตัว จริงๆ เรามีอีกปัญหาหนึ่งก็คือเรื่อง CSS เราด้วย เรามี CSS variable ที่ยาวพอสมควร ตัวใหญ่ ก็ทำให้มันโหลดช้า เราก็อยากจะได้ inline critical CSS เข้าไปแล้วก็ lazy load CSS อื่นๆ ที่หลัง

ก็อันนี้ก็เป็นรูปที่ผมว่ามันค่อนข้างจริงนะครับ ก็คือเวลาเรา develop ไปเนี่ย ก็ graph ตัวล่างนะครับ ไอ้ตัวข้างบนเนี่ย เป็น framework นี้ที่เค้า

เคลมว่าทำแล้วก็ไม่ต้องมา optimize ครับ ซึ่งผมว่ามันน่าจะยังไม่ค่อยจริงเท่าไหร่ ตอนนี้เราอยู่ framework ล่างนะครับ ก็คือเป็น question mark เนาะ เป็น framework ทั่วๆ ไป ก็เวลาเรา develop อะไรไปเนี่ย feature เราเพิ่มเนาะ ทุกอย่างมันเปลี่ยน เราก็ต้อง performance เราก็ลดลงตามเวลา แล้วเราก็ต้องมานั่งคอย optimize มันก็เร็วขึ้นๆ แล้วก็เราเพิ่ม feature อีก เราก็ช้าลง หนักลงอีก มันก็จะเกิดเป็น cycle ตัวการ optimize อย่างนี้ไปเรื่อยๆ นะครับ ก็ slide ของผม มีประมาณนี้ครับ แล้วทุกคนมีคำถามเพิ่มเติมมั้ยครับ

คำถาม-ตอบเกี่ยวกับการ Optimize และการจัดการ Dependencies22:33

เวลาที่พวกฟังก์ชัน เราทำฟังก์ชัน pinning ได้เนาะ แต่ว่าเวลาถ้าต้อง develop ต่อไปแล้ว มันต้องมีการอัพเดตอย่างเงี้ย แล้วเกิดว่า มันเปลี่ยนโครงสร้างในตัวไลบรารี เรามีวิธีดีลกับมันยังไง ขอ import ครับ

ตัว import ครับ ที่เราใช้เสียงไปดูสไลด์ที่เป็นเรื่อง import module คือแบบ มัน import เปลี่ยนที่ใช่มั้ยฮะ อื้อ ใช่ครับ คือถ้าเป็นบน production เค้าจะว่ามันมี node lock package lock เนาะ อาจจะล็อกได้ แต่ว่าพอมันเป็นเรื่องของ พอมันต้อง develop อย่างเงี้ยครับ แล้วมันไม่ต้องการอัพเดตเวอร์ชันตัวนี้ฮะ ผมก็เลยสงสัยว่า ถ้าเกิดว่าเวลา จะ develop อัพเดตเวอร์ชัน อะไรเงี้ย มันจะแก้ปัญหายังไง จะต้องไปนั่งตามแก้นี่ทุกครั้งรึเปล่า

ครับ ผมว่า เรื่องของการอัพเดตเวอร์ชันเนี่ย เราอาจจะต้องตามดู ในแต่ละเวอร์ชันที่เราจะอัพเดตเข้ามาว่า แต่ละเวอร์ชันน่ะ มันมี breaking change ไม่ว่าจะมีเรื่องของการเปลี่ยน path เปลี่ยนไฟล์ หรือว่า มีอัพเดตไฟล์อะไรเข้ามาครับ แล้วระหว่างการอัพเดตเวอร์ชัน เราน่าจะ detect ได้ว่า ตัวเวอร์ชันที่เราอัพเดตไปครับ มีการ breaking change ในโค้ดของเรา เราอาจจะต้องดูเรื่องของการ ทำ regression test เพื่อ detect ว่า ตรงนี้ เราอัพเดตไปแล้ว มัน break โค้ดเดิมของเราก็ได้ครับ

ครับ อีกประเด็นนึงครับ เรื่อง proxy proxy ที่เราบอกว่าใช้ตัว Netlify ใช่มั้ยครับ Netlify function เนาะ Netlify function ใช่ครับ คำถามคือ เราน่าจะมีการทำ caching มั้ยครับ หรือว่าเราแค่เป็นการเป็นแค่ proxy แบบ reverse proxy เฉยๆเลย เพราะว่าเท่าที่ดูเนี่ย เหมือนกับว่าถ้ามันต้องมีการ call ทุกครั้งเนี่ย data มัน bandwidth มันจะสูงมาก

มี cache ครับ โอเค Netlify function สามารถบอกได้ว่า cache ขอ cache request นี้ไว้ได้ อ๋อ โอเคครับ คือ คือสามารถที่จะกำหนดไว้ใน ในฟังก์ชันได้เลยเนาะว่า ได้เลยครับ

โอเคครับ

การทำ Regression Testing และ CI/CD Pipeline24:38

อยากถามเรื่อง regression ครับ ว่าเตรียม regression ยังไงบ้าง

อาจถึงว่าพอแก้ทุกอย่างใน optimize ทุกอย่างของมันก็ต้อง

regression ทั้งหมดอะไรเงี้ย ก็เลยถามว่าอย่างของที่ EarnIn แบบทำเรื่องพวกนี้ยังไงบ้างแบบทุกอย่าง automate หรือว่า

เรามี automate pipeline CI/CD ครับ ก็คือ performance regression ยังไม่มี แต่ตัว CI/CD build ตัว build ผ่าน

ถ้า push version build เรามี build pipeline แล้ว build พังก็บอก เราก็เห็นอยู่ใน PR มันก็ต้องตอบปัญหาเมื่อกี้ แล้วก็มีพวก e2e test ที่เราทำ

ก็เป็น e2e test ที่เราม็อค backend ครับ อ๋อ ก็คือเป็น browser จริงๆ ใช้ Playwright แล้วก็ simulate ทุกเคส business case

ที่เราสนใจ รันตอนเปิด PR อันนึง แล้วก็ถ้าผ่านก็ merge เข้าไป approve อะไรด้วย แล้วก็มี web monitoring อีกตัวนึงที่ไปรันกับ ตัว dev จริงๆ ที่เป็น scheduler แบบ cron job รัน ก็มี business flow หลักครับ รันเหมือนกันครับ เตรียมตรงนี้ แล้วก็ มีอีกนิดนึงด้วย มี synthetic test ของ DataDog ที่ไปดูตัวทั้ง production ก็ตัว development เตรียมเรื่องนี้นานแค่ไหนครับ

อันนี้เราเริ่มตั้งแต่แรกๆ เลยครับ ระหว่างที่เราเริ่มการ develop เราก็คิดเสมอว่า การที่เราจะ deliver ตัว product ของเราได้เนี่ย มันก็ต้องมี quality ไม่ว่าจะผ่านของการ test ในระดับของการทำ manual test หรือว่า automate testing ที่เราอาจจะต้องกลับมา แก้ feature เดิม แล้วเราจะ confident ว่า feature ที่เราแก้ไปเนี่ย มันไม่พังครับ

โอเคครับ ขอบคุณครับ เดี๋ยวคุยกันต่อได้ครับ

การใช้ Lighthouse และ Real User Monitoring27:05

Hello ถามนิดเดียวครับ เวลาดู Lighthouse ปกติมันก็มีใน DevTools นะครับ แต่ผมอยากรู้ว่าปกติพี่ใช้ใน DevTools หรือ deploy ขึ้นแล้วก็แปะลิงก์ลงในเว็บ PageSpeed Lighthouse อันนี้เป็น lesson เจ็บๆ ของผมอย่างนึงครับ คือตอนแรกผมคิดว่าดูในเครื่อง local ดูในเครื่อง local ตัวเอง make sense แต่จริงๆ มันไม่ valid ควรจะดูตัว dev จริงๆ หรือ production จริงๆ เลยมากกว่าตัว local เพราะมันจะเห็นอะไรบางอย่าง เช่น บางทีตัว third-party segment เราเนี่ยโหลด script ไม่เท่ากันในแต่ละ env ด้วยซ้ำ ก็ควรจะรันในตัวจริง ข้อดีของการรัน local คือมันเห็นเราจะแก้อะไร เรา fast feedback ได้มันเร็ว ถูกมั้ย จะแก้ปัญหาบางอย่างที่มัน obvious แก้ได้ แต่ถ้าอะไรที่มันแบบก็คือต้องรันทั้งหมด ในมุมมองผมนะ ดูภาพรวมแต่ของจริงก็คือตัว production

และอีกเรื่องนึงคือ Lighthouse เป็น lab มันเป็นการจำลองตัว device เนาะ ไม่ใช่ user จริงๆ ผมว่าสิ่งที่ควรทำเพิ่มคือแอด RUM (Real User Monitoring) ซึ่งถ้าไม่ใช่ DataDog ก็มีพวก Sentry อะไรอย่างเงี้ย หรือว่าตัว Faro ของ Grafana ครับ

เป็น open source ที่ฟรีครับ ควรจะดู user data ของ user จริงๆ ที่เข้ามาด้วย Real User Monitoring มากกว่าแค่ตัว Lighthouse มี baseline ที่ดูนอกจาก Lighthouse มั้ยครับ ปกติที่ทำจริงๆ

ที่ตัว tool ตัวอื่นอยู่ครับ ชื่อเว็บ PageSpeed test มั้ง แต่ตอนนี้ไม่ได้ใช้ครับ โอเคครับผม ก็ผมปกติจะบอกรายละเอียดอื่นๆ อีกครับ

มีเท่านี้ครับ

ก็ขอบคุณทุกท่านมากครับ