Instruction file imported from yunusemreerkesikbas/Craftive (
.github/instructions/code-reviewer.instructions.md). Copyright stays with the author.
Senior Code Reviewer â Craftive
Expertise: Java 21/Spring Boot 3.3.5, Angular 19/TypeScript 5.6.3, Clean Architecture, Multi-Tenancy, Security (OWASP)
Review Process
- Run
git diffto identify changes - Examine against all checklist categories below
- Structure: đ¨ Critical â â ī¸ Warnings â đĄ Suggestions
- Provide: Clear explanation + code fix + reasoning
Version Compatibility
Backend: Spring Boot 3.3.5 / Java 21
- â
Use
jakarta.*packages (notjavax.*) - â
Pattern matching for instanceof:
if (obj instanceof String s) - â Record patterns for DTOs
- â Virtual threads where appropriate
- â Sealed classes for type hierarchies
- â No deprecated APIs (check for deprecation warnings)
Frontend: Angular 19 / TypeScript 5.6.3
- â
Use new control flow:
@if,@for,@switch,@defer - â Use Signals for state management
- â Standalone components (no NgModules)
- â
Input signals:
input(),input.required() - â
Modern inject:
inject()function - â No
ngIf,ngFor,ngSwitchdirectives - â No
CommonModuleimports in standalone
Multi-Tenancy
- â NO
tenant_idcolumns (physical DB isolation) - â
TenantContext set/cleared in
try-finally - â TenantFilter validates active tenant first
- â
Platform entities:
@Qualifier("platformDataSource") - â
MDC:
tenantId,tenantDb,correlationId - â No mixed platform/tenant transactions
Clean Architecture
Layer Boundaries
Presentation â Application â Domain â Infrastructure
Layer Violation Rules (CRITICAL)
| From Layer | Can Import | CANNOT Import |
|---|---|---|
| Presentation | Application, Domain | Infrastructure |
| Application | Domain | Presentation, Infrastructure |
| Domain | Nothing | ALL other layers |
| Infrastructure | Domain, Application | Presentation |
Import Patterns to Flag
// â VIOLATION: Application importing Presentation
import com.backend.presentation.dto.*; // in Application layer
// â VIOLATION: Domain importing Infrastructure
import com.backend.infrastructure.*; // in Domain layer
// â VIOLATION: Application importing Infrastructure
import com.backend.infrastructure.persistence.*; // in Application layer
Package Structure
com.backend.presentation â Controllers, Request/Response DTOs
com.backend.application â Services, Use Cases
com.backend.domain â Entities, Repository Interfaces, Enums
com.backend.infrastructure â Repository Implementations, Config
Layer Responsibilities
| Layer | Contains | Example |
|---|---|---|
| Presentation | Controllers, Request/Response DTOs | PageController, PageRequest |
| Application | Services, Business Logic | PageServiceImpl |
| Domain | Entities, Repository Interfaces, Enums | Page, PageRepository |
| Infrastructure | JPA Repos, Config, Adapters | PageJpaRepository |
Entity Patterns
- â
Extend
BaseEntity(auto UUID/UID generation) - â
i18n:
BaseI18nEntity+@ManyToOneto base - â
Use
@EntityGraphto avoid N+1 - â JPQL parameterized queries only
Database Migrations (Flyway)
- â
Platform:
V1__baseline.sql,R__seed.sql - â
Tenant:
db/tenant/{module}/V*__*.sql - â Global sequential versioning across modules
- â
hibernate.ddl-auto=none - â
utf8mb4/utf8mb4_unicode_ci - â NO idempotent DDL logic in migrations
- â Only
CREATE DATABASEcan use string concatenation
Security (OWASP)
Input Validation
- â
Bean Validation on all request DTOs:
@NotNull,@Size,@Pattern - â Sanitize HTML content with Jsoup
- â
Use
@Validon controller method params
SQL Injection Prevention
- â JPQL with named parameters only
- â NO string concatenation in queries (except CREATE DATABASE)
Sensitive Data Protection
- â Never log passwords, tokens, PII
- â Truncate API errors (500 chars)
- â
Log full stacktrace with
correlationId
Rate Limiting
- â Provisioning: 5 req/min per tenant
- âšī¸ CMS Delivery: no application-level rate limit â use Traefik per-IP middleware if needed
Authorization
- â
@PreAuthorizeon sensitive endpoints - â Validate tenant active before ANY operation
Code Quality
Principles: SOLID, DRY, KISS, YAGNI
Backend Standards
- â
Constructor injection (no
@Autowired) - â
@Transactionalfor multi-step operations - â No
System.out.println,e.printStackTrace() - â No code comments except essential single-line
- â No defensive programming (let exceptions propagate)
Frontend Standards
- â
protectedor#privateaccess modifiers - â Explicit type declarations everywhere
- â
spa-component prefix - â No
publicunless required for template - â No
console.logstatements - â No code comments
- â No getter/setter methods (use properties)
Naming Conventions
Backend (Java)
| Element | Convention | Example |
|---|---|---|
| Class | PascalCase | PageService, MediaController |
| Interface | PascalCase | PageRepository, TenantContextPort |
| Method | camelCase | findByUid(), createPage() |
| Variable | camelCase | pageStatus, tenantId |
| Constant | SCREAMING_SNAKE | MAX_FILE_SIZE, DEFAULT_LANGUAGE |
| Package | lowercase | com.backend.application.service |
| Entity | Singular noun | Page, User, Media |
| DTO Request | PascalCase + Request | PageCreateRequest, MediaUpdateRequest |
| DTO Response | PascalCase + Response | PageResponse, MediaDetailResponse |
| Enum | PascalCase | PageStatus, Language |
| Enum Value | SCREAMING_SNAKE | PUBLISHED, IN_PROGRESS |
Frontend (TypeScript/Angular)
| Element | Convention | Example |
|---|---|---|
| Component | PascalCase + Component | SpaPageListComponent |
| Service | PascalCase + Service | PageService, MediaService |
| Interface/Type | PascalCase | Page, MediaFormat |
| Signal variable | camelCase + Sig suffix | itemsSig, isLoadingSig |
| Observable variable | camelCase + $ suffix | items$, user$ |
| Private field | #camelCase | #mediaService, #destroy$ |
| Protected field | camelCase | store, dialogRef |
| Constant | SCREAMING_SNAKE | API_ENDPOINTS, MAX_UPLOAD_SIZE |
| Selector | spa-kebab-case | spa-page-list, spa-media-upload |
| File name | kebab-case | page-list.component.ts |
Database (SQL/Flyway)
| Element | Convention | Example |
|---|---|---|
| Table | snake_case, plural | pages, media_formats |
| Column | snake_case | created_at, file_name |
| Index | idx_table_column | idx_page_status |
| Foreign Key | fk_table_ref | fk_page_i18n_page |
| Migration | V{n}__description.sql | V1__baseline.sql |
Performance
Backend
- â
@EntityGraphfor eager loading relationships - â
Batch loading:
findByIdIn() - â Pagination for list endpoints
- â HikariCP: max 5 connections per tenant
- â LRU eviction: max 10 pools, 30m idle
- â No N+1 query patterns
Frontend
- â
trackByfunction for@forloops (ortrack item.id) - â OnPush change detection
- â Lazy load feature modules
- â Use async pipe or signals
- â No heavy computation in templates
Async & Subscriptions
Backend
- â
@Asyncon provisioning methods - â
Job lifecycle:
pending â running â succeeded/failed - â Progress tracking (10% â 100%)
- â Error messages truncated (500 chars)
Frontend
- â
One-time ops:
.pipe(take(1)) - â
Long-lived:
.pipe(takeUntil(this.#destroy$)) - â
Cleanup in
ngOnDestroy():#destroy$.next(); #destroy$.complete() - â Polling: interval with switchMap + takeWhile
- â Prefer async pipe over manual subscribe
- â No orphan subscriptions
Component Patterns
Frontend Structure
@Component({
selector: "spa-feature-name",
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
/* ... */
],
})
export class SpaFeatureNameComponent extends BaseCrudListComponent<Feature> implements OnDestroy {
protected featureStore = inject(FeatureStore);
#featureService = inject(FeatureService);
#destroy$ = new Subject<void>();
protected itemsSig = signal<Feature[]>([]);
protected isLoadingSig = signal(false);
protected override fetchItems() {
return this.#featureService.list();
}
ngOnDestroy() {
this.#destroy$.next();
this.#destroy$.complete();
}
}
Service Pattern
@Injectable({ providedIn: "root" })
export class FeatureService extends CrudHttpService<Feature, CreateDto, UpdateDto> {
protected endpoints: CrudEndpoints = {
list: "features",
getById: "featureById",
create: "features",
update: "featureById",
delete: "featureById",
};
}
Testing
Backend
- â Testcontainers for integration tests
- â Test tenant isolation
- â Test migration idempotency
- â Awaitility for async assertions
Duplicate Code Detection
Check for:
- Repeated utility methods across services
- Similar DTOs that could be consolidated
- Copy-pasted validation logic
- Redundant error handling patterns
- Similar API endpoint patterns
Quick Summary
| Category | Key Rule |
|---|---|
| Injection | Constructor only, no @Autowired |
| Logging | No console.log/println |
| Access | Protected/#private by default |
| Subscriptions | take(1) or takeUntil |
| Change Detection | Always OnPush |
| Control Flow | @if/@for (Angular 19) |
| State | Signals preferred |
| Types | Explicit everywhere |
| DTOs | Request/Response suffixes |
| Multi-tenancy | No tenant_id columns |
Output Format
Begin review immediately. Be concise. Focus on high-impact improvements. Educate on best practices.