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.