Update .cursorrules with type-driven design principles
Document design preferences observed in this project: Principles: - Parse, Don't Validate (newtype wrappers with validation) - Correctness by Construction (illegal states unrepresentable) - Functional Core, Imperative Shell (pure domain + DI) - Ghost of Departed Proofs (proof tokens like ValidConfigToken) Guidelines: - Module organization (single responsibility, < 300 lines) - Error handling (Result types, structured logging) - Code quality (no dead code, comprehensive tests) - Testing strategy (pure functions, mocks, isolation) These rules capture the architectural decisions made throughout the sqlite-serve implementation.
This commit is contained in:
52
.cursorrules
52
.cursorrules
@ -1,2 +1,52 @@
|
|||||||
|
# Environment
|
||||||
- Always use `direnv exec "$PWD"` when invoking cargo and other tools that need access to the nix/direnv environment
|
- Always use `direnv exec "$PWD"` when invoking cargo and other tools that need access to the nix/direnv environment
|
||||||
- Avoid using `head` and `tail` to avoid missing output
|
- Avoid using `head` and `tail` to avoid missing output
|
||||||
|
|
||||||
|
# Design Principles
|
||||||
|
|
||||||
|
## Parse, Don't Validate
|
||||||
|
- Create newtype wrappers for domain concepts (DatabasePath, SqlQuery, etc.)
|
||||||
|
- Validate at parse time, not at use time
|
||||||
|
- Use Result types for parsing - if you have the type, it's valid
|
||||||
|
- Make illegal states unrepresentable
|
||||||
|
|
||||||
|
## Correctness by Construction
|
||||||
|
- Use the type system to enforce invariants
|
||||||
|
- Prefer types that can only represent valid states
|
||||||
|
- Example: SqlQuery can only be constructed from SELECT statements
|
||||||
|
|
||||||
|
## Functional Core, Imperative Shell
|
||||||
|
- Keep business logic pure and testable (domain.rs)
|
||||||
|
- Move I/O to adapter layer (adapters.rs)
|
||||||
|
- Use dependency injection via traits
|
||||||
|
- Handler should be minimal orchestration only
|
||||||
|
|
||||||
|
## Ghost of Departed Proofs
|
||||||
|
- Use proof tokens to carry compile-time guarantees
|
||||||
|
- Example: ValidConfigToken proves config is non-empty
|
||||||
|
- Functions accept tokens, not raw data
|
||||||
|
- Type system enforces the proofs
|
||||||
|
|
||||||
|
## Module Organization
|
||||||
|
- Single responsibility per module
|
||||||
|
- Small, focused files (< 300 lines preferred)
|
||||||
|
- Tests co-located with code (#[cfg(test)])
|
||||||
|
- Clear separation: types → domain → adapters → glue
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
- Use Result types, not exceptions
|
||||||
|
- Structured logging with context
|
||||||
|
- User-friendly error messages
|
||||||
|
- Fail fast on invalid configuration
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
- No dead code - delete unused functions immediately
|
||||||
|
- Comprehensive tests for all modules
|
||||||
|
- Prefer pure functions over stateful code
|
||||||
|
- Keep handlers minimal - logic belongs in tested modules
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- Test pure functions with simple inputs
|
||||||
|
- Use mock implementations for DI traits
|
||||||
|
- Aim for >90% coverage
|
||||||
|
- Tests should be fast and isolated
|
||||||
Reference in New Issue
Block a user