Overview
This guide provides comprehensive information for developers who will maintain and extend RLink. It covers routine maintenance tasks, code organization, best practices, and common workflows.
Daily Development Workflow
Starting Work
Pull Latest Changes
git checkout main
git pull origin main
Create Feature Branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/bug-description
Install Dependencies (if needed)
Before Committing
Committing Changes
Follow conventional commit format:
# Format: <type>(<scope>): <description>
# Examples:
git commit -m "feat(cms): add project photo gallery"
git commit -m "fix(crm): resolve reservation linking bug"
git commit -m "docs: update API documentation"
git commit -m "refactor(iam): improve user query performance"
Commit Types :
feat: New feature
fix: Bug fix
docs: Documentation changes
refactor: Code refactoring
test: Adding tests
chore: Maintenance tasks
Code Organization
Directory Structure Guidelines
Pattern : /api/{module}/{resource}/route.tsapp/api/
├── cms/
│ ├── projects/
│ │ ├── route.ts # GET /api/cms/projects (list)
│ │ ├── [id]/
│ │ │ └── route.ts # GET/PATCH/DELETE /api/cms/projects/[id]
│ │ └── export/
│ │ └── route.ts # GET /api/cms/projects/export (CSV)
│ ├── articles/
│ └── careers/
├── crm/
└── iam/
Best Practice : Keep routes RESTful and predictable
Pattern : /lib/{module}/{purpose}.tslib/
├── cms/
│ ├── types.ts # TypeScript types/interfaces
│ ├── queries.ts # Database queries
│ ├── cache.ts # Cache helpers
│ └── validation.ts # Input validation schemas
├── crm/
├── iam/
├── db.ts # Database connection
├── auth.ts # Auth utilities
└── email/ # Email utilities
Best Practice : Keep business logic separate from API routes
Pattern : Organize by type or featurecomponents/
├── layout/
│ ├── Navbar.tsx
│ ├── Sidebar.tsx
│ └── Footer.tsx
├── tables/
│ ├── DataTable.tsx
│ ├── Pagination.tsx
│ └── Filters.tsx
├── modals/
│ ├── ConfirmDialog.tsx
│ └── FormModal.tsx
└── ui/ # shadcn/ui primitives
├── button.tsx
├── input.tsx
└── ...
Best Practice : Reuse components, avoid duplication
Pattern : Separate files by concerndb/
├── schema.ts # Application tables
├── auth-schema.ts # Better Auth tables
└── types.ts # Shared DB types
Best Practice : Use Drizzle ORM types, avoid raw SQL when possible
Database Management
Adding a New Table
Define Schema
Add to db/schema.ts: import { pgTable , uuid , varchar , timestamp } from 'drizzle-orm/pg-core' ;
export const newTable = pgTable ( 'new_table' , {
id: uuid ( 'id' ). defaultRandom (). primaryKey (),
name: varchar ( 'name' , { length: 255 }). notNull (),
createdAt: timestamp ( 'created_at' ). defaultNow (). notNull (),
updatedAt: timestamp ( 'updated_at' ). defaultNow (). notNull (),
});
Generate Migration
Review generated SQL in drizzle/ directory
Apply Migration
# Development
npx drizzle-kit migrate
# Production
# Set production DATABASE_URL
DATABASE_URL = "..." npx drizzle-kit migrate
Update Types
Create TypeScript types in lib/{module}/types.ts: import { InferSelectModel , InferInsertModel } from 'drizzle-orm' ;
import { newTable } from '@/db/schema' ;
export type NewTableSelect = InferSelectModel < typeof newTable >;
export type NewTableInsert = InferInsertModel < typeof newTable >;
Modifying Existing Tables
NEVER modify production data directly! Always use migrations.
Update Schema
Modify the table definition in db/schema.ts
Review Migration
Check the generated SQL carefully, especially for:
Data loss (dropping columns)
Constraint violations
Performance impact (adding indexes to large tables)
Test in Development
Apply migration to development database and test thoroughly
Apply to Production
Only after thorough testing
Adding New Features
Feature Development Checklist
Example: Adding a New CMS Feature
Let’s say you want to add “Testimonials” to the CMS:
1. Database Schema
// db/schema.ts
export const testimonials = pgTable ( 'testimonials' , {
id: uuid ( 'id' ). defaultRandom (). primaryKey (),
clientName: varchar ( 'client_name' , { length: 255 }). notNull (),
projectId: uuid ( 'project_id' ). references (() => projects . id ),
content: text ( 'content' ). notNull (),
rating: integer ( 'rating' ). notNull (), // 1-5
published: boolean ( 'published' ). default ( false ),
createdAt: timestamp ( 'created_at' ). defaultNow (). notNull (),
});
Generate and apply migration: npx drizzle-kit generate
npx drizzle-kit migrate
2. Types
// lib/cms/types.ts
export type Testimonial = InferSelectModel < typeof testimonials >;
export type TestimonialInsert = InferInsertModel < typeof testimonials >;
3. API Routes
// app/api/cms/testimonials/route.ts
import { db } from '@/lib/db' ;
import { testimonials } from '@/db/schema' ;
export async function GET ( request : Request ) {
try {
const data = await db . select (). from ( testimonials );
return Response . json ( data );
} catch ( error ) {
return Response . json (
{ error: 'Failed to fetch testimonials' },
{ status: 500 }
);
}
}
export async function POST ( request : Request ) {
try {
const body = await request . json ();
const [ result ] = await db . insert ( testimonials ). values ( body ). returning ();
return Response . json ( result , { status: 201 });
} catch ( error ) {
return Response . json (
{ error: 'Failed to create testimonial' },
{ status: 500 }
);
}
}
4. UI Components
// app/home/cms/testimonials/page.tsx
'use client' ;
import { useQuery } from '@tanstack/react-query' ;
export default function TestimonialsPage () {
const { data , isLoading } = useQuery ({
queryKey: [ 'testimonials' ],
queryFn : async () => {
const res = await fetch ( '/api/cms/testimonials' );
return res . json ();
},
});
if ( isLoading ) return < LoadingSkeleton />;
return (
< div >
< h1 > Testimonials </ h1 >
{ /* Table/list component here */ }
</ div >
);
}
5. Navigation
Add to sidebar navigation in layout component
6. Documentation
Update API reference and feature documentation
Database Query Optimization
// db/schema.ts
import { index } from 'drizzle-orm/pg-core' ;
export const projects = pgTable ( 'projects' , {
// ... columns
}, ( table ) => {
return {
statusIdx: index ( 'projects_status_idx' ). on ( table . status ),
createdAtIdx: index ( 'projects_created_at_idx' ). on ( table . createdAt ),
};
});
Select Only Needed Fields
// Bad - fetches all columns
const projects = await db . select (). from ( projectsTable );
// Good - fetches only needed columns
const projects = await db
. select ({
id: projectsTable . id ,
name: projectsTable . name ,
status: projectsTable . status ,
})
. from ( projectsTable );
import { eq } from 'drizzle-orm' ;
const projectsWithGalleries = await db
. select ()
. from ( projects )
. leftJoin ( galleries , eq ( projects . id , galleries . projectId ))
. limit ( 10 );
const { data } = useQuery ({
queryKey: [ 'projects' ],
queryFn: fetchProjects ,
staleTime: 5 * 60 * 1000 , // 5 minutes
cacheTime: 10 * 60 * 1000 , // 10 minutes
});
import dynamic from 'next/dynamic' ;
const HeavyComponent = dynamic (() => import ( './HeavyComponent' ), {
loading : () => < LoadingSkeleton />,
ssr: false , // If component doesn't need SSR
});
import Image from 'next/image' ;
< Image
src = "/uploads/large-image.jpg"
alt = "Project"
width = { 800 }
height = { 600 }
quality = { 75 }
placeholder = "blur"
blurDataURL = "data:image/..." // Optional blur placeholder
/>
Testing
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run specific test file
npm test -- users.test.ts
Writing Tests
Create tests in __tests__/ directory:
// __tests__/api/cms/projects.test.ts
import { GET } from '@/app/api/cms/projects/route' ;
describe ( 'GET /api/cms/projects' , () => {
it ( 'should return list of projects' , async () => {
const request = new Request ( 'http://localhost:3000/api/cms/projects' );
const response = await GET ( request );
const data = await response . json ();
expect ( response . status ). toBe ( 200 );
expect ( Array . isArray ( data )). toBe ( true );
});
it ( 'should support pagination' , async () => {
const request = new Request ( 'http://localhost:3000/api/cms/projects?page=1&limit=5' );
const response = await GET ( request );
const data = await response . json ();
expect ( data . data . length ). toBeLessThanOrEqual ( 5 );
expect ( data . pagination ). toBeDefined ();
});
});
Security Maintenance
Regular Security Tasks
Dependency Updates
Update Dependencies
# Update all dependencies
npm update
# Or update specific package
npm update package-name
Test Thoroughly
Run tests and manually test critical features
Commit Changes
git commit -m "chore: update dependencies"
Monitoring & Maintenance
Weekly Tasks
Monthly Tasks
Quarterly Tasks
Debugging Tips
Database Issues
# Connect to database
psql " $DATABASE_URL "
# Useful queries
SELECT * FROM __drizzle_migrations ORDER BY created_at DESC ;
SELECT COUNT ( * ) FROM users ;
SELECT * FROM activity_logs WHERE created_at > NOW () - INTERVAL '1 hour' ;
# Check table sizes
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename )) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size ( schemaname || '.' || tablename ) DESC ;
API Debugging
Enable verbose logging in development:
// lib/db.ts
import { drizzle } from 'drizzle-orm/neon-http' ;
import { neon } from '@neondatabase/serverless' ;
const sql = neon ( process . env . DATABASE_URL ! );
export const db = drizzle ( sql , {
logger: process . env . NODE_ENV === 'development' ,
});
Frontend Debugging
Use React Query DevTools:
// app/layout.tsx
import { ReactQueryDevtools } from '@tanstack/react-query-devtools' ;
export default function RootLayout ({ children }) {
return (
< html >
< body >
< QueryClientProvider client = { queryClient } >
{ children }
< ReactQueryDevtools initialIsOpen = { false } />
</ QueryClientProvider >
</ body >
</ html >
);
}
Common Workflows
Fixing a Bug
Reproduce Locally
Understand the issue and reproduce in development
Create Fix Branch
git checkout -b fix/bug-description
Write Test
Write failing test that demonstrates the bug
Implement Fix
Fix the issue and verify test passes
Test Manually
Manually test the fix in development
Create PR
Push branch and create pull request
Deploying to Production
Merge to Main
After PR approval, merge to main branch
Automatic Deployment
Vercel automatically deploys main branch
Monitor Deployment
Watch deployment logs in Vercel dashboard
Verify Production
Test critical features in production
Rollback if Needed
If issues occur, promote previous deployment
Documentation
Keeping Documentation Updated
Documentation should be updated WITH code changes, not as an afterthought.
When to update docs :
New feature added → Update feature docs + API reference
API endpoint changed → Update API reference
Database schema changed → Update database schema docs
Bug fix → Update troubleshooting if applicable
Configuration changed → Update getting started / deployment
Documentation Standards
Use clear, concise language
Include code examples
Add warnings for breaking changes
Flag anything not verified against the repo with a clear Note or link to source
Keep changelog up to date
Best Practices Summary
Code Quality
Follow TypeScript best practices
Use ESLint and Prettier
Write meaningful comments
Keep functions small and focused
Git Workflow
Use feature branches
Write descriptive commit messages
Keep commits atomic
Rebase before merging
Testing
Write tests for new features
Test edge cases
Maintain high coverage
Run tests before committing
Security
Never commit secrets
Validate all inputs
Use parameterized queries
Keep dependencies updated
Getting Help
If you need assistance:
Check Documentation : This documentation site
Review Code : Look at existing implementations
Search Issues : GitHub issues for similar problems
Ask Team : Reach out to other developers
External Resources :
Next Steps
Architecture Understand the system design
REST API reference Explore API endpoints
Troubleshooting Solve common issues
Deployment Deploy to production