วิศวกรไม่อาจทำพลาดแบบสตาร์ทอัพได้เมื่อสร้าง Ledger
(news.alvaroduran.com)- ในฟินเทคที่เกี่ยวข้องกับเงินจริง ความต่างเพียงไม่กี่เซนต์ก็ทำลายความเชื่อมั่นของผู้ใช้ได้ และสตาร์ทอัพซื้อขายหุ้นที่บันทึกธุรกรรมด้วยวิธี single-entry ประสบปัญหา dancing cents
- Ledger แบบ single-entry เหลือไว้เพียงเงินเข้าออก จึงยากต่อการตามรอยสาเหตุที่ทำให้เกิดความต่าง เช่น อัตราแลกเปลี่ยนเงินตรา, rounding to even ของโบรกเกอร์, หรือค่าธรรมเนียม FINRA TAF
- Ledger แบบ double-entry มองว่าเงินเคลื่อนย้ายจากบัญชีหนึ่งไปยังอีกบัญชีหนึ่งเสมอ และแยก Accounts, Entries, Transactions เพื่อบันทึกทั้งต้นทางและปลายทาง
- หากกำหนดสถานะ pending, discarded, posted ของ Entries และเงื่อนไขการ post ของ Transactions ให้ชัดเจน จะจัดการ partial failure และ Entries สำหรับชดเชยได้ปลอดภัยขึ้น
- Ledger เป็นทั้งอินเทอร์เฟซสำหรับรายงานทางบัญชีและเป็น system of record ที่รักษาความสอดคล้องของเงิน ดังนั้นเมื่อขยายระบบ ความตึงเครียดระหว่าง availability กับ strong consistency จะเพิ่มขึ้น
ความต่างไม่กี่เซนต์ที่ทำลายความเชื่อมั่นของผู้ใช้
- สตาร์ทอัพที่สร้างแพลตฟอร์มซื้อขายหุ้นปฏิบัติตามหลัก “make it work, make it right, make it fast” และไม่ได้สร้าง ระบบบัญชีแบบ double-entry ตั้งแต่แรก
- ไม่นานหลังเปิดตัว ยอดเงินที่ vendor รับรู้กับยอดเงินที่ระบบภายในรับรู้คลาดเคลื่อนกันทีละไม่กี่เซนต์
- ภายในเรียกปัญหานี้ว่า dancing cents
- หากผู้ใช้ซื้อหุ้น Apple มูลค่า 5 ดอลลาร์ แต่คำสั่งซื้อแสดงเป็น 4.98 ดอลลาร์ ผู้ใช้จะติดต่อฝ่ายสนับสนุนลูกค้าทันที
- แก่นของปัญหาไม่ได้อยู่ที่จำนวนเงินที่เสียไป แต่อยู่ที่ ความเชื่อมั่นและการเติบโต
- ผู้ใช้ที่ไม่พอใจจะไม่แนะนำบริการ ทำให้การเติบโตของสตาร์ทอัพติดขัด
- CEO สั่งให้ฝ่ายสนับสนุนลูกค้าชดเชยด้วยมือเป็นจำนวนไม่กี่เซนต์เมื่อเกิดธุรกรรมผิดพลาด
- ถึงขั้นสร้าง Slack bot เพื่อจัดการเรื่องนี้
เงินไม่ได้ติดตามแค่ยอดคงเหลือปัจจุบัน แต่รวมถึงมูลค่าในอนาคตด้วย
- Ledger คือ ระบบที่ติดตามเงิน
- เงินไม่ใช่แค่ยอดคงเหลือปัจจุบันธรรมดา แต่จำเป็นเมื่อแสดงมูลค่าที่จะได้รับหรือจะต้องจ่ายในอนาคต
- ในเชิงแนวคิด เงินคือ สินทรัพย์ในอนาคต
- หากบันทึกเพียงเงินเข้าออก เช่น “ผู้ใช้จ่าย 5 ดอลลาร์”, “ผู้ใช้จ่าย 6 ดอลลาร์” จะไม่เพียงพอต่อการอธิบายกระแสการเงินจริง
- การโอนเงินผ่านธนาคารนั้นช้าเมื่อเทียบกับอินเทอร์เน็ต และธนาคารจำนวนมากชำระบัญชีการโอนใน วันทำการถัดไป
- เมื่อการชำระเงินเสร็จสิ้น จะมีความมั่นใจว่าสักวันหนึ่งจะได้รับเงิน
- แต่หุ้นต้องซื้อผ่านโบรกเกอร์ในตอนนี้
- ต้องแสดงทั้งยอด pending ที่จะชำระบัญชีในอีกไม่กี่วันและยอดที่ออกไปยังโบรกเกอร์ทันทีพร้อมกัน
- ในวิธี single-entry เมื่อเกิดข้อผิดพลาด การ rollback ทำได้ยากมาก และในบาง corner case ก็ไม่สามารถแม้แต่จะพยายาม rollback ได้
เหตุผลที่ Ledger แบบ single-entry ขัดขวางการดีบัก
- Ledger แบบ single-entry แสดงกระแสเงินได้ แต่ไม่สามารถอธิบายได้ว่า กระแสนั้น เกิดขึ้นเพราะอะไร
- หากต้องการหาสาเหตุของการเคลื่อนย้ายเงินเฉพาะรายการ ต้องนำข้อมูลจากหลาย model มาปะติดปะต่อกัน และในบางกรณีก็ทำไม่ได้ด้วยซ้ำ
- Ledger แบบ double-entry เก็บทั้งสิ่งที่เกิดขึ้นและเหตุผลที่เกิดขึ้นไว้ด้วยกัน
- การเคลื่อนย้ายเงินทั้งหมดเกิดจากบัญชีหนึ่งไปยังอีกบัญชีหนึ่ง
- มีการบันทึกว่าแต่ละเซนต์ออกจากบัญชีใดและไปยังบัญชีใด
- ปัญหา dancing cents แก้ได้ยากในระบบ single-entry
- ยากที่จะรู้ว่าไม่กี่เซนต์ที่หายไปเกิดจากอัตราแลกเปลี่ยนหรือไม่
- อาจเกิดจาก rounding to even mechanism ของโบรกเกอร์
- อาจเกิดจาก FINRA TAF fees ที่เรียกเก็บเมื่อสิ้นวัน
- หากไม่เข้าใจว่าระบบทำงานอย่างไร การกำจัด bug ก็ทำได้ยาก
โมเดลข้อมูลของ Ledger: Accounts, Entries, Transactions
- วิศวกรจำนวนมากเมื่อเริ่มติดตามเงินครั้งแรก มักใส่จำนวนเงินไว้ใน domain model
- เช่น มี attribute price ใน Order หรือมีคอลัมน์ amount ในตาราง expenses
- นี่คือแนวทาง balance as property
- วิธีนี้ทำงานได้เร็วในช่วงแรก แต่เมื่อเวลาผ่านไป reporting จะซับซ้อนและช้าลง อีกทั้งการประมวลผลการชำระเงินและ analytics ก็ยากขึ้น
- หากงานรายงานตอนกลางคืนใช้เวลาหลายชั่วโมง แนวทางนี้อาจเป็นสาเหตุรากฐาน
- ควรมอง Ledger เป็น โมเดลข้อมูลแยกต่างหาก ที่สามารถอนุมานธุรกรรมทางการเงินทั้งหมดของระบบได้
- entity สามอย่างเป็นโครงสร้างพื้นฐาน
- Accounts: bucket ของมูลค่า และเป็นมุมมองสำหรับดูว่ามูลค่าเปลี่ยนแปลงอย่างไรเมื่อเวลาผ่านไป
- Entries: กระแสเงินระหว่างบัญชี และแสดงการแลกเปลี่ยนมูลค่าเสมอ
- Transactions: หน่วยที่รับประกันว่า Entries ถูกจับคู่และประมวลผลอย่างถูกต้อง
สถานะและความไม่เปลี่ยนรูปของ Entries
- Entries สามารถมีสถานะได้สามแบบคือ pending, discarded, posted
- Entry จะถูกสร้างในสถานะ pending เสมอ
- มูลค่าที่แลกเปลี่ยน
- ทิศทางคือ credit หรือ debit
- ข้อมูล account ที่อ้างอิงถึง
- การแสดงทิศทางของจำนวนเงินด้วยค่าบวกและค่าลบเป็นความผิดพลาดที่พบบ่อย
- โดยพื้นฐาน Entries เป็น immutable แต่ pending Entry สามารถถูก discarded เพื่อสร้าง posted Entry ได้
- อีกทางเลือกหนึ่งคือสร้าง reversal Entry เพื่อย้อน pending Entry
- แต่แนวทาง reversal Entry อาจทำให้ประวัติของบัญชีรกได้
- หากใช้สถานะ discarded เมื่อต้องการดู Entries ปัจจุบันก็เพียงยกเว้นรายการที่ตั้งค่า
discarded_atไว้ และยังไม่เสียประวัติ
- ในระบบ double-entry ยอดรวมของ credit Entries ที่ไม่ถูก discarded จะเท่ากับยอดรวมของ debit Entries ที่ไม่ถูก discarded
- ในเชิงแนวคิด หมายความว่าไม่ว่าจะย้ายเงินภายในกระเป๋าอย่างไร ยอดรวมก็ยังเท่าเดิม
- บัญชีพิเศษบางประเภทที่แทนโลกภายนอกและถูกรวมเข้าใน Profit and Loss statement เป็นข้อยกเว้นที่ไม่สามารถทำให้สมดุลได้
Transactions และการจัดการ partial failure
- Entries ถูกสร้างเป็นคู่ และ Transactions รับประกันว่ากระบวนการนั้นดำเนินไปตามที่ตั้งใจ
- Transaction จะถูก posted ได้ก็ต่อเมื่อ Entries ที่เชื่อมโยงอยู่ในสถานะ posted หรือ discarded และถูกแทนที่ด้วย posted Entries แล้วเท่านั้น
- Transaction ที่เกิด partial failure สามารถย้อนกลับเชิงความหมายได้ด้วย compensating Entries
- แนวทางนี้เข้ากันได้ดีกับ Saga pattern
- Saga แลก atomicity กับ availability
- แทนที่จะใช้ transaction ที่ช้าและล็อกหลายตาราง จะแบ่ง process ออกเป็นงานย่อยที่เล็กกว่าและ checkpoint ระหว่างทาง
- ระหว่างนั้น transaction อื่นสามารถทำงานได้ ทำให้ throughput ดีขึ้น
Accounts และ normal balance
- จากมุมมองของ Account เดียว Ledger จะดูเหมือนระบบ single-entry
- Account หนึ่งมีความสัมพันธ์แบบ one-to-many กับ Entries หลายรายการ
- ยอดคงเหลือรวมต้องตรงกับค่าที่ aggregate จากยอดคงเหลือย่อยของ Entries ที่เชื่อมโยงอยู่
- วิธีคำนวณยอดรวมจะแตกต่างกันตาม normal balance ของ Account
- ทางบัญชีควรหลีกเลี่ยงการผูกเครื่องหมายบวก·ลบไว้กับจำนวนเงินของ Entry
- บางบัญชีมี net credit เป็นปกติ และบางบัญชีมี net debit เป็นปกติ
- ตัวอย่างเช่น บัญชีเงินสดธนาคารอาจมี net debit เป็นปกติ แต่หากเบิกเกินบัญชีก็อาจกลายเป็นค่าลบได้
- normal credit balance หมายถึง ภาวะปกติที่ยอดรวมของ credit Entries ที่เชื่อมโยงมากกว่ายอดรวมของ debit Entries
- normal debit balance คือกรณีกลับกัน
ความตึงเครียดระหว่างระบบบัญชีกับระบบวิศวกรรม
- ใน Ledger มีสองระบบที่มีความต้องการแตกต่างกันอยู่ร่วมกัน
- Accounting system: อินเทอร์เฟซของ Ledger ที่มองจากภายนอก
- Engineering system: implementation ที่ Ledger ใช้มองตัวเอง
- Accounting system เปิดเผยข้อมูลที่ aggregate จากหลายมุมมอง
- Reporting
- Financial ratios
- Business Intelligence
- Engineering system ต้องรับประกันความสอดคล้องและความถูกต้องของข้อมูล
- ในบริษัทฟินเทค Ledger ทำหน้าที่เป็น source of truth คล้าย CRM ของทีมขาย
- เหตุผลที่การขยาย Ledger ทำได้ยากคือความต้องการของสองระบบนี้แตกต่างกัน
- Accounting system ต้องการ availability สูงและ latency ต่ำ
- Engineering system ต้องการ strong consistency และการตรวจสอบแบบ schema-on-write
แหล่งข้อมูลบัญชีสำหรับนักพัฒนา
- An Engineer’s Guide to Double-Entry Bookkeeping: อธิบายบัญชีแบบ double-entry พร้อมโค้ด Python พื้นฐาน
- Double Entry Accounting For Developers: คำอธิบายบัญชีแบบ double-entry สำหรับนักพัฒนาของ Django Hordak
- Modern Treasury ledger series part I: บทความแรกของซีรีส์ 6 ตอนเกี่ยวกับการขยาย Ledger
- Beancount, Martin Kleppmann, Modern Treasury Accounting for Developers: แหล่งข้อมูลสำหรับนักพัฒนาที่อธิบายบัญชีจากมุมมองที่แตกต่างกัน
- Peter Selinger accounting tutorial: tutorial สำหรับศึกษาให้ลึกขึ้น
- Uber, Square, Airbnb ก็เปิดเผยวิธีที่แต่ละบริษัทนำ Ledger แบบ double-entry ไปใช้ในระบบของตน
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
อยากให้ไปบอกแบบนั้นกับลูกค้าของ Synapse ดูบ้าง เงินหลายล้านดอลลาร์ หายไป
ธนาคารต้องกระทบยอดบัญชีตามกฎที่เข้มงวดว่าเงินไหลไปไหน แต่ฟินเทคมักจะเอาบัญชีแยกประเภทของตัวเองไปวางทับบน บัญชี FBO พื้นฐานหนึ่งบัญชีหรือไม่กี่บัญชีที่รวมเงินลูกค้าไว้ เพื่อใช้ติดตามยอดคงเหลือของลูกค้าแต่ละราย ในกรณีของ Synapse ยอดรวมคงเหลือของลูกค้าตามบัญชีแยกประเภทของตนเองสูงกว่ายอดเงินจริงในบัญชี FBO มาก
หลายคนสงสัยว่าเป็นการฉ้อโกง แต่ผมขอเดิมพันว่าน่าจะเป็นแค่บัญชีแยกประเภทที่เละเทะและเต็มไปด้วยบั๊กมากกว่า พอได้เห็นข้างในแล้ว ผมคงไม่เอาเงินไปฝากในบัญชีเงินฝากของฟินเทคเด็ดขาด ใช้ธนาคารจริง ๆ ดีกว่า ถึงฟินเทคจะโฆษณาว่าเงินฝากอยู่ภายใต้ประกัน FDIC แต่นั่นคุ้มครองเฉพาะกรณีธนาคารพื้นฐานล้มเท่านั้น ไม่ได้คุ้มครองกรณีที่ฟินเทคติดตามเงินของผมไม่ได้
อ้างอิง: https://www.forbes.com/sites/zennonkapron/2024/11/08/what-th...
Andreessen Horowitz ลงทุนไว้ และพวกเขาเป็นฝ่ายที่ทำสงครามแบบเผาให้ราบกับกฎระเบียบของรัฐบาลทุกอย่าง
https://finance.yahoo.com/personal-finance/synapse-bankruptc...
ก่อนหน้านั้นผมคาดไว้แล้วว่า codebase เละเทะเกินกว่าจะติดตามเรื่องพวกนี้ได้อย่างถูกต้อง และเคยเถียงกับหัวหน้าที่เชื่อว่าทุกอย่างเดินได้เหมือนเวทมนตร์ ไม่กี่วันต่อมาก็ได้รับอีเมลว่าจะเริ่มตรวจสอบความไม่ตรงกันทางบัญชี
JPMC เคยเสนอใช้ คริปโทเคอร์เรนซี ภายในเพื่อจัดการกระแสเงินสดให้สอดคล้องกัน แต่ไม่รู้ว่าจริง ๆ ไปถึงขั้นไหน
https://lex.substack.com/p/podcast-what-really-happened-at-s...
โต้แย้งอยู่พักหนึ่ง แต่พวกเขาไม่ยอมรับจริงจังและไม่แก้ให้ด้วย ผมเคยสงสัยแบบไม่มีหลักฐานว่าอาจเป็นการขโมยเล็ก ๆ น้อย ๆ ภายใน แต่ความไร้ความสามารถน่าจะเป็นคำอธิบายที่เป็นไปได้มากกว่า
เพื่อนที่เคยทำงานเป็นผู้ดูแลระบบที่ BoA บอกว่ามี log บางชนิดที่ต้องเก็บไว้ 7 ปี แต่พอ disk เริ่มเต็มก็ลบทิ้งไปเฉย ๆ
สิ่งหนึ่งที่ต้องใช้เวลาทำความเข้าใจตอนเริ่มทำงานที่ Google คือการยอมแลกความน่าเชื่อถือหรือความถูกต้องเพื่อ scalability
ก่อนหน้านั้นผมเคยทำระบบ billing หรือเว็บแอป OLTP ขนาดเล็ก และไม่เคยแม้แต่จะคิดถึงคำถามอย่างข้อมูลสูญหายที่ยอมรับได้ หรืออัตราความผิดพลาดที่ไม่เป็นศูนย์ สิ่งที่ช็อกกว่าความจริงที่ว่าเมื่อรับคำขอหลายล้านครั้งต่อวินาที ย่อมมีบางส่วนล้มเหลว คือความแตกต่างของทัศนคติด้านวิศวกรรม
ณ ตอนนี้ก็อาจมีคนหลายพันคนเปิด Gmail แล้วโหลดไม่ขึ้นหรือเจอ error 500 อยู่ ไม่มีใครไล่หาสาเหตุ เพราะผู้ใช้จะกด refresh แล้วใช้ชีวิตต่อไป ในทางกลับกัน แม้ตัวเลขความทนทานของ storage จะดูน่าประทับใจที่ 99.99999% ต่อปี แต่ถ้ามีลูกค้า 2 พันล้านคน ก็จะมี 200 คนที่เจอวันแย่สุด ๆ จริง ๆ
การเปลี่ยนผ่านจากนิสัยที่เคยตรวจสอบทุก error ใน log ไปสู่แนวทางที่มองว่าทุกอย่างพังอยู่เล็กน้อยตลอดเวลา และต้องคิดต้นทุนก่อนจะแก้อะไรสักอย่างนั้นค่อนข้างช็อก
กฎคร่าว ๆ ที่เห็นบ่อยในสเกลใกล้เคียงกันคือ ตั้งเป้าข้อมูลสูญหาย 1 ครั้งต่อ 100 ปีสำหรับโครงสร้างพื้นฐานทั้งหมด โดยปกติต้นทุนจะเพิ่มขึ้นน้อยกว่า 10% มาก แต่ต้องมีคนที่เข้าใจ combinatorics ตอนออกแบบอัลกอริทึมการจัดวางข้อมูล
ถ้าต้องการทำความเข้าใจเรื่องนี้ งานวิจัย copyset เป็นจุดเริ่มต้นที่ดี
ตอนนำเสนอวิธีแก้และบอกอัตราความผิดพลาดนี้ ก็ถูกถามว่าผู้ใช้ Android 200,000 คนนั้นจะกู้คืนได้อย่างไร ไม่มีวิธีกู้คืน และการซิงก์รายชื่อก็จะพังไปเฉย ๆ ผมเลยถูกบอกให้กลับไปออกแบบใหม่
ตัวเลขนั้นทำให้ถ่อมตัวขึ้นจริง ๆ แน่นอนว่ามีหลายพื้นที่ที่ 99.99% ก็เพียงพอ แต่ก็มีอีกมากพอ ๆ กันที่มันไม่เพียงพอ
เรื่องแบบนี้ช่วยได้ด้วยการจ้างคนที่ใช่ตั้งแต่ต้น ไม่ใช่จ้าง ผู้เชี่ยวชาญ LeetCode มาเต็มทีม แล้วไม่ถามเลยว่านอกจากความสามารถในการนึกถึงโครงสร้างข้อมูลและอัลกอริทึมในหัว พวกเขาสามารถสร้างสิ่งที่อยากสร้างจริง ๆ ได้หรือไม่
ถ้าคนรู้ว่าควรสร้างสิ่งนั้นอย่างไร ก็ไม่จำเป็นต้องแลกการเติบโต และมันจะถูกสร้างให้ถูกต้องตั้งแต่แรก
บางครั้งเราต้องการวิศวกรที่มีพื้นฐานการศึกษาด้านอื่น เช่น บัญชี การเงิน หรือชีววิทยา ส่วนที่สำคัญที่สุดในอาชีพของผมคือการเข้าใจอุตสาหกรรมของสิ่งที่ผมกำลังสร้างอย่างลึกซึ้ง และรู้จักผู้เชี่ยวชาญในสาขานั้นที่สามารถตั้งคำถามที่สำคัญจริง ๆ ได้ นั่นแหละคือการแก้ปัญหาและวิศวกรรม ส่วนที่เหลือคือการโปรแกรม/เขียนโค้ด
ผมใช้ชีวิตการทำงานส่วนใหญ่ในพื้นที่ที่เทคโนโลยีกับการเงินทับซ้อนกัน ส่วนใหญ่เป็นด้านการปฏิบัติตามกฎภาษีขายและภาษีการใช้ ระหว่างนั้นผมไม่เคยตระหนักพอว่านักบัญชี คอนโทรลเลอร์ และทนายความมีอิทธิพลต่อผมมากแค่ไหน
ช่วงหลังผมได้ให้คำปรึกษาระบบบัญชีแยกประเภทของสตาร์ทอัพที่ค่อนข้างเก่าแห่งหนึ่ง และตกใจมากเมื่อเห็นว่าถ้าวิศวกรที่ไม่มีพื้นฐานการเงินหรือบัญชีมาสร้าง ระบบบัญชี ผลลัพธ์จะออกมาเป็นอย่างไร
ไม่จำเป็นต้องตามหาคนวิเศษที่เป็นทั้งนักบัญชีและวิศวกร แค่ให้มีนักบัญชีตัวจริงนั่งข้างทีมวิศวกรรมระหว่างกระบวนการออกแบบก็พอแล้ว หลังจากออกแบบการยกเครื่องทั้งหมดเสร็จ ผมให้เพื่อนที่เป็น CPA ตรวจทานอย่างละเอียด เขาพบช่องโหว่ในบางสถานการณ์ แต่โดยรวมก็ใช้ได้
เงินเป็นปัญหาวิศวกรรมที่ยาก เพราะเงินมาพร้อมกับความพิลึกพิลั่นสารพัดของมนุษย์ที่รายล้อมมัน
สิ่งนี้ทำให้ยืนยันอีกครั้งว่า ความรู้โดเมน สำคัญในภาวะผู้นำด้านวิศวกรรม ถ้าทำงานในบริษัทการเงิน ก็ต้องเข้าใจการเงินในระดับหนึ่งเพื่อการตัดสินใจทางเทคนิคและการประนีประนอมที่ถูกต้อง และงานสื่อสารมวลชนหรือพาณิชย์ก็เช่นกัน
องค์กรที่ประสบความสำเร็จที่ผมเคยทำงานด้วย มักใส่คำถามที่ไม่ใช่เชิงเทคนิคแต่เฉพาะโดเมนไว้ในการสัมภาษณ์ทีมเทคโนโลยีเสมอ ในทางกลับกัน บางทีมที่เก่งทางเทคนิคมากกลับลำบากเพราะขาดความเข้าใจเชิงโดเมน
มองไปทางไหนก็ดูเหมือนทุกที่ชอบคนที่มีประสบการณ์วิศวกรรมซอฟต์แวร์มากกว่าผมสองเท่า แม้ไม่มีความรู้โดเมนเลย มากกว่าคนอย่างผมที่มีประสบการณ์บัญชีและมีประสบการณ์วิศวกรรมซอฟต์แวร์ค่อนข้างสั้น ผมสงสัยว่าจะมีวิธีใช้ประโยชน์จากสิ่งนี้อย่างมีประสิทธิภาพไหม
การเงินก็เป็นเรื่องเทคนิค วิศวกรรมเครื่องกลก็เป็นเรื่องเทคนิค และการจัดการกีฬา หรือสังคมวิทยา ก็มีองค์ประกอบเชิงเทคนิคขนาดใหญ่เช่นกัน เมื่อมองความสามารถทางเทคนิคให้กว้างขึ้น ก็จะเกิดความถ่อมตัวที่จำเป็นต่อการทำงานร่วมกันในหลายโดเมน
หน้าที่ของ PM คือทำงานร่วมกับวิศวกรรมเพื่อตรวจสอบว่าข้อกำหนดถูกต้องหรือไม่ และผลิตภัณฑ์ที่สร้างขึ้นตอบโจทย์ข้อกำหนดนั้นหรือไม่ ในสภาพแวดล้อมแบบ Agile บทสนทนาและการตรวจสอบแบบนี้เกิดขึ้นทุกสปรินต์ ดังนั้นจึงยากที่อะไรสักอย่างจะหลุดรอดไปนานเกินไป
ถ้าไม่มี PM ทีมวิศวกรรมก็จำเป็นต้องมีความรู้โดเมนลึก แต่ถ้ามีแล้ว นั่นไม่ใช่ความรับผิดชอบของวิศวกรรม แต่เป็นความรับผิดชอบขององค์กรผลิตภัณฑ์
ขอเล่าเรื่องเก่าสักเรื่อง ผมไม่เคยสร้างระบบบัญชีคู่ แต่เมื่อหลายสิบปีก่อนเคยสร้าง ระบบคิดเงิน ในสตาร์ทอัพอินเทอร์เน็ต/โทรคมนาคมที่เติบโตจนมีรายได้ระดับแปดหลัก
ตอนนั้นเป็นนักพัฒนาหนุ่มที่ยังไม่รู้อะไรมาก และบังเอิญได้เริ่มทำลอจิกการคิดเงินตั้งแต่วันแรก ซึ่งไม่รู้ว่าดีหรือร้าย ผมสร้างมันไว้สองจุดในระบบ คือหน้าเว็บคิดเงินสำหรับผู้บริโภค และโปรเซสแบ็กเอนด์แยกต่างหากที่สร้างอินวอยซ์และตัดบัตรเครดิต
การทำให้ทั้งสองอย่างสอดคล้องกันเป็นเรื่องยากอย่างน่าประหลาดใจ เราเผาเงินทุนเพื่อวนพัฒนาต่อเนื่องในการหาการตอบรับของตลาด และมีการเพิ่มผลิตภัณฑ์กับบริการใหม่ วิธีส่วนลดและราคาใหม่ การคิดเงินตามการใช้งาน การคิดเงินรายเดือน ใช้ฟรี X ครั้งแรก ผู้ชำระเงินหลัก/บัญชีย่อยของบัญชีองค์กร ศูนย์ต้นทุนที่ผู้ใช้กำหนดเอง การปันส่วนภาษีไปยังศูนย์ต้นทุนนั้น และการปันส่วนระดับ 1 เซ็นต์ เป็นต้น ทุกครั้งที่เพิ่มสิ่งเหล่านี้ ก็จะมีรายละเอียดและข้อยกเว้นใหม่ ๆ ทำให้ตัวเลขในสองหน้าจอ/สองวิธีไม่ตรงกัน
เพราะผมเป็นคนรับผิดชอบการคิดเงิน ทุกเดือนผมจึงใช้เวลาหลายวันไล่อ่านอินวอยซ์ทั้งหมดด้วยมือ เพื่อตรวจสอบครั้งสุดท้ายว่าตัวเลขตรงกันก่อนจะตัดบัตรเครดิตและส่งอินวอยซ์กระดาษ ผมมักจะพบปัญหาใหม่ที่กระทบลูกค้าหนึ่งรายหรือไม่กี่รายเสมอหรือบ่อยครั้ง แล้วแก้โค้ดก่อนการคิดเงินจริง ผมรู้สึกกังวลเสมอกับการปล่อยระบบไปโดยไม่ตรวจซ้ำทุกอย่างด้วยมือ
ผมเคยคิดจะรีแฟกเตอร์ลอจิกการคิดเงินให้เหลือที่เดียวเพื่อกำจัดความไม่ตรงกันและการตรวจเทียบด้วยมือ แต่หลังจากคิดอยู่นาน ผมก็ตระหนักว่า codebase เดียวทำให้ไม่สบายใจ และ codebase สองชุดกลับช่วยจับข้อผิดพลาดของผมได้ หลังจากนั้นผมค่อย ๆ ทำให้การรันอัตโนมัติและการตรวจเทียบข้ามกันระหว่างสอง implementation ง่ายขึ้นเรื่อย ๆ
โค้ดคิดเงินค่อนข้างรกเกินกว่าจะเอาไปอวดได้ แต่ผมภูมิใจมากกับความถูกต้องของการคิดเงิน การที่มีเรื่องร้องเรียนน้อย และความผิดพลาดชวนหวาดเสียวหลายอย่างที่หลีกเลี่ยงได้ตลอดหลายปี ผมรู้สึกผิดนิดหน่อยกับความซับซ้อนที่ทิ้งไว้ให้คนรับช่วงต่อ แต่จนถึงตอนนี้ก็ไม่ได้เสียใจมากนัก
หลังจากประสบการณ์นั้น ผมเข้าใจแรงจูงใจของการทำบัญชีคู่มาโดยตลอด เท่ากับว่าผมประดิษฐ์ โค้ดคิดเงินแบบลอจิกคู่ ขึ้นมาใหม่ในแบบห่วย ๆ เพื่อกันไม่ให้ความผิดพลาดของผมไปทำร้ายลูกค้า
ทีมข้อมูลที่ผมเคยไปรับหน้าที่นำในบริษัทเก่ามีนิสัยโชคร้ายคือทำเงิน “หาย” ไม่ใช่ว่าเงินจริงหายไประหว่างย้ายไปที่อื่น แต่เป็นกรณีที่บันทึกที่ควรใช้เรียกเก็บเงินลูกค้าหายไป
เวลาไม่ทำรายได้หาย ก็จะคิดเงินซ้ำ และเรื่องแบบนี้เกิดขึ้นต่อเนื่อง ต้องใช้การทำงานหนักถึง 3 ปี กว่าจะได้ความเชื่อมั่นจากผู้บริหารกลับมา
ไม่มีการทดสอบเลยหรือ? ถ้าถึงขั้นยกตัวอย่างว่า “ทุกครั้งที่ซื้อของ 5 ดอลลาร์ ในบันทึกธุรกรรมจะเหลือ 4.98 ดอลลาร์” คือถ้าเสียเงินทุกธุรกรรม ปัญหามันใหญ่กว่า การไม่มีบัญชีคู่ มาก
ใครกันที่สร้างระบบการเงินแบบนั้นแล้วคิดว่าปกติ? เรื่องค่าชดเชยก็เป็นปัญหา แต่ถ้าเป็นบริการแบบนั้นควรหนีให้เร็วที่สุดเท่าที่ทำได้
พวกเขาล้อกันว่าเป็น “เซ็นต์เต้นระบำ” และทำแบบนั้นเพราะรู้ว่าไม่ต้องรับผลลัพธ์ที่มีนัยสำคัญใด ๆ พวกเขาเคลื่อนที่เร็ว ทำบางอย่าง—ที่เกี่ยวกับเงิน—พัง แล้วก็หัวเราะกลบเกลื่อน
ตอนนี้กลับพยายามสั่งสอนคนอื่นราวกับมีอำนาจทางศีลธรรมและเทคนิค เพียงเพราะพวกเขาตั้งใจตัดสินใจแบบนั้น นี่เป็นเรื่องไร้สาระแบบ วัฒนธรรมสตาร์ทอัพ VC ที่หยิ่งผยองอย่างน่าทึ่ง
แน่นอนว่ามันอาจให้เบาะแสในการหา bug ได้ แต่การเขียน test พื้นฐานก็ให้ผลแบบเดียวกัน
ไม่เข้าใจว่าทำไมผู้เขียนถึงหยิบสุภาษิต “make it work, make it right, make it fast” มาใช้ในเชิงลบ บางทีอาจเข้าใจผิดว่า “make it fast” อยู่ตรงไหน
“make it right” เป็นขั้นตอนที่สอง และก่อนจะทำให้มันทำงานได้อย่างถูกต้อง งานควรหยุดอยู่ตรงนั้น ระบบต้องทำงานอย่างสมบูรณ์แข็งแรง “make it fast” หรือการ optimize ควรเริ่มหลังจากปัญหาเรื่องความถูกต้องและความสมบูรณ์แข็งแรงถูกแก้หมดแล้วเท่านั้น
เรื่องนี้ไม่เกี่ยวกับความเร็วในการส่งมอบหรือการทำงานเร็ว แต่หมายถึงให้เลื่อนการ optimize ไปเป็นขั้นตอนสุดท้าย
แต่ถ้าสิ่งที่ผู้เขียนอยากพูดคือ บางอย่างต่อให้ “ทำงานได้” แบบคร่าว ๆ ก็ยังห่างจาก “ความถูกต้อง” มากจนแก้ย้อนกลับทีหลังไม่ได้ และต้องทำให้ “ถูกต้อง” ตั้งแต่ต้นก่อนที่มันจะเริ่มพอทำงานได้ อันนี้ก็เข้าใจได้
ผมก็คิดเหมือนกัน และเคยมีส่วนร่วมในการ audit ระบบ fintech ผู้ตรวจสอบต้องดาวน์โหลดทุกอย่างเป็นสเปรดชีต Excel แล้วกระทบยอดตัวเลขก่อนอนุมัติสมุดบัญชี ใช้ทั้งเวลาและเงินมาก และผมเดาว่าอีก 3 ปีต่อมา ใน liquidity event คงสร้างความต่างอย่างน้อยราว 0.1 unicorn
ในสตาร์ทอัพที่เคลื่อนไหวเร็ว จะปล่อย MVP ที่ใช้งานได้จริงให้เร็วที่สุด เพราะต้องสร้างฐานลูกค้า การเงิน ฯลฯ จึงมักหยุดอยู่ที่ขั้น “make it work”
สุภาษิตที่เหมาะกว่าน่าจะเป็นของ Facebook คือ “move fast and break things” แต่เรื่องนั้นใช้ได้ก็ต่อเมื่อสามารถแก้ทีหลังได้เท่านั้น เช่น ถ้าคุณสร้างเครื่องบิน ก็คงไม่ทำแบบนั้น
เมื่อดูบริบทแล้ว ความเข้าใจผิดที่กล่าวในประโยคแรกดูเป็นไปได้มากที่สุด เพราะถัดจากนั้นก็พูดถึงแรงกดดันด้านเวลาที่สตาร์ทอัพได้รับ
คอมเมนต์ส่วนใหญ่ที่นี่กำลังทำซ้ำสิ่งที่บทความวิจารณ์อยู่ เห็นการถกเถียงยาว ๆ ที่ปกป้อง บัญชีเดี่ยว นับไม่ถ้วน
บัญชีเดี่ยวอาจง่ายกว่าและเป็นแบบทั่วไปกว่า แต่บางครั้งการทำตามระบบและ abstraction ที่พัฒนามาหลายศตวรรษก็เป็นความคิดที่ดี
ถ้าไม่ได้จำเป็นต้องใช้อะไรที่แตกต่างจริง ๆ ใช้บัญชีคู่ดีกว่า สัญชาตญาณของโปรแกรมเมอร์อาจรู้สึกตะขิดตะขวง แต่เมื่อถึงช่วงที่ต้องเรียกนักบัญชีจริง ๆ มาช่วยจัดการความไม่ตรงกัน คุณจะขอบคุณมัน
เกี่ยวกับเรื่องนี้ มีใครรู้แหล่งข้อมูลดี ๆ สำหรับโปรแกรมเมอร์ด้านการชำระเงินหรือสาขาใกล้เคียงไหม? ประมาณ “การบัญชีสำหรับโปรแกรมเมอร์” น่ะ
https://martin.kleppmann.com/2011/03/07/accounting-for-compu...
https://www.moderntreasury.com/journal/accounting-for-develo...
[0]: https://www.winstoncooke.com/blog/a-basic-introduction-to-ac...
ถ้าบอกว่ากำลังใช้ระบบฐานข้อมูลที่ข้อมูล 1% หายไปทุก ๆ 10 ธุรกรรม คุณจะยังรับคำแนะนำในบล็อกวิศวกรรมแบบนี้อย่างจริงจังได้ไหม? บทความแบบนี้ให้ความรู้สึกเหมือนโฆษณาประชาสัมพันธ์บุคคลหรือกลุ่มใดกลุ่มหนึ่งที่ห่อมาอย่างสุขุม มากกว่าจะเป็นการแนะนำแนวคิด
ถ้าอยากเห็นว่า ทัศนคติหละหลวม แบบนี้ต่อซอฟต์แวร์ที่เคลื่อนย้ายเงินก่อผลอย่างไรกับคนจริง ๆ ให้ดูคดีอื้อฉาว Post Office
https://en.wikipedia.org/wiki/British_Post_Office_scandal
ทุกสิ่งที่เคลื่อนย้ายเงินต้องถูกปฏิบัติอย่างจริงจังที่สุด และควรรู้จักความล้มเหลวในอดีตให้มากที่สุดเท่าที่ทำได้
https://en.wikipedia.org/wiki/Mr_Bates_vs_The_Post_Office