Development Tenets for do.dev
These core principles guide every decision we make in building and maintaining the do.dev platform. They are not aspirational - they are requirements.
1. Real-Time First 🚀
We build for the speed of thought.
- Everything should be as near to real-time as possible
- Use Convex real-time subscriptions for data that isn't natively real-time
- User actions should have immediate visual feedback
- Background processes should use optimistic updates
- WebSocket connections over polling when feasible
2. Fail Gracefully, Never Silently 🛡️
Every edge case is a user's reality.
- Handle loading states explicitly (no blank screens)
- Account for race conditions (auth initialization, webhook delays)
- Provide meaningful error messages that guide users to solutions
- Always have a fallback (if Clerk fails, if webhooks are delayed, if APIs timeout)
- Log errors comprehensively but never expose sensitive data
3. Type Safety is Non-Negotiable 📋
If it compiles, it should work.
- TypeScript strict mode always on
- No
anytypes without explicit justification - Define interfaces for all data structures
- Test TypeScript compilation locally before deploying
- Prefer explicit interfaces over conditional types
4. Developer Experience is User Experience 👩💻
The best code is code that developers love to work with.
- Clear, semantic naming (no abbreviations unless universal)
- Self-documenting code over extensive comments
- Consistent patterns across the codebase
- Git commits that tell a story
- Fast local development with hot reload
- Comprehensive development documentation
5. Performance from Day One ⚡
Speed is a feature, not an optimization.
- Measure performance impact before merging
- Lazy load what you can
- Bundle size matters - check it
- Optimize images and assets
- Use proper caching strategies
- Server-side render when it improves UX
6. Security by Default 🔒
Trust no input, verify everything.
- All user input must be validated
- Authentication and authorization on every protected route
- Webhooks must be verified
- API keys and secrets in environment variables only
- Regular security audits
- Principle of least privilege for all roles
7. Don't Repeat Yourself (DRY) 🔄
Write once, use everywhere.
- Shared components over duplicated code
- Centralized configuration
- Reusable utilities and hooks
- Single source of truth for business logic
- Extract patterns when you see them repeated 3+ times
8. Progressive Enhancement 📈
Start simple, enhance intelligently.
- Core functionality works without JavaScript
- Features degrade gracefully
- Mobile-first responsive design
- Accessibility from the start, not as an afterthought
- Support users on slower connections
9. Ship Small, Ship Often 🚢
Perfect is the enemy of shipped.
- Small, focused pull requests
- Feature flags for gradual rollouts
- Continuous deployment
- Quick iterations based on user feedback
- Rollback strategy for every deployment
10. User Trust is Sacred 🤝
Every interaction should build confidence.
- Transparent about system status
- Clear about data usage
- Honest about limitations
- Quick to acknowledge and fix issues
- Proactive communication during outages
11. Data Integrity Above All 💾
Lost data loses users.
- ACID compliance where it matters
- Backup strategies for critical data
- Audit trails for important operations
- Data validation at every layer
- Clear data retention policies
12. Observability is Mandatory 📊
You can't fix what you can't see.
- Structured logging with context
- Performance monitoring on key metrics
- Error tracking with proper grouping
- User behavior analytics (privacy-respecting)
- Real-time alerts for critical issues
Applying These Tenets
When making decisions, ask yourself:
- Does this make the user experience more real-time?
- Have I handled all the ways this could fail?
- Is this type-safe and will it catch errors at compile time?
- Would I enjoy maintaining this code in 6 months?
- Will this perform well at scale?
- Is this secure by default?
- Am I repeating code that already exists?
- Does this work for all users, not just the ideal case?
- Can I ship this incrementally?
- Does this build or erode user trust?
- Is the data safe and validated?
- Will I know if this breaks in production?
Examples in Practice
✅ Good: Webhook Race Condition Handling
// Assume new users without roles are waitlist (webhook hasn't fired yet)
const isWaitlistUser = userRoles.length === 0 || (userRoles.length === 1 && userRoles[0] === "waitlist")❌ Bad: Silent Failures
// Don't do this
try {
await saveUser(data)
} catch (e) {
// Silent failure - violates Tenet #2
}✅ Good: Type-Safe Interfaces
interface AuthUser {
id: string
email: string
roles: string[]
// ... other fields
}❌ Bad: Using Any
// Don't do this
const processData = (data: any) => {
// Violates Tenet #3
}Living Document
These tenets evolve with our understanding. If you find yourself consistently violating a tenet, either:
- You're doing something wrong, or
- The tenet needs updating
Discuss with the team before changing core tenets.