34 คะแนน โดย GN⁺ 2024-07-12 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • การบัญชีแทบไม่ได้เปลี่ยนแปลงมากนักตลอดหลายร้อยปีที่ผ่านมา
  • ถึงอย่างนั้น ก็ยังมีความสับสนมากเกี่ยวกับวิธีที่ถูกต้องในการสร้างซอฟต์แวร์สำหรับระบบการเงิน
  • บทความนี้จะแชร์บทเรียนจากประสบการณ์การสร้างระบบการเงินในองค์กรขนาดใหญ่
  • แม้จะเน้นที่การสร้างระบบบัญชีเป็นหลัก แต่หลักการเหล่านี้ก็ใช้ได้กับระบบการเงินโดยทั่วไปเช่นกัน

คำจำกัดความของคำศัพท์ทางการเงินพื้นฐาน

  • บัญชีแยกประเภททั่วไป (General Ledger, GL): บันทึกทางบัญชีหลักของบริษัทที่สรุปรายการธุรกรรมทางการเงินทั้งหมดในช่วงเวลาที่กำหนด โดยอาจมองได้ว่าเป็นผลรวมจากบัญชีแยกประเภทย่อยที่เกี่ยวข้อง
  • บัญชีแยกประเภทย่อย (Sub-ledger): มีรายละเอียดของธุรกรรมแต่ละรายการทั้งหมดที่เกี่ยวข้องกับ GL ใด GL หนึ่ง ระเบียนในบัญชีแยกประเภทย่อยจะมีข้อมูลที่ละเอียดกว่าบัญชีแยกประเภททั่วไปมาก (เช่น ลูกค้าเฉพาะราย รายการสินค้าเฉพาะในคำสั่งซื้อ ฯลฯ) ความแตกต่างของข้อมูลระหว่างบัญชีแยกประเภทย่อยกับ GL จะแตกต่างกันไปตามประเภทธุรกิจและปริมาณข้อมูลที่จัดการ ธุรกิจขนาดเล็กบางแห่งอาจดำเนินงานได้โดยไม่ต้องมีบัญชีแยกประเภทย่อย แต่ถ้าขนาดเล็กมากก็มีโอกาสน้อยที่จะต้องใช้ซอฟต์แวร์แบบกำหนดเอง
  • บันทึกทางการเงิน (Financial Record): คำเรียกรวมของบัญชีแยกประเภททั่วไปและบัญชีแยกประเภทย่อย
  • สาระสำคัญ (Material): บ่งชี้ว่าความคลาดเคลื่อนของข้อมูลในงบการเงินส่งผลต่อการตัดสินใจของผู้มีส่วนได้ส่วนเสียที่มีเหตุผลหรือไม่ คำจำกัดความนี้จงใจให้ค่อนข้างกำกวม เพราะแต่ละบริษัทมีเกณฑ์สาระสำคัญต่างกัน ตัวอย่างเช่น สิ่งที่สำคัญสำหรับบริษัทที่มีรายได้ต่อปี 250,000 ดอลลาร์ อาจไม่สำคัญสำหรับบริษัทที่มีรายได้ต่อปี 1 พันล้านดอลลาร์ จากมุมมองด้านการออกแบบ คุณค่าหลักของแนวคิดนี้คือการใช้จัดหมวดหมู่ข้อมูลทางการเงินประเภทต่าง ๆ

High Level Data Flow

Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger

