d4ea931bf8
* feat(agents): add modular engineering rules from 2026 standards Add a rules directory with individual rule files derived from the Cal.com Engineering in 2026 and Beyond blog post. Rules are organized by section (architecture, quality, data, api, performance, testing, patterns, culture) following the Vercel agent-skills structure. Includes: - _sections.md defining rule categories and impact levels - _template.md for creating new rules - 14 individual rule files covering key engineering standards - README documenting the rules structure and usage Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(agents): consolidate DI and Repository+DTO docs into rules - Move di-pattern.md content to rules/patterns-di-pattern.md - Extract Repository + DTO section from knowledge-base.md into: - rules/data-repository-methods.md (method naming conventions) - rules/data-dto-boundaries.md (DTO location and naming) - Update knowledge-base.md to reference the new rule files - Delete old di-pattern.md file Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore(agents): remove stub reference sections from knowledge-base.md The rules directory is self-contained with its own README, so these redirect sections are unnecessary clutter. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(agents): combine DI pattern rules into single file Merged patterns-di-pattern.md into patterns-dependency-injection.md to eliminate overlap and create one comprehensive DI guide. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
5.3 KiB
5.3 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Repository Method Naming Conventions | HIGH | Improves code discoverability and reusability | data, repository, naming, conventions, methods |
Repository Method Naming Conventions
Impact: HIGH
Repository methods should follow consistent naming conventions to improve discoverability and promote code reuse across different features.
Rule 1: Don't include the repository's entity name in method names
Method names should be concise and avoid redundancy since the repository class name already indicates the entity type.
// Good - Concise method names
class BookingRepository {
findById(id: string) { ... }
findByUserId(userId: string) { ... }
create(data: BookingCreateInput) { ... }
delete(id: string) { ... }
}
// Bad - Redundant entity name in methods
class BookingRepository {
findBookingById(id: string) { ... }
findBookingByUserId(userId: string) { ... }
createBooking(data: BookingCreateInput) { ... }
deleteBooking(id: string) { ... }
}
Rule 2: Use include or similar keywords for methods that fetch relational data
When a method retrieves additional related entities, make this explicit in the method name using keywords like include, with, or andRelations.
// Good - Clear indication of included relations
class EventTypeRepository {
findById(id: string) {
return prisma.eventType.findUnique({
where: { id },
});
}
findByIdIncludeHosts(id: string) {
return prisma.eventType.findUnique({
where: { id },
include: { hosts: true },
});
}
findByIdIncludeHostsAndSchedule(id: string) {
return prisma.eventType.findUnique({
where: { id },
include: { hosts: true, schedule: true },
});
}
}
// Bad - Unclear what data is included
class EventTypeRepository {
findById(id: string) {
return prisma.eventType.findUnique({
where: { id },
include: { hosts: true, schedule: true },
});
}
findByIdForReporting(id: string) {
return prisma.eventType.findUnique({
where: { id },
include: { hosts: true },
});
}
}
Rule 3: Keep methods generic and reusable - avoid use-case-specific names
Repository methods should be general-purpose and describe what data they return, not how or where it's used. This promotes code reuse across different features.
// Good - Generic, reusable methods
class BookingRepository {
findByUserIdIncludeAttendees(userId: string) {
return prisma.booking.findMany({
where: { userId },
include: { attendees: true },
});
}
findByDateRangeIncludeEventType(startDate: Date, endDate: Date) {
return prisma.booking.findMany({
where: {
startTime: { gte: startDate },
endTime: { lte: endDate },
},
include: { eventType: true },
});
}
}
// Bad - Use-case-specific method names
class BookingRepository {
findBookingsForReporting(userId: string) {
return prisma.booking.findMany({
where: { userId },
include: { attendees: true },
});
}
findBookingsForDashboard(startDate: Date, endDate: Date) {
return prisma.booking.findMany({
where: {
startTime: { gte: startDate },
endTime: { lte: endDate },
},
include: { eventType: true },
});
}
}
Rule 4: No business logic in repositories
Repositories should only handle data access. Business logic, validations, and complex transformations belong in the Service layer.
// Good - Repository only handles data access
class BookingRepository {
findByIdIncludeAttendees(id: string) {
return prisma.booking.findUnique({
where: { id },
include: { attendees: true },
});
}
updateStatus(id: string, status: BookingStatus) {
return prisma.booking.update({
where: { id },
data: { status },
});
}
}
class BookingService {
async confirmBooking(bookingId: string) {
const booking = await this.bookingRepository.findByIdIncludeAttendees(bookingId);
if (!booking) {
throw new Error("Booking not found");
}
if (booking.status !== "PENDING") {
throw new Error("Only pending bookings can be confirmed");
}
await this.emailService.sendConfirmationToAttendees(booking.attendees);
return this.bookingRepository.updateStatus(bookingId, "CONFIRMED");
}
}
// Bad - Business logic in repository
class BookingRepository {
async confirmBooking(bookingId: string) {
const booking = await prisma.booking.findUnique({
where: { id: bookingId },
include: { attendees: true },
});
if (!booking) {
throw new Error("Booking not found");
}
if (booking.status !== "PENDING") {
throw new Error("Only pending bookings can be confirmed");
}
await sendEmailToAttendees(booking.attendees);
return prisma.booking.update({
where: { id: bookingId },
data: { status: "CONFIRMED" },
});
}
}
Summary
- Method names should be concise:
findByIdnotfindBookingById - Use
include/withkeywords when fetching relations:findByIdIncludeHosts - Keep methods generic and reusable:
findByUserIdIncludeAttendeesnotfindBookingsForReporting - No business logic in repositories - that belongs in Services
Reference: Cal.com Engineering Blog