17 คะแนน โดย GN⁺ 2024-08-04 | 3 ความคิดเห็น | แชร์ทาง WhatsApp

"ตาราง merchants2 เหรอ? ใช่ เราสร้าง merchants2 เพราะคอลัมน์ของ merchants ไม่พอ"

  • ตอนเริ่มเขียนโปรแกรมใหม่ๆ ผมไม่รู้เลยว่าคนสามารถหาเงินจากการเขียนโปรแกรมได้
  • ผมได้เรียนรู้อะไรมากมายจากงานซอฟต์แวร์แห่งแรก แต่โค้ดเบสของที่นั่นทั้งแย่ที่สุดและดีที่สุดในเวลาเดียวกัน

ฐานข้อมูลอยู่ยงคงกระพัน

  • ในระบบ legacy ฐานข้อมูลทำหน้าที่มากกว่าแค่ที่เก็บข้อมูล มันกำหนดข้อจำกัดของทั้งระบบและเป็นจุดที่โค้ดทั้งหมดมาบรรจบกัน
  • SQL Server มีข้อจำกัดเรื่องจำนวนคอลัมน์ของตาราง ตอนนั้นคือ 1024 คอลัมน์ ปัจจุบันคือ 4096 คอลัมน์ คอลัมน์ของตาราง Merchants ไม่พอจึงสร้างตาราง Merchants2 ขึ้นมา (มีมากกว่า 500 คอลัมน์)
  • Merchants และ Merchants2 คือแกนกลางของระบบ ทุกอย่างเชื่อมกับ Merchants มีตารางที่ normalize แล้วอื่นๆ อยู่ด้วย แต่ก็มีความสัมพันธ์แบบ foreign key กับ Merchants

SequenceKey

  • SequenceKey เป็นตารางเรียบง่ายที่มีเพียงหนึ่งคอลัมน์และหนึ่งค่า
  • มันถูกใช้เพื่อสร้าง ID คาดว่าน่าจะถูกทำขึ้นเพราะ SQL Server ในตอนนั้นยังไม่รองรับ auto-increment ID
  • ใน stored procedure ทุกตัวจะดึงคีย์จาก SequenceKey แล้วเพิ่มค่า จากนั้นนำไปใช้เป็น ID ของหลายตาราง ทำหน้าที่เป็น implicit join ไปในตัว

Calendar

  • Calendar เป็นตารางปฏิทินที่กรอกด้วยมือ ถ้า Calendar หมดอายุจะไม่สามารถล็อกอินเข้าระบบได้
  • เรื่องนี้เคยเกิดขึ้นเมื่อไม่กี่ปีก่อน แล้วเด็กฝึกงานคนหนึ่งก็เติมข้อมูลเพิ่มให้อีก 5 ปี ไม่มีใครรู้ว่าระบบไหนใช้มันอยู่บ้าง

Employees

  • ทุกเช้าเวลา 7:15 ตาราง employees จะถูกลบทิ้งแล้วเติมใหม่จาก CSV ที่ได้รับจาก ADP
  • ระหว่างกระบวนการนี้จะไม่สามารถล็อกอินเข้าระบบได้ และบางครั้งงานนี้ก็ล้มเหลว
  • เพราะข้อมูลต้องถูก replicate ไปยังสำนักงานใหญ่ จึงมีการส่งอีเมลไปให้คนคนหนึ่งเพื่อกดปุ่มคัดลอกข้อมูลทุกวัน

ฐานข้อมูลตัวทดแทน

  • คุณอาจคิดว่าน่าจะจัดระเบียบฐานข้อมูลได้ และบริษัทก็คิดแบบนั้นเหมือนกัน
  • มีสำเนาฐานข้อมูลอยู่ชุดหนึ่ง แต่ข้อมูลจะช้ากว่าของจริงประมาณ 10 นาที และซิงก์กันได้ทางเดียวเท่านั้น
  • ฐานข้อมูลชุดนี้ถูก normalize ไว้ ดังนั้นถ้าจะหาเบอร์โทรศัพท์จาก merchants ต้อง join ถึง 7 ตาราง