เป้าหมายหลัก 3 ประการของระบบบัญชี

  1. ความถูกต้อง (Accurate): บันทึกทางการเงินต้องสะท้อนสถานะที่ทราบของธุรกิจ
  • ตัวอย่าง: หากขายสินค้า 10 ชิ้น ราคาชิ้นละ $9.99 ยอดรวมในบันทึกทางการเงินนั้นต้องเป็น $99.90
  • เรื่องนี้อาจดูชัดเจน แต่เมื่อมีการรวมธุรกรรมหลายพันหรือหลายล้านรายการ ความคลาดเคลื่อนร้ายแรงอาจเกิดขึ้นได้จากการบวกข้ามระบบแบบง่าย ๆ หรือจากข้อผิดพลาดในการปัดเศษ
  • บันทึกจาก Wasteman

    • คนมักพูดว่าการตั้งชื่อเป็นปัญหาที่ยากที่สุดในวิทยาการคอมพิวเตอร์ แต่ผมคิดว่าการบวกเลขยากเป็นอันดับถัดมา
    • ช่วงหลายปีที่ทำงานกับระบบการเงินขนาดใหญ่ ผมเห็นมานับครั้งไม่ถ้วนว่า bug เล็กมากสามารถทำให้ข้อมูลต่างกันอย่างมหาศาลได้
    • อย่าเริ่มพูดถึงการหาผลรวมด้วย float เลย ต้องใช้จำนวนเต็มเสมอ ผมเรียนรู้บทเรียนนั้นมาอย่างยากลำบาก
  • บันทึกทางการเงินต้อง ครบถ้วน (complete)
    • พูดให้ชัดเจนขึ้นคือ บัญชีแยกประเภทย่อยและบัญชีแยกประเภททั่วไปต้องแสดงกิจกรรมทางธุรกิจทั้งหมดที่เกิดขึ้น ณ ช่วงเวลาหนึ่งอย่างครบถ้วน
    • หากมีเหตุการณ์ที่เกิดขึ้นแล้วแต่ไม่มีอยู่ในบันทึกทางการเงิน แสดงว่าระบบไม่ครบถ้วน
    • นี่ไม่ได้หมายความว่าไม่อนุญาตให้มี eventual consistency
    • แต่เราต้องรู้ว่าข้อมูลจะครบถ้วนเมื่อใด เพื่อจะได้บอกผู้มีส่วนได้ส่วนเสียว่านี่คือข้อมูลที่ยืนยันแล้ว
  • บันทึกจาก Wasteman

    • การรับประกันความครบถ้วนก็เป็นปัญหาที่ยากอย่างน่าประหลาดใจเช่นกัน
    • เมื่อระบบขยายใหญ่ขึ้น ข้อมูลที่ไหลผ่านหลายระบบอาจถูกเปลี่ยนรูปหรือหายไปโดยไม่ตั้งใจ
  1. ตรวจสอบได้ (Auditable): บันทึกทางการเงินต้องสามารถตรวจสอบได้ง่าย เพื่อให้ผู้มีส่วนได้ส่วนเสียตรวจพบข้อผิดพลาดและวัดผลการดำเนินงานของธุรกิจได้อย่างถูกต้อง
  2. ทันเวลา (Timely): ระบบบัญชีต้องตอบโจทย์ความต้องการเฉพาะของธุรกิจ
  • สำหรับธุรกิจขนาดเล็ก การเทตัวเลขทั้งหมดตอนสิ้นเดือนอาจเพียงพอ แต่ธุรกิจขนาดใหญ่มักต้องการระบบที่เกือบเรียลไทม์
    • สิ่งนี้ช่วยให้ติดตามสถานะทางการเงินได้ตลอดทั้งเดือน ตัดสินใจจากข้อมูลการเงินได้เร็วขึ้น และลดความเร่งรีบในการปิดงบช่วงต้นเดือนหรือต้นไตรมาส
  • ไม่ว่าความต้องการนั้นคืออะไร ระบบบัญชีของเราต้องตอบโจทย์ความต้องการของธุรกิจ และต้องเป็นไปตามความหมายของคำว่า ทันเวลา สำหรับพวกเขา
  • บันทึกจาก Wasteman

    • ผู้คนมักหลงประเด็นไปกับการถกเถียงเรื่องระบบ batch เทียบกับ streaming เมื่อพูดถึงความทันเวลา
    • ในมุมมองของผม สำหรับระบบส่วนใหญ่ นี่ไม่ใช่ความแตกต่างที่สำคัญ
    • มันสำคัญก็ต่อเมื่อคุณแคร์ latency ที่สั้นมากในระดับวินาทีถึงนาที
    • แต่ก็น่าแปลกที่บ่อยครั้งผมได้ยินคนเถียงกันว่าควรทำแบบไหน ทั้งที่ผู้ใช้ปลายทางไม่จำเป็นต้องเห็นการอัปเดตเกินวันละไม่กี่ครั้ง
    • การที่มีคนร้องขอ ไม่ได้แปลว่าจำเป็นจริงเสมอไป

