Các nguyên tắc kỹ thuật để xây dựng hệ thống tài chính
(substack.wasteman.codes)- Kế toán về cơ bản không thay đổi nhiều trong suốt vài trăm năm qua
- Mặc dù vậy, vẫn có rất nhiều nhầm lẫn về cách đúng đắn để xây dựng phần mềm cho các hệ thống tài chính
- Bài viết này chia sẻ những bài học rút ra từ kinh nghiệm xây dựng hệ thống tài chính tại các doanh nghiệp lớn
- Dù tập trung vào việc xây dựng hệ thống kế toán, các nguyên tắc này cũng áp dụng cho các hệ thống tài chính nói chung
Định nghĩa các thuật ngữ tài chính cơ bản
- Sổ cái tổng hợp (General Ledger, GL): Sổ kế toán chính của công ty, tóm tắt toàn bộ giao dịch tài chính trong một khoảng thời gian nhất định. Có thể xem đây là phần tổng hợp của các sổ cái phụ tương ứng
- Sổ cái phụ (Sub-ledger): Chứa thông tin chi tiết về từng giao dịch riêng lẻ liên quan đến một GL cụ thể. Bản ghi trong sổ cái phụ chứa dữ liệu chi tiết hơn nhiều so với sổ cái tổng hợp (ví dụ: khách hàng cụ thể, một hạng mục cụ thể trong đơn hàng, v.v.). Mức chênh lệch dữ liệu giữa sổ cái phụ và GL phụ thuộc vào loại hình kinh doanh và lượng dữ liệu đang xử lý. Một số doanh nghiệp nhỏ có thể vận hành mà không cần sổ cái phụ, nhưng nếu quy mô nhỏ thì khả năng cần phần mềm tùy chỉnh cũng không cao
- Bản ghi tài chính (Financial Record): Cách gọi chung cho sổ cái tổng hợp và sổ cái phụ
- Trọng yếu (Material): Cho biết việc sai lệch thông tin trong báo cáo tài chính có ảnh hưởng đến quyết định của một bên liên quan hợp lý hay không. Định nghĩa này được cố ý để hơi mơ hồ, vì mỗi doanh nghiệp có tiêu chí trọng yếu khác nhau. Ví dụ, điều quan trọng với một công ty có doanh thu hằng năm 250.000 USD có thể không quan trọng với một công ty có doanh thu hằng năm 1 tỷ USD. Từ góc độ thiết kế, giá trị chính của khái niệm này là phân loại các nhóm dữ liệu tài chính khác nhau
Luồng dữ liệu cấp cao
Business System --(Financial Events)--> Sub Ledger(s) --(Summarized Accounting Entries)--> General Ledger
Ba mục tiêu chính của hệ thống kế toán
- Chính xác (Accurate): Bản ghi tài chính phải phản ánh trạng thái đã biết của doanh nghiệp
- Ví dụ: nếu bán 10 sản phẩm giá $9.99, thì tổng trong bản ghi tài chính phải là $99.90
- Điều này có vẻ hiển nhiên, nhưng khi tổng hợp hàng nghìn hay hàng triệu giao dịch, những lỗi cộng dồn đơn giản hoặc lỗi làm tròn giữa các hệ thống có thể tạo ra sai lệch nghiêm trọng
-
Ghi chú của Wasteman
- Mọi người nói rằng đặt tên là vấn đề khó nhất trong khoa học máy tính, nhưng tôi nghĩ phép cộng là vấn đề khó thứ hai
- Trong vài năm làm việc với các hệ thống tài chính quy mô lớn, tôi đã chứng kiến vô số trường hợp chỉ một lỗi rất nhỏ cũng tạo ra khác biệt lớn trong dữ liệu
- Đừng nhắc đến việc tính tổng trên float. Tôi đã phải học một cách đau đớn vì sao luôn nên dùng số nguyên
- Bản ghi tài chính phải đầy đủ (complete)
- Cụ thể hơn, sổ cái phụ và sổ cái tổng hợp phải thể hiện đầy đủ mọi hoạt động kinh doanh đã xảy ra tại một thời điểm nhất định
- Nếu có sự kiện đã xảy ra nhưng không có trong bản ghi tài chính, thì hệ thống không đầy đủ
- Điều này không có nghĩa là không được phép dùng tính nhất quán cuối cùng (eventual consistency)
- Cần biết khi nào dữ liệu sẽ trở nên đầy đủ để có thể thông báo cho các bên liên quan rằng dữ liệu đã được chốt
-
Ghi chú của Wasteman
- Đảm bảo tính đầy đủ cũng là một vấn đề khó đến ngạc nhiên
- Khi hệ thống mở rộng, dữ liệu có thể vô tình bị biến đổi hoặc bị bỏ sót khi đi qua nhiều hệ thống
- Có thể kiểm toán (Auditable): Bản ghi tài chính phải dễ kiểm toán để các bên liên quan có thể phát hiện lỗi và đo lường chính xác hiệu quả kinh doanh
- Kịp thời (Timely): Hệ thống kế toán phải đáp ứng các nhu cầu cụ thể của doanh nghiệp
- Với doanh nghiệp nhỏ, có thể chỉ cần đổ toàn bộ số liệu vào cuối tháng, nhưng doanh nghiệp lớn thường muốn hệ thống gần thời gian thực
- Điều này giúp theo dõi tình hình tài chính trong suốt tháng, đưa ra quyết định dựa trên dữ liệu tài chính nhanh hơn và giảm áp lực chốt sổ vào đầu tháng/quý
- Dù nhu cầu là gì, hệ thống kế toán của chúng ta phải đáp ứng đúng yêu cầu của doanh nghiệp và đúng với ý nghĩa kịp thời mà họ cần
-
Ghi chú của Wasteman
- Mọi người thường bị lạc trong các cuộc tranh luận về hệ thống batch và streaming khi nói đến tính kịp thời
- Theo quan điểm của tôi, với đa số hệ thống thì đây không phải là khác biệt quan trọng
- Nó chỉ quan trọng nếu bạn quan tâm đến độ trễ cực ngắn từ vài giây đến vài phút
- Nhưng tôi ngạc nhiên vì quá thường xuyên nghe mọi người tranh cãi nên làm gì trong khi người dùng thực ra không cần xem cập nhật quá vài lần mỗi ngày
- Được yêu cầu không có nghĩa là thực sự cần thiết
Ba nguyên tắc kỹ thuật chính mà hệ thống kế toán nên tuân theo
- Tính bất biến (Immutability) và độ bền dữ liệu (Durability)
- Cho phép kiểm toán, từ đó hỗ trợ gỡ lỗi và độ chính xác
- Khi dữ liệu là bất biến, bạn có thể chụp lại trạng thái hệ thống vào bất kỳ lúc nào
- Điều này giúp việc tính toán lại toàn bộ trạng thái từ một trạng thái trước đó trở nên rất dễ dàng, vì không có trạng thái nào bị mất
- Dữ liệu đã được ghi vào bản ghi tài chính thì không thể xóa
- Mọi chỉnh sửa đối với hệ thống phải được thể hiện dưới dạng giao dịch tài chính mới
- Ví dụ: nếu hệ thống có lỗi khiến một dịch vụ đáng ra giá $900 lại bị ghi nhận là đã bán với giá $1000
- Để sửa sai, trước tiên cần đảo bút toán kế toán tương ứng với sai sót đó, rồi ghi lại bút toán với số tiền chính xác
- Dữ liệu phải được ghi ở mức chi tiết nhỏ nhất (Data recorded at the smallest grain)
- Tương tự nguyên tắc ở trên, điều này cũng cực kỳ quan trọng để tạo ra dấu vết kiểm toán rõ ràng
- Dù báo cáo tài chính và sổ cái tổng hợp là dữ liệu đã được tổng hợp, chúng vẫn được tính từ các sự kiện chi tiết hơn
- Khi dữ liệu không thể giải thích được, bạn cần dữ liệu ở mức chi tiết nhất để gỡ lỗi vấn đề là gì
- Lưu dữ liệu ở mức chi tiết thấp nhất cũng giúp việc chỉnh sửa dữ liệu phát sinh từ tập dữ liệu đó trở nên rất dễ dàng
- Nếu một tập dữ liệu bất biến duy nhất là nguồn sự thật cốt lõi cho mọi cách nhìn của dữ liệu đó,
- thì để sửa một cách nhìn, bạn chỉ cần sửa dữ liệu rồi chạy lại pipeline tạo ra cách nhìn đó
- Tương tự, khi kế toán viên chuẩn bị khóa sổ,
- họ sẽ đối chiếu toàn bộ giao dịch đã phát sinh với số dư tài khoản để xác minh sổ sách là chính xác
- nếu phát hiện chênh lệch, họ có thể đào sâu đến đúng giao dịch gây ra vấn đề
- Phải có tính idempotent (Idempotency)
- Mỗi sự kiện tài chính chỉ được xử lý một lần, và dữ liệu trùng lặp trong bản ghi tài chính chắc chắn sẽ gây ra sai lệch rõ ràng
- Vì lý do này, mọi đoạn mã tạo ra bản ghi tài chính đều phải là idempotent
- Idempotent nghĩa là một phép toán dù được áp dụng nhiều lần thì kết quả vẫn không thay đổi
- Tức là dù xử lý cùng một sự kiện tài chính nhiều lần, kết quả vẫn phải giống như lần xử lý đầu tiên
Thực hành tốt nhất
- Ưu tiên dùng số nguyên để biểu diễn số tiền tài chính: các phép toán số học sẽ dễ hơn nhiều. Hãy tránh float
- Hỗ trợ mức độ chi tiết phù hợp cho số tiền tài chính để giảm thiểu mất độ chính xác khi quy đổi tiền tệ
- Nếu chỉ xử lý đô la, biểu diễn theo cent có thể là đủ
- Với doanh nghiệp toàn cầu, nên ưu tiên đơn vị micro hoặc kiểu số thập phân như
DECIMAL(19, 4) - Trong các hệ thống tài chính nói chung, số thập phân khá phổ biến, nhưng trong hệ thống tài chính quảng cáo thì micro là tiêu chuẩn
- Sử dụng một phương pháp làm tròn nhất quán: cách làm tròn có thể tạo ra chênh lệch đáng kể so với số tiền kỳ vọng
- Ví dụ, mọi giá trị từ 5 trở lên thì làm tròn lên chữ số có nghĩa tiếp theo, còn 4 trở xuống thì làm tròn xuống
- Hoặc luôn làm tròn lên, v.v.
- Điều quan trọng là phải giữ nhất quán trên toàn hệ thống (nếu lệch 1 cent mỗi giao dịch thì với 10 triệu giao dịch sẽ thành chênh lệch $100k)
- Trì hoãn việc quy đổi tiền tệ càng lâu càng tốt: quy đổi tiền tệ quá sớm có thể làm mất độ chính xác
- Hãy trì hoãn việc quy đổi cho đến sau khi đã tổng hợp theo đồng tiền địa phương
- Sử dụng biểu diễn thời gian bằng số nguyên: hơi gây tranh cãi một chút nhưng rất đáng khuyến nghị
- Nhiều thư viện khác nhau xử lý việc parse timestamp thành object theo những cách khác nhau
- Tốt hơn là tránh những rắc rối này và dùng số nguyên
- Unix timestamp hoặc datetime số nguyên dựa trên UTC đều hoạt động hoàn hảo
- Càng ít chuyển đổi dữ liệu giữa các hệ thống càng tốt
-
Ghi chú của Wasteman
- Tôi còn chưa nhắc đến các lỗi liên quan đến giờ tiết kiệm ánh sáng ban ngày. Dùng số nguyên tăng dần sẽ tránh được hoàn toàn
- Nếu vẫn nhất quyết dùng datetime thì ít nhất hãy dùng UTC. Thật đáng ngạc nhiên là có rất nhiều công ty rất lớn vẫn dùng timestamp không phải UTC
2 bình luận
Cái này thực sự rất hữu ích. Nếu cứ vô tư chuyển kiểu (
decimal,float,double) và làm tròn mà không có thảo luận nghiệp vụ trong thực tế thì sẽ rất nguy hiểm.Ý kiến trên Hacker News
Nhấn mạnh tầm quan trọng của việc sử dụng phương pháp làm tròn nhất quán
Khuyến nghị biểu diễn thời gian bằng số nguyên
Khuyến nghị sử dụng cơ sở dữ liệu quan hệ cho hệ thống kế toán
Mục tiêu chính của hệ thống kế toán là độ chính xác, khả năng kiểm toán và tính kịp thời
Ý kiến về tính toàn vẹn của hệ thống kế toán
Với doanh nghiệp toàn cầu, khuyến nghị dùng ít nhất 8 chữ số thập phân
Đề cập tầm quan trọng của giao diện người dùng (UI)
Giải thích sự khác biệt giữa xử lý theo lô và xử lý dòng
Chia sẻ kinh nghiệm xây dựng hệ thống hóa đơn bằng TypeScript
Khuyến nghị sử dụng các lớp trong thư viện chuẩn
Giải thích khó khăn trong việc làm tròn và chia sẻ dữ liệu
Chia sẻ kinh nghiệm làm API cho 10 ngân hàng hàng đầu của Mỹ
Giới thiệu "Accounting Patterns" của Martin Fowler