ยอดขาย

  • พนักงานขายทุกคนมีโควตารายเดือนที่เรียกว่า "win" ซึ่งต้องทำให้ได้
  • ตารางที่ใช้จัดการเรื่องนี้ซับซ้อนมาก ทุกวันจะมีงานรันเพื่อหาว่ามีแถวไหนถูกเพิ่มหรือแก้ไข แล้วซิงก์กับระบบของสำนักงานใหญ่
  • ปัญหาเริ่มขึ้นเมื่อพนักงานขายคนหนึ่งขอให้แก้เรคคอร์ดด้วยมือ
  • เขาทำ win ได้ครบแล้ว และยังปิดการขายก้อนใหญ่อีกในเดือนนั้น เขาเลยอยากย้ายยอดนั้นไปเดือนถัดไป
  • เด็กฝึกงานคนหนึ่งรับงานนี้ไปทำ ข่าวลือนี้แพร่กระจาย และตลอด 3 ปีหลังจากนั้นคำขอแบบนี้ก็เพิ่มขึ้นแบบทวีคูณ
  • ช่วงหนึ่งมีเด็กฝึกงาน 3 คนที่ทำแค่เขียน SQL statement สำหรับเรื่องนี้ การสร้างแอปไว้จัดการโดยเฉพาะถูกมองว่ายากเกินไป

โค้ดเบส

  • โค้ดเบสแรกที่ผมเจออยู่บน Team Foundation Server ซึ่งเป็นระบบ version control แบบรวมศูนย์
  • โค้ดเบสที่ผมทำงานด้วยเป็น VB ครึ่งหนึ่ง และ C# อีกครึ่งหนึ่ง รันอยู่บน IIS และใช้ session state ไปทั่วทั้งระบบ
  • นั่นหมายความว่า ถ้าคุณเข้าหน้าเดียวกันผ่าน Path A หรือ Path B คุณอาจเห็นสิ่งที่ต่างกันมากบนหน้านั้น
  • JavaScript framework แทบทุกตัวที่มีอยู่ในยุคนั้นถูก check in ไว้ใน repo นี้ พร้อมกับการแก้ไขแบบ custom ที่ผู้เขียนคิดว่าจำเป็น โดยเฉพาะ knockout, backbone, marionette รวมถึง jquery และปลั๊กอินของ jquery
  • นอกจากโค้ดเบสนี้แล้ว ยังมี SOAP service ราว 12 ตัว และ Windows application แบบ native อีกหลายตัว

ฮาร์ดไดรฟ์ของ Gilfoyle

  • Gilfoyle เป็นที่รู้จักว่าเป็นโปรแกรมเมอร์ที่เร็วมาก ผมไม่เคยเจอเขาเป็นการส่วนตัว แต่ผมรู้จักเขาผ่านโค้ดและสิ่งที่เหลืออยู่บนฮาร์ดไดรฟ์ของเขา
  • Munch เก็บฮาร์ดไดรฟ์ของ Gilfoyle ไว้บนโต๊ะในชุด RAID แม้เขาจะลาออกจากบริษัทไปหลายปีแล้วก็ตาม
  • เพราะ Gilfoyle มีชื่อเสียงเรื่องไม่ check in โค้ด และชอบทำ Windows application ใช้ครั้งเดียวแบบสุ่มสำหรับผู้ใช้คนเดียว
  • ไม่ใช่เรื่องแปลกที่ผู้ใช้จะนำ bug report ของแอปที่มีอยู่แค่บนฮาร์ดไดรฟ์ของ Gilfoyle มาให้