หลักการทางวิศวกรรมหลัก 3 ประการที่ระบบบัญชีควรยึดถือ

  1. ความไม่เปลี่ยนแปลงของข้อมูล (Immutability) และความทนทานถาวร (Durability)
  • ช่วยให้ตรวจสอบย้อนหลังได้ ซึ่งเป็นประโยชน์ต่อการดีบักและความถูกต้อง
  • หากข้อมูลไม่เปลี่ยนแปลง เราจะสามารถบันทึกสถานะของระบบได้ทุกเมื่อ
  • สิ่งนี้ทำให้การคำนวณโลกใหม่จากสถานะก่อนหน้าเป็นเรื่องง่ายมาก เพราะไม่มีสถานะใดสูญหาย
  • เมื่อข้อมูลถูกบันทึกลงในบันทึกทางการเงินแล้ว จะลบออกไม่ได้
  • การแก้ไขใด ๆ ต่อระบบต้องแสดงเป็นธุรกรรมทางการเงินใหม่
    • ตัวอย่าง: หากระบบมี bug ทำให้บริการที่ควรมีราคา $900 ถูกบันทึกผิดว่าเป็น $1000
    • ในการแก้ไขความผิดพลาดนี้ ต้องยกเลิกรายการบัญชีที่ผิดก่อน แล้วจึงลงรายการบัญชีใหม่ด้วยจำนวนเงินที่ถูกต้อง
  1. ข้อมูลควรถูกบันทึกในระดับที่เล็กที่สุด (Data recorded at the smallest grain)
  • คล้ายกับหลักการข้างต้น นี่ก็สำคัญอย่างมากต่อการสร้าง audit trail ที่ชัดเจน
  • แม้ว่ารายงานทางการเงินและบัญชีแยกประเภททั่วไปจะเป็นข้อมูลสรุปรวม แต่ทั้งหมดก็คำนวณมาจากเหตุการณ์ที่ละเอียดกว่า
  • เมื่อข้อมูลมีสิ่งที่อธิบายไม่ได้ เราจำเป็นต้องมีข้อมูลที่ละเอียดที่สุดเพื่อดีบักว่าปัญหาคืออะไร
  • การเก็บข้อมูลในระดับความละเอียดต่ำสุดยังทำให้แก้ไขข้อมูลอนุพันธ์จากชุดข้อมูลนั้นได้ง่ายมาก
    • หากมีชุดข้อมูลไม่เปลี่ยนแปลงเพียงชุดเดียวที่เป็นแหล่งความจริงหลักสำหรับทุกมุมมองของข้อมูลนั้น
    • การแก้ไขมุมมองก็เพียงแค่แก้ไขข้อมูล แล้วรัน pipeline ที่สร้างมุมมองนั้นใหม่อีกครั้ง
  • ในทำนองเดียวกัน เมื่อผู้ทำบัญชีเตรียมพร้อมจะปิดบัญชี
    • พวกเขาจะกระทบยอดธุรกรรมทั้งหมดที่เกิดขึ้นกับยอดคงเหลือของบัญชี เพื่อตรวจสอบว่าบัญชีถูกต้องหรือไม่
    • หากพบความไม่ตรงกัน ก็สามารถเจาะลงไปหาธุรกรรมที่เป็นต้นเหตุได้อย่างแม่นยำ
  1. ต้องมีคุณสมบัติ Idempotency
  • ทุกเหตุการณ์ทางการเงินต้องถูกประมวลผลเพียงครั้งเดียว และความซ้ำซ้อนในบันทึกทางการเงินจะก่อให้เกิดความคลาดเคลื่อนอย่างชัดเจน
  • ด้วยเหตุนี้ โค้ดทั้งหมดที่สร้างบันทึกทางการเงินจึงต้องเป็น idempotent
    • idempotent หมายถึงคุณสมบัติที่เมื่อนำการดำเนินการเดิมไปใช้หลายครั้งแล้ว ผลลัพธ์ยังคงเหมือนเดิม
    • กล่าวคือ แม้จะประมวลผลเหตุการณ์ทางการเงินหลายครั้ง ผลลัพธ์ก็ต้องเหมือนกับการประมวลผลครั้งแรก

