I build software around three principles: correctness, observability, and security.

There's deep satisfaction in building things that provably work.


Correctness

Correctness means the software does what it claims to do. Many bugs exist because someone thought "this probably works" instead of proving it does.

Boundaries that mean something

I structure systems with clear separations between layers. Each component has one job. When responsibilities blur, bugs hide in the overlap. When boundaries are sharp, contracts are clear.

Clear contracts

Function interfaces should be explicit about what they accept and return. When requirements change, well-defined contracts show every place that needs to change with them.

Authorization before logic

In any operation that matters, permission checks come first. Not somewhere in the middle. Not "usually." First.

This eliminates an entire category of bugs: those where clever code paths accidentally bypass authorization. By making the check the first line, there's nothing to bypass.

Tests as evidence

Claims of correctness require proof. I test comprehensively, covering not just the happy path but the edge cases where systems actually break.

More importantly, tests run under realistic conditions. If production enforces constraints, tests enforce those same constraints. A bug that would appear in production should fail the same way in tests.


Observability

Observability means understanding what the system is doing, in real-time and historically. Two distinct kinds: a durable record of what happened in the business (who did what, when), and operational telemetry about how the running system is behaving (latencies, errors, queues). Same software, different audiences, different requirements.

Audit log

If there's a write, there should be a logged event. Every state change should get recorded. Who did it. What changed. When. From where.

Operational telemetry

App logs should be structured and searchable and compliant with observability tools.

Context travels with requests

Information about who's asking and from where should flow through the system. Even code deep in the stack should be capable of recording meaningful context. This matters for debugging and compliance.

Errors have structure

When things fail, they fail informatively. Errors carry machine-readable codes for automation, human-readable messages for users, and structured details for debugging. The same error can be logged consistently, analyzed programmatically, and presented appropriately to different audiences.


Security

Defense in depth

Security lives at multiple layers. Application code checks permissions. Server renders strict security policy headers. Databases enforce isolation. Network policies restrict access.

Isolation as architecture

When systems handle multiple customers or contexts, isolation needs to be structural. The architecture should make cross-boundary access extremely hard to achieve, not just discouraged by policy.

Secure defaults

Security features are on by default. Opting out requires explicit action. The easy path is the secure path.

Secrets stay secret

Logs support debugging and compliance without becoming security liabilities. Sensitive values are hashed or omitted.