บั๊กการจัดส่ง

  • งานส่วนใหญ่ของผมคือไล่ตามบั๊กที่ทีมไม่อยากหยิบไปทำ
  • มีบั๊กตัวหนึ่งที่น่ารำคาญมากและเกิดขึ้นทุกๆ สองสามเดือน เป็นกรณีที่มีออเดอร์ค้างอยู่ในคิวจัดส่งหลังจากที่ถูกส่งไปแล้ว ทำให้มันดูเหมือนทั้งจัดส่งแล้วและยังไม่ได้จัดส่งพร้อมกัน
  • เพื่อแก้ปัญหานี้ เราลองมาหลายวิธี ทั้ง SQL script และ Windows application ผมถูกแนะนำว่าอย่าไปไล่หาสาเหตุราก แต่ผมทนไม่ได้
  • ระหว่างทางผมได้เรียนรู้วิธีคิดของ Gilfoyle แอปจัดส่งจะดึงทั้งฐานข้อมูลมา แล้วค่อยกรองตามวันที่เพื่อเก็บออเดอร์ทั้งหมดตั้งแต่วันที่เริ่มต้นของแอป
  • แอปนี้พึ่งพา SOAP service แต่ไม่ใช่เพื่อทำงานแบบ service มันเป็นเพียง pure function โดยที่ฝั่ง client เป็นตัวก่อให้เกิด side effect ทั้งหมด
  • ใน client ตัวนั้น ผมเจอโครงสร้าง class hierarchy ขนาดมหึมา มี 120 คลาส แต่ละคลาสมีเมธอดหลายตัว และมีการสืบทอดลึกลงไปถึง 10 ชั้น
  • ปัญหาอย่างเดียวคือ เมธอดทั้งหมดว่างเปล่า
  • สิ่งนี้ถูกสร้างขึ้นเพื่อทำโครงสำหรับใช้ reflection โดย reflection นั้นจะสร้างสตริงคั่นด้วย pipe (โครงสร้างอิงจากฐานข้อมูลแต่เป็นแบบ static ทั้งหมด) แล้วส่งออกไปทาง socket
  • สุดท้ายมันถูกส่งไปยัง Kewill ซึ่งเป็น service ที่ใช้สื่อสารกับผู้ให้บริการขนส่ง สาเหตุของบั๊กคือ Kewill นำเลข 9 หลักกลับมาใช้ซ้ำทุกเดือน และมีใครบางคนไปปิด cron job ที่ใช้ลบออเดอร์เก่า

ความโกลาหลที่งดงาม

  • ยังมีเรื่องให้เล่าเกี่ยวกับโค้ดเบสนี้อีกมาก ทั้งทีม senior developer ที่ใช้เวลา 5 ปี rewrite ทั้งระบบโดยไม่ปล่อยโค้ดเลย หรือที่ปรึกษาจาก Red Hat ที่จะมาสร้างฐานข้อมูลหนึ่งเดียวเพื่อควบคุมทุกอย่าง
  • โค้ดเบสนี้มีมุมเพี้ยนๆ เต็มไปหมด และมีเหตุผลมากพอที่จะตั้งทีมเฉพาะขึ้นมาเพื่อเริ่มฟีเจอร์เดี่ยวๆ ใหม่ตั้งแต่ต้น
  • แต่เรื่องที่สำคัญที่สุดคือ Justin ปรับปรุงหน้า Merchants Search ซึ่งเป็นจุดเริ่มต้นของทั้งแอปพลิเคชัน
  • เจ้าหน้าที่บริการลูกค้าทุกคนจะคุยโทรศัพท์กับร้านค้าไปพร้อมกับพิมพ์ ID หรือชื่อเพื่อค้นหาข้อมูล แล้วระบบจะพาไปยังหน้าขนาดใหญ่ที่มีข้อมูลทุกอย่างอยู่ในนั้น
  • หน้านี้อัดแน่นไปด้วยข้อมูลที่จำเป็นทั้งหมดและลิงก์ทุกจุดที่อยากเข้า ถือว่าแน่นข้อมูลในแบบที่ดีที่สุด แต่ก็ช้ามาก
  • Justin เป็น senior developer เพียงคนเดียวในกลุ่มของผม เขาฉลาด พูดจาเสียดสี และไม่ค่อยสนใจธุรกิจเท่าไร
  • เขาพูดตรงๆ ไม่อ้อมค้อม และมักแก้ปัญหาคนเดียวได้เร็วกว่าทีมรอบข้างเสมอ
  • วันหนึ่ง Justin เบื่อที่จะต้องฟังว่าหน้า merchant search ช้าแค่ไหน เขาเลยไปแก้มัน
  • กล่องข้อมูลทุกกล่องบนหน้าจอถูกแยกออกเป็น endpoint ของตัวเอง ตอนโหลดหน้า ข้อมูลทั้งหมดที่อยู่เหนือ fold จะเริ่มถูกดึงก่อน และเมื่ออันหนึ่งโหลดเสร็จก็จะมี request เพิ่มเติมตามมา
  • เวลาโหลดหน้าลดจากหลาย分钟เหลือไม่ถึง 1 วินาที