แนวปฏิบัติที่ดี

  • ควรใช้จำนวนเต็มในการแทนจำนวนเงินทางการเงิน: ทำให้การคำนวณเลขคณิตง่ายขึ้นมาก ควรหลีกเลี่ยง float
  • ควรรองรับความละเอียดของจำนวนเงินทางการเงินเพื่อลดการสูญเสียความแม่นยำระหว่างการแปลงสกุลเงิน
    • หากจัดการเฉพาะดอลลาร์ การแทนค่าเป็นหน่วยเซนต์อาจเพียงพอ
    • สำหรับธุรกิจระดับโลก ควรใช้หน่วย micro หรือทศนิยมอย่าง DECIMAL(19, 4)
    • การใช้ทศนิยมเป็นที่นิยมในระบบการเงินทั่วไป แต่ในระบบการเงินด้านโฆษณา หน่วย micro คือมาตรฐาน
  • ควรใช้วิธีปัดเศษที่สม่ำเสมอ: วิธีปัดเศษที่ต่างกันอาจทำให้ยอดที่คาดหวังต่างไปอย่างมีนัยสำคัญ
    • วิธีปัดค่าทุกค่าที่ 5 ขึ้นไปเป็นเลขนัยสำคัญถัดไป และ 4 หรือต่ำกว่าปัดลง
    • หรือวิธีปัดขึ้นเสมอ เป็นต้น
    • สิ่งสำคัญคือการรักษาความสม่ำเสมอทั่วทั้งระบบ (ถ้าต่างกันรายการละ 1 เซนต์ เมื่อมี 10 ล้านรายการก็จะต่างกัน $100k)
  • ควรเลื่อนการแปลงสกุลเงินออกไปให้นานที่สุดเท่าที่ทำได้: การแปลงสกุลเงินล่วงหน้าอาจทำให้สูญเสียความแม่นยำ
    • เลื่อนการแปลงสกุลเงินออกไปจนกว่าจะมีการรวมยอดในสกุลเงินท้องถิ่นเสร็จ
  • ใช้การแทนเวลาแบบจำนวนเต็ม: อาจเป็นประเด็นถกเถียงเล็กน้อย แต่แนะนำอย่างยิ่ง
    • ไลบรารีต่าง ๆ ที่ใช้ parse timestamp เป็น object มักจัดการแตกต่างกัน
    • เพื่อหลีกเลี่ยงความปวดหัวเหล่านี้ การใช้จำนวนเต็มจะดีกว่า
    • Unix timestamp หรือ integer datetime ที่อิงกับ UTC ก็ใช้งานได้สมบูรณ์
    • ยิ่งมีการแปลงข้อมูลระหว่างระบบน้อยเท่าไรยิ่งดี
    • บันทึกจาก Wasteman

      • ยังไม่ได้พูดถึง bug ที่เกี่ยวกับเวลาออมแสงเลย หากใช้จำนวนเต็มที่เพิ่มขึ้นเรื่อย ๆ จะหลีกเลี่ยงปัญหานี้ได้ทั้งหมด
      • ถ้ายังยืนยันจะใช้ datetime อย่างน้อยก็ควรใช้ UTC เป็นเรื่องน่าแปลกที่บริษัทขนาดใหญ่มากจำนวนไม่น้อยยังใช้ timestamp ที่ไม่ใช่ UTC

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

 
kandk 2024-07-12

