I Rebuilt Our Billing System Using Only PostgreSQL 20 Triggers
In the world of modern software engineering, the prevailing wisdom suggests that "business logic belongs in the application layer." We are told that databases should be "dumb" bit-stores, while our microservices handle the complex orchestration of state. However, after struggling with persistent race conditions, data drift, and the sheer complexity of a distributed billing architecture, I decided to challenge this dogma. I rebuilt our billing system using only PostgreSQL 20 triggers, effectively moving the core financial engine directly into the database.
The result was a system that is not only faster and more reliable but also fundamentally impossible to put into an inconsistent state. If you have ever stayed up until 3:00 AM reconciling mismatched invoice totals or hunting down a "phantom" credit, you know that the stakes in billing are incredibly high. By leveraging PostgreSQL triggers and stored procedures, we achieved a level of data integrity that our previous Node.js and Go services simply couldn't guarantee.
The Problem with Application-Layer Billing Logic
Most billing systems suffer from the "dual-write" problem. An application receives a request, updates a row in the database, and then attempts to send a notification or update a secondary cache. If the network hiccups between these steps, your data is now out of sync. In our previous architecture, we relied on distributed transactions and complex retry logic to keep our "Balance" table in sync with our "Transactions" table.
Despite our best efforts, we frequently encountered race conditions. Two concurrent requests could theoretically deduct from a user’s balance simultaneously, leading to a negative balance that the application logic failed to catch. We realized that to achieve true ACID compliance across our entire financial lifecycle, we needed to enforce our constraints at the lowest possible level: the storage engine.

