Most business software is a pile of rules. A booking system checks that two guests can't hold the same room on the same night. An approval flow checks that nothing ships without a sign-off. A budget checks that nobody spends past a limit. Those rules usually live in application code, spread across services, background jobs and front-end validation, and each copy has to agree with the others.
Here's my argument: for a lot of systems, that's the wrong place for them. Put the rules that must never break in the database, put the numbers that change in rows the database reads, and the rest of the system gets small enough to hold in your head.
I didn't start with that belief. I got there by watching a simulation.
I had a bot run a reservation simulator for a small lodging business against a pebble, which is a single SQLite file on pebblebase that holds the data, the rules, the views and the notes. The bot played the front desk. It booked rooms, changed stays and tried the edge cases.
What surprised me was how little there was to it. My note to my lead bot that day was: "I'm finding that a thick database can really make our system simple. Stupid simple."
The second surprise was how fast the modeling went. My takeaway at the time: "we can model operations insanely fast. What we actually need is to define our system first."
I now call the pattern ThickDB. It has two parts.
An invariant is a rule that must hold no matter who writes to the data: a person, a script, or an AI agent having a bad day. Two bookings can't overlap. A deleted record can't come back. A decision needs an approver.
If that rule lives in application code, every new writer has to remember it. If it lives in a database trigger, the database refuses the write, and there's nothing to remember. That matters more now than it used to, because the number of things writing to your data is going up. I run a team of bots, and every one of them is another writer.
The trap with triggers is hard-coding the numbers. How many nights is the maximum stay? How many missed targets before a plan has to change? Those are business decisions, and they change.
So the split I settled on is this. When my lead bot laid it out, I said: "Yes that's the pattern. Policy lives in configuration, the trigger reads from a configuration tier."
The trigger enforces the behavior. A config table holds the values. Changing a policy means editing a row, not migrating a schema or redeploying code.
Here's the shape, simplified (this is a sketch, not production code):
CREATE TABLE config (name TEXT PRIMARY KEY, value INTEGER NOT NULL);
INSERT INTO config VALUES ('max_nights', 14);
CREATE TRIGGER booking_max_nights
BEFORE INSERT ON bookings
WHEN julianday(NEW.check_out) - julianday(NEW.check_in)
> (SELECT value FROM config WHERE name = 'max_nights')
BEGIN
SELECT RAISE(ABORT, 'stay is longer than max_nights');
END;
I use the same pattern outside of bookings. In the pebble that tracks my KPIs, if a play misses its target too many periods in a row, the database refuses a "continue" decision, and the play has to be changed, paused or killed. The threshold is a row. If I want to be more patient, I edit the row.
The usual objection is that real systems need more than this. Hotel software, for example, comes with a long list of rules. My reaction during the simulation was blunt: "PMS systems have so many rules. It's not hard. People rent a room for a set number of nights and pay a rate. That's it."
A lot of that rule weight is built for software that has to serve everyone. As I put it: "I think the thing is that those rules are for general software. We may be able to just get away with what we need."
The same goes for scale. When the simulator raised concurrency as a risk, I pushed back: "I don't think 10-30 room properties should ever have concurrency issues ... The biggest bottlenecks are reads for big promotions when demand is the highest ... Yeah we don't even need that complexity." Size the system to the load you actually have, not the one a vendor has to plan for.
Auth is another piece people expect to need a platform for. My answer was: "We can handle auth at the boundary and only allow triggers based on user." Check who someone is at the edge, then let the database decide what that person is allowed to change.
Once the database holds the rules, the rest of the stack can be thin. My working vision for a minimum viable system is: "we just define our system in prose. Translate that into a database. Ai as the interface and we are off." And: "My ideal world is that we just have a simple system, less code, and we really define everything in the database itself. We can expose everything over MCP, which then maps tools to user accounts, so that your MCP can only do what you have access to."
That's also why I like SQLite for this. One file holds the data and the rules together. In my words: "File is the portable image that can be used anywhere." You can copy it, test it and hand it to another agent without explaining how three services fit together.
This isn't free. Rules in triggers are less visible than rules in code you read every day, so they need tests and plain-language notes next to them. Not every rule belongs in the database either. Things that are judgment calls, like whether a guest deserves an exception, still belong with a person. And if your data really does need to be spread across many machines, one file stops being the whole story.
But for the systems most small teams build, the question is worth asking: which rules must never break, and which numbers will change? Put the first in triggers and the second in rows, and see how much code you can delete.
ThickDB came out of building pebblebase, my tool for building and hosting small apps through AI chat. pebblebase hasn't taken off yet. I still count it as worth it. As I said to one of my bots: "I learned a lot ... ThickDB came from it and it truly changed the model for developing software for me."
If you want to try the pattern without setting up a database, pebblebase is built around it: you describe the tool in your AI chat, and your AI can build the tables, triggers and config rows in one file. It's a free pilot at https://pebblebase.dev. If you try ThickDB anywhere else, I'd like to hear where it held up and where it didn't.