อันนี้มีประโยชน์จริง ๆ นะครับ ถ้าแปลงชนิดข้อมูล (decimal, float, double) หรือปัดเศษไปแบบไม่คิดและไม่คุยกันในงานจริงก่อน อาจเกิดปัญหาใหญ่ได้ครับ

 
GN⁺ 2024-07-12
ความคิดเห็นจาก Hacker News
  • เน้นย้ำความสำคัญของการใช้แนวทางการปัดเศษที่สม่ำเสมอ

    • กล่าวถึงความจำเป็นในการจัดการกลยุทธ์การปัดเศษในโค้ดโดเมนธุรกิจแยกตามรหัสประเทศ
  • แนะนำวิธีแสดงเวลาเป็นจำนวนเต็ม

    • แนะนำให้ใช้ Unix timestamp หรือวันเวลา UTC แบบอิงจำนวนเต็ม
    • ใช้ได้กับเวลาที่เฉพาะเจาะจงทั้งในอดีตหรืออนาคต
    • ตัวอย่าง: ในกรณีของบริษัทที่มีนโยบายยกเลิกภายใน 48 ชั่วโมง สามารถคำนวณ timestamp ในอนาคตได้
    • เช่น เวลาสิ้นสุดปีภาษีของแต่ละประเทศ ควรจัดเก็บเขตเวลาไว้
  • แนะนำให้ใช้ฐานข้อมูลเชิงสัมพันธ์สำหรับระบบบัญชี

    • ให้คุณสมบัติ ACID
    • มีชนิดข้อมูลตัวเลขแบบความแม่นยำไม่จำกัด พร้อมการคำนวณและโหมดการปัดเศษที่ผ่านการพิสูจน์แล้ว
    • สามารถคำนวณและทำรายงานผ่าน SQL ได้
    • หากจ้างผู้เชี่ยวชาญ SQL ก็สามารถจัดทำรายงานที่สวยงามได้
    • ให้ประสิทธิภาพสูง รวมถึงเครื่องมือป้องกันและกู้คืนจากภัยพิบัติ
    • แชร์ประสบการณ์สร้างระบบการเงินสำหรับบริษัทข้ามชาติ
  • เป้าหมายหลักของระบบบัญชีคือความถูกต้อง ความสามารถในการตรวจสอบย้อนหลัง และความทันเวลา

    • กล่าวถึงความสม่ำเสมอว่าเป็นองค์ประกอบสำคัญด้วย
    • มีมิติเวลาหลายแบบ และจำเป็นต้องให้มุมมองที่สม่ำเสมอตามแต่ละมิติ
    • ตัวอย่าง: หากธุรกรรมเสร็จสมบูรณ์แล้วแต่ยังไม่ชำระราคา ควรรวมไว้เฉพาะในบัญชี front office
  • ความเห็นเกี่ยวกับความสมบูรณ์ของระบบบัญชี

    • ควรตั้งสมมติฐานว่าไม่ใช่ทุกธุรกรรมจะถูกประมวลผลทันเวลา
    • จำเป็นต้องประมวลผลธุรกรรมผ่าน ledger หลายชั้นและมีขั้นตอนการกระทบยอด
  • สำหรับธุรกิจระดับโลก แนะนำให้ใช้ทศนิยมอย่างน้อย 8 ตำแหน่ง

    • เน้นย้ำถึงความจำเป็นในการเลื่อนการแปลงอัตราแลกเปลี่ยนออกไปให้นานที่สุดเท่าที่ทำได้
    • การแปลงอัตราแลกเปลี่ยนเป็นส่วนหนึ่งของภาระผูกพันทางกฎหมายและทางบัญชี
  • กล่าวถึงความสำคัญของส่วนติดต่อผู้ใช้ (UI)

    • แสดงความผิดหวังต่อ UI ของซอฟต์แวร์บัญชี
    • เน้นย้ำความจำเป็นของโซลูชัน UI ที่ดีกว่า
  • อธิบายความแตกต่างระหว่างการประมวลผลแบบแบตช์กับแบบสตรีมมิง

    • การออกแบบของทั้งสองระบบแตกต่างกันโดยสิ้นเชิง
    • มีความยากในการประมวลผลข้อมูลเดิมจำนวนมาก
  • แชร์ประสบการณ์การสร้างระบบใบแจ้งหนี้ด้วย TypeScript

    • อธิบายวิธีป้องกันข้อผิดพลาดจากการปัดเศษ
    • มีการให้ลิงก์ที่เกี่ยวข้อง
  • แนะนำให้ใช้คลาสจาก standard library

    • แนะนำให้ใช้ BigDecimal ของ Java และ Decimal ของ Python
    • เน้นย้ำความจำเป็นของมาตรฐานในการคง scale เดิมหรือจัดเก็บ scale
  • อธิบายความยากของการปัดเศษและการแชร์ข้อมูล

    • ปัญหาในการแชร์ข้อมูลกับระบบที่รองรับได้เพียงทศนิยม 2 ตำแหน่ง
  • แชร์ประสบการณ์ทำงานกับ API ของธนาคาร 10 อันดับแรกในสหรัฐฯ

    • กล่าวถึงปัญหาความสม่ำเสมอของวิธีจัดเก็บอัตราดอกเบี้ย
    • เน้นย้ำความสำคัญของความสม่ำเสมอ
  • แนะนำ "Accounting Patterns" ของ Martin Fowler

    • แชร์ประสบการณ์การสร้างระบบจัดการเหตุการณ์ทางการเงิน
    • มีการให้ลิงก์ที่เกี่ยวข้อง