Boring Solutions That Age Well
Cleverness is cheap. Maintenance is where the bill arrives.
I’ve spent enough time around live systems to stop confusing novelty with courage. The choice that looks boring in a planning document is often the choice that leaves you with fewer regrets six months later.
This is not an abstract case for old tools. These are the specific boring choices we made, and the reasons they keep earning their place.
Terraform over CDK
We chose Terraform for infrastructure-as-code. CDK was a reasonable alternative — it lets you write TypeScript, it has type safety, it’s AWS-native.
We chose Terraform anyway. The reasons:
- Largest community. When something breaks at 2am, the answer already exists on the internet. CDK’s community is growing but smaller. Pulumi’s is smaller still.
- Most transferable skill. If we hire someone, they probably know Terraform. If they don’t, HCL is readable enough that they can learn it in a day. CDK requires understanding CloudFormation underneath, which defeats the purpose of the abstraction.
- Plan/apply workflow.
terraform planshows exactly what will change before anything happens. This fits a PR-based review culture. The output is readable by anyone, not just the person who wrote the code. - Not locked to AWS. We’re single-cloud today. If that changes, Terraform changes with us. CDK doesn’t.
The BSL license change added uncertainty. OpenTofu exists as a fork if it becomes a problem. We noted this as a consequence and moved on.
This decision is documented in ADR-0004, including the options we rejected and why.
PostgreSQL with RLS over NoSQL
We use PostgreSQL across every app. Not DynamoDB, not MongoDB, not Firestore. Postgres.
The choice wasn’t about performance benchmarks. It was about where correctness guarantees belong.
Row Level Security means the database enforces access control on every query. If someone adds an API route and forgets to check permissions, the data is still protected. If an edge function bypasses the middleware, RLS still holds. The security model is in the database, not in the application’s good behavior.
This also means we write SQL. Functions like accept_offer run as single atomic transactions — accept one offer, decline all others, fulfill the bulletin. Three updates, zero race conditions. Doing this in application code requires explicit locking that most teams don’t implement.
PostgreSQL is not exciting. It’s been around since 1996. The documentation is exhaustive. The failure modes are known. That’s the point.
Convention-based discipline over tooling
Every app enforces a strict rule: components never import the Supabase client directly. All data access goes through src/lib/api/. CollectionChecker has 10 API modules. w3f1nd has 15+.
This convention is enforced by discipline, not by tooling. There’s no ESLint rule. No custom loader. No dependency injection framework. People follow it because they understand why it exists.
The service layer enables three things:
- Demo mode. Every API function can check
isSupabaseConfiguredand return mock data. Without the service layer, you’d need to mock Supabase itself. - Testability. API functions are plain async functions. They’re easy to test without rendering components.
- Separation of concerns. Components describe UI. API modules describe data access. The boundary is clear.
We could build tooling to enforce this. A lint rule that flags Supabase imports outside src/lib/api/ would take an hour. But the convention is easy enough to explain that tooling isn’t necessary yet. If it becomes necessary, we’ll add it. Until then, documentation and code review are sufficient.
Clerk for identity, not custom auth
We didn’t build our own auth. We use Clerk.
This sounds obvious. Most teams use an auth provider. The less obvious part is what we didn’t do: we didn’t customize Clerk beyond what it supports natively. No custom sign-in flows. No bespoke session management. The JWT template is named supabase and uses Clerk’s standard claims.
The reason: auth is the part of the system where cleverness is most dangerous. A custom auth flow that works slightly better than the standard one is not worth the risk of a custom auth flow that has a security flaw nobody discovers for six months.
Clerk handles identity. Supabase handles data. The bridge between them (@consilientus/auth) is our code, but it’s thin — 200 lines of actual logic across four entry points. The less custom auth code we maintain, the fewer auth bugs we’ll ship.
The cost of boring
Boring has real costs. Terraform’s HCL is less expressive than TypeScript. PostgreSQL doesn’t scale horizontally like DynamoDB. Convention-based discipline requires trust in the team.
These are acceptable costs because the alternative costs are higher. CDK’s expressiveness comes with an abstraction layer that hides CloudFormation’s complexity until it doesn’t. DynamoDB’s horizontal scaling comes with a data modeling paradigm that makes relational queries painful. Tooling-enforced discipline scales better but takes time to build and maintain.
Every boring choice is a bet that the known trade-offs are better than the unknown ones. So far, the bet has paid off. The auth system works across six apps. The RLS policies have never leaked data. The Terraform state has never corrupted. The API service layer convention has never been violated.
The exciting alternative might have been better. But the boring choice is still working, and I’m not worried about it at 2am.