- Choose the most straightforward solution that addresses the requirements
- Favor readability over cleverness
- Minimize complexity by using built-in features before custom implementations
- Ask: "Could a new developer understand this code without extensive explanation?"
- Don't implement functionality until it's actually needed
- Avoid speculative features based on what "might be needed later"
- Focus on the current requirements from the Frontend Requests documentation
- If a feature isn't explicitly requested in the specs, don't build it
- Extract common logic into utility functions or services where it makes sense
- But don't over-abstract - sometimes duplication is clearer than the wrong abstraction
- Only extract code when you've seen the pattern repeated at least 3 times
- Balance DRY with readability and maintainability
- Each module should have one clear purpose and responsibility
- Clear boundaries between modules - e.g., sync module handles sync logic, events module handles events
- Functions should do one thing and do it well
- Keep file size manageable (generally under 300 lines)
-
Explicit is better than implicit
- Use explicit function returns rather than side effects
- Prefer named exports over default exports
- Use descriptive variable and function names
-
Favor composition over inheritance
- Build functionality by combining simple pieces
- Use dependency injection through function parameters
-
Maintain clear boundaries
- Sync module should not directly handle analytics computation
- Events module should not know about sync details
- Frontend-compatible modules can use both but keep the integration simple
-
Error handling
- Don't swallow errors - log properly and return appropriate status codes
- Use consistent error handling patterns across modules
- Create specific error types only when truly needed
-
Strategic Logging
- Log only essential information that provides actual value
- Focus on error conditions, sync operations, and significant state changes
- Avoid logging routine operations or request/response data that might contain sensitive information
- Use log levels appropriately (error, warn, info, debug)
- Don't log inside loops unless absolutely necessary
- Include just enough context to troubleshoot issues without excessive detail
-
Dependency Management
- Minimize external dependencies - use what's in the package.json
- Before adding a new library, ask if built-in Node.js or existing modules can handle it
-
Function Design
- Keep functions small (under 30 lines if possible)
- Minimize function parameters (aim for 3 or fewer)
- Avoid nested callbacks deeper than 2 levels - use async/await
-
Commenting & Documentation
- Document "why" not "what" (the code should show what it does)
- Add comments for non-obvious business logic or edge cases
- Use JSDoc for public API functions
-
Database/ORM Usage
- Use Prisma features appropriately (transactions, relations)
- Keep database queries efficient - select only needed fields
- Consider pagination for large data sets
-
Premature Optimization
- Don't optimize code until performance issues are identified
- Focus on correct functionality before optimizing
-
Over-Engineering
- Don't create complex abstraction layers "just in case"
- Avoid design patterns that don't clearly improve the codebase
- Prefer simple functions over complex class hierarchies
-
Magic Numbers/Strings
- Use named constants for values that have meaning
- But don't create constants for values used only once
-
Excessive Abstraction
- Don't create abstractions that hide more than they reveal
- If an abstraction makes code harder to understand, it's the wrong abstraction
When making implementation decisions, ask these questions:
- Necessity: Does this code directly address a requirement in the spec?
- Simplicity: Is this the simplest way to solve the problem?
- Clarity: Will others (and future you) understand this code easily?
- Maintainability: How difficult will this be to change or debug later?
- Conventions: Does this follow the established patterns in the codebase?
The goal is to create a maintainable solution not to create the most elegant or sophisticated possible solution.
Good code is code that works correctly and can be understood, maintained, and modified by humans. Prioritize these qualities over technical brilliance or advanced patterns.