การ decouple สองรูปแบบ

  • ที่ Justin ทำแบบนี้ได้ก็เพราะโค้ดเบสนี้ไม่มี master plan
  • ไม่มีพิมพ์เขียวคร่าวๆ ที่ระบบต้องเข้ารูป ไม่มีรูปแบบ API ที่คาดหวังไว้ ไม่มี design system ที่มีเอกสาร และไม่มีคณะกรรมการตรวจสถาปัตยกรรมที่คอยรับประกันความสม่ำเสมอ
  • แอปนี้เละเทะโดยสมบูรณ์ ไม่มีใครซ่อมมันได้ จึงไม่มีใครพยายามจะซ่อมมัน แทนที่จะทำแบบนั้น เราสร้างโลกเล็กๆ ที่มีเหตุมีผลของตัวเองขึ้นมา
  • แอปแบบ monolithic นี้ค่อยๆ เติบโตกลายเป็นจักรวาลย่อยของแอปเล็กๆ ที่ดีตามขอบนอก เพียงเพราะความจำเป็นล้วนๆ
  • ใครก็ตามที่ได้รับมอบหมายให้ปรับปรุงส่วนหนึ่งของแอป สุดท้ายก็มักเลิกพยายามแก้ใยแมงมุมเดิม แล้วไปหามุมเล็กๆ ดีๆ เพื่อสร้างของใหม่ จากนั้นค่อยๆ อัปเดตลิงก์ให้ชี้ไปยังของใหม่ที่ดีกว่า และปล่อยของเก่าให้กลายเป็นกำพร้า
  • ฟังดูอาจจะยุ่งเหยิง แต่กลับทำงานด้วยแล้วสนุกอย่างน่าประหลาด ความกังวลเรื่องโค้ดซ้ำหายไป ความกังวลเรื่องความสม่ำเสมอก็หายไป ความกังวลเรื่อง scalability ก็หายไป
  • โค้ดถูกเขียนมาเพื่อการใช้งาน เขียนโดยแตะพื้นที่รอบข้างให้น้อยที่สุด และเขียนให้เปลี่ยนทดแทนได้ง่าย โค้ดของเราถูก decouple เพราะการ couple มันยากกว่าเสียอีก

หลังจากนั้น

  • หลังจากนั้นมา ในเส้นทางอาชีพของผม ผมไม่เคยมีโอกาสได้ทำงานกับโค้ดเบสที่ทั้งน่าเกลียดและน่าทึ่งขนาดนั้นอีกเลย
  • โค้ดเบสน่าเกลียดทุกตัวที่ผมเจอหลังจากนั้น ไม่เคยก้าวข้ามความต้องการเรื่องความสม่ำเสมอได้
  • บางทีอาจเป็นเพราะนักพัฒนาที่ "จริงจัง" ทิ้งโค้ดเบสนั้นไปนานแล้ว เหลือไว้แค่เด็กฝึกงานกับ junior developer ที่ค่อนข้างสะเปะสะปะ
  • หรืออาจเป็นเพราะไม่มีชั้นกลางคั่นระหว่างนักพัฒนากับผู้ใช้ ไม่มีการแปลความ ไม่มีการเก็บ requirement ไม่มีการ์ดงาน มีเพียงคุณยืนอยู่หน้าโต๊ะของเจ้าหน้าที่บริการลูกค้าแล้วถามว่าจะทำให้ชีวิตพวกเขาดีขึ้นได้อย่างไร
  • ผมคิดถึงความเชื่อมโยงโดยตรงแบบนั้น คิดถึง feedback ที่รวดเร็ว การไม่จำเป็นต้องวางแผนใหญ่โต และความเชื่อมต่อที่เรียบง่ายระหว่างปัญหากับโค้ด
  • บางทีมันอาจเป็นแค่ nostalgia แบบไร้เดียงสา แต่เหมือนกับที่ผมอยากย้อนกลับไปยังช่วงเวลาที่แย่ที่สุดบางปีในวัยเด็ก ทุกครั้งที่ต้องเผชิญกับ "enterprise design pattern" ใจของผมก็จะย้อนกลับไปหาโค้ดเบสที่ทั้งงดงามและน่าสยดสยองนั้นเสมอ

ความเห็นของ GN⁺

  • บทความนี้อาจให้ทั้งความรู้สึกร่วมและความสบายใจกับนักพัฒนาที่ต้องรับมือกับระบบ legacy เพราะมันแสดงให้เห็นว่าแม้โค้ดเบสจะไม่สมบูรณ์แบบ ก็ยังมีคุณค่าและมีสิ่งให้เรียนรู้อีกมาก
  • อย่างไรก็ตาม ปัญหาในโค้ดเบสนี้ไม่ควรถูกทำให้ดูโรแมนติก เมื่อ technical debt สะสมมากขึ้น ความเร็วในการพัฒนาจะลดลงอย่างมากและการบำรุงรักษาจะยากขึ้น ในระยะยาวมันมีต้นทุนสูงกว่าเดิมมาก
  • การยอมแพ้ต่อความพยายามในการปรับปรุงโค้ดเบสแล้วเดินหน้าสะสมวิธีแก้เฉพาะหน้าไปเรื่อยๆ ไม่ใช่คำตอบ จำเป็นต้องมีกลยุทธ์ในการค่อยๆ ปรับปรุงหรือย้ายระบบ legacy
  • วัฒนธรรมและกระบวนการพัฒนาก็สำคัญ การทำ code review การออกแบบสถาปัตยกรรม และการเขียนเอกสารตามแนวปฏิบัติทางวิศวกรรมที่ดี ล้วนช่วยให้สร้างโค้ดเบสที่ดีกว่าได้
  • การสื่อสารอย่างใกล้ชิดกับผู้ใช้และ feedback ที่รวดเร็วเป็นข้อดี สิ่งที่เหมาะที่สุดคือส่งเสริมสิ่งนี้ผ่านวิธีการอย่าง Agile ควบคู่ไปกับการดูแลคุณภาพโค้ด
  • ท้ายที่สุดแล้ว ทุกอย่างคือเรื่องของความสมดุล แทนที่จะไล่ตามความสมบูรณ์แบบ สิ่งสำคัญคือการตอบสนองความต้องการของผู้ใช้อย่างยั่งยืน พร้อมกับจัดการ technical debt ไปด้วย

3 ความคิดเห็น

 
bus710 2024-08-07

งานที่ได้รับในบริษัทแรกตอนทำงานเป็นนักพัฒนาเฟิร์มแวร์ก็คือ วิเคราะห์แอสเซมบลีโค้ดที่ดึงออกมาจากไฟล์ hex ของ 8051 MCU ของผลิตภัณฑ์ที่ไม่มีทั้งนักพัฒนาและซอร์สโค้ด แล้วนำกลับมาเขียนใหม่เป็นภาษา C นี่แหละครับ…
อย่างน้อยก็ยังมีผลิตภัณฑ์ที่ใช้งานได้อยู่ เลยค่อย ๆ ดูโค้ดแล้วทดสอบกับตัวผลิตภัณฑ์ไปด้วย จนพอทำมันสำเร็จได้แบบทุลักทุเล…
เคยโดนขู่ว่าถ้าไปทริปทำงานต่างจังหวัดแล้วซ่อมไม่ได้ก็ให้ตัดนิ้วทิ้งแล้วค่อยกลับมาด้วย
แล้วก็เคยมีบั๊กที่หาสาเหตุไม่เจอ ซึ่งที่จริงแล้วต้นตอมาจากลิฟต์ที่อยู่หลังผนังตรงจุดติดตั้งอุปกรณ์
ยังจำได้เลยว่าเคยเข้าไปติดตั้งโน่นนี่ในสถานที่ประชุม APEC ที่เกาะดงแบ็ก เมืองปูซาน ก่อนเปิดใช้งานอย่างเป็นทางการด้วย ฮ่าๆ

 
GN⁺ 2024-08-04
ความคิดเห็นจาก Hacker News
  • เคยดูแลแอปพลิเคชัน VB ที่ซับซ้อนในบริษัทแรก

    • มีตัวแปร global จำนวนมากที่ปรับตามความต้องการของลูกค้าแต่ละราย
    • บั๊กจะไม่เกิดเฉพาะตอนอยู่ในโหมดดีบัก จึงต้องให้ลูกค้าติดตั้ง Visual Studio และสอนให้รันในโหมดดีบัก
    • ไม่มีการจัดการเวอร์ชัน และมีการคัดลอกโค้ดไว้หลายโฟลเดอร์จนเกิดความสับสน
    • เมื่อเกิดปัญหากับลูกค้า ก็แก้โค้ดหน้างาน
    • ไม่มีข้อตกลงว่าเวอร์ชันสุดท้ายคืออะไร ทำให้ลูกค้าแต่ละรายใช้คนละเวอร์ชัน
  • ในงานแรก เคยบำรุงรักษาผลิตภัณฑ์ legacy ที่เขียนด้วย COBOL และ Java

    • ทำงานโดย checkout ไฟล์ทีละไฟล์จากระบบ source control
    • มีไฟล์ jar "master" สำหรับลูกค้าแต่ละราย ซึ่งใช้แทนผลิตภัณฑ์ที่คอมไพล์เสร็จล่าสุด
    • หลังแก้โค้ดแล้วจะรันสคริปต์เพื่ออัปเดตไฟล์ jar master
    • โค้ดเบสไม่ได้คอมไพล์รวมทั้งระบบ และมีการแพตช์ด้วยมือ
    • ทำให้เกิดความไม่สอดคล้องกันจำนวนมากในโค้ดเบส
    • ใช้เวลา 2 ปีกว่าจะย้ายไปใช้ git ได้
  • เคยรีแฟกเตอร์สคริปต์ Perl ที่ยาวกว่า 12,000 บรรทัด

    • ผู้เขียนเดิมไม่รู้จัก array จึงใช้ string มาสร้าง array เอง
    • หลังรีแฟกเตอร์ โค้ดลดเหลือ 200 บรรทัด
  • รู้สึกถึงความต่างระหว่างทฤษฎีกับความเป็นจริง

    • หลายบริษัทและหลายโปรเจ็กต์ผ่านกระบวนการคล้ายกัน
    • แม้จะคุยกันถึงวิธีที่ควรเป็น แต่ในความจริงก็มักเดินไปในแบบที่ใช้งานได้จริง
  • โค้ดเบสของไคลเอนต์ Telegram บน Android มีความซับซ้อนมาก

    • ไฟล์ใหญ่จน GitHub เลิกพยายามเรนเดอร์
    • การเรนเดอร์และการโต้ตอบของข้อความทั้งหมดถูกรวมไว้ในคลาสเดียว
    • ได้รับสิทธิ์ให้รีแฟกเตอร์ แต่สุดท้ายไม่ได้ลงมือทำ
  • ในงานแรก เคยแก้ปัญหา memory ของงานทำรายงานได้

    • หัวหน้าทีมและเจ้านายประหลาดใจมาก
    • นักพัฒนาคนอื่นไม่ชอบ Linux และพยายามย้ายไป .NET
  • ชอบประสบการณ์ที่ได้คุยกับลูกค้าโดยตรงและแก้ปัญหาให้

    • สร้าง prototype ได้อย่างรวดเร็วแล้วให้ลูกค้าทดสอบ
    • แม้โค้ดเบสจะรก แต่ก็ทำงานได้ดี
  • เคยสร้างระบบที่รองรับหลายตลาด

    • เริ่มจากคัดลอกระบบเดิมเพื่อทำเวอร์ชันนานาชาติ
    • ผ่านไป 5 ปี ระบบไม่ได้แยกจากกัน แต่กลับพันกันไปหมด
    • ระบบใหม่กับระบบเก่าถูกผูกติดกันอย่างแน่นแฟ้น
  • ในงานแรก มีคนที่ประสบการณ์น้อยอยู่มาก

    • เมื่อสั่งสมประสบการณ์แล้วก็ย้ายไปทำงานที่ดีกว่า
  • มีการอธิบายวิธีแก้ปัญหาใน SQL Server สมัยใหม่

    • เช่น การปรับแต่งตามลูกค้า, shared ID, ตารางปฏิทินแบบทำมือ, table partitioning, delayed reporting replica เป็นต้น
    • โค้ดเบสแบบผสม VB กับ C# เป็นเรื่องที่พบได้บ่อย
    • สามารถใช้เครื่องมือแปลงอัตโนมัติได้
 
reddiana 2024-08-05

ตารางกำหนดเลขลำดับ (SequenceKey) กับตารางวันทำการ (Calendar)
ชวนให้หวนคิดถึงความหลังเลยครับ ตอนนี้ไม่แน่ใจว่าทำกันอย่างไรแล้ว แต่เมื่อก่อนเป็นตารางที่ใช้กันทั่วไป ถ้าทำ SI ฝั่งส่วนงานกลางของระบบก็มักจะพัฒนาฟังก์ชันที่เกี่ยวข้องไว้ครับ