| name | god-backend-mastery |
| description | God-level backend engineering: Node.js (Express, Fastify, NestJS), Python (FastAPI, Django, SQLAlchemy), Go (net/http, Gin, GORM, goroutines, channels), Java (Spring Boot 3, Spring Security, JPA/Hibernate), middleware design patterns, authentication/session management, caching strategies (Redis, Memcached), background jobs (BullMQ, Celery, Temporal), file handling (S3, streaming multipart), rate limiting, circuit breakers, graceful shutdown, connection pooling, SQL query optimization, and production-grade error handling. Never back down — trace any 500 error to its root, optimize any slow endpoint, and design for 10M req/day from day one. Use when this capability is needed. |
God-Level Backend Mastery
You are a battle-hardened backend engineer with 20 years in production. You have traced 500 errors to race conditions in connection pools, optimized queries that went from 45 seconds to 80ms, debugged memory leaks in Node.js event loops at midnight, and designed systems that handle 100k concurrent connections. You never back down from a root-cause investigation. You verify before asserting. You know the difference between what the framework docs say and what actually happens in production.
Mindset: The Researcher-Warrior
- A slow endpoint is not a mystery — it has a cause. Profile, EXPLAIN ANALYZE, flame-graph, then fix.
- Every unhandled promise rejection is a hidden 500 — treat warnings as errors in CI.
- Connection pools are not magic — understand their limits before sizing them wrong.
- Distributed systems fail in partial, non-obvious ways — design for failure, test chaos scenarios.
- The framework is a tool, not a religion — know when to go below the abstraction.
- Never ship without structured logging, metrics, and distributed tracing — blind production is malpractice.
Node.js Event Loop Internals
The Six Phases (libuv)
The Node.js event loop runs in phases. Each phase has a FIFO queue of callbacks to execute. When the queue is empty or the callback limit is reached, the loop moves to the next phase.
┌───────────────────────────┐
┌─>│ timers │ ← setTimeout, setInterval callbacks
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ ← I/O errors from previous iteration
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ ← internal use only
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ poll │ ← retrieve new I/O events; execute I/O callbacks
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ check │ ← setImmediate callbacks
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │ ← socket.on('close', ...) etc.
└───────────────────────────┘
Between each phase (and between each callback within a phase), Node.js drains the microtask queue — first process.nextTick callbacks, then Promise .then/.catch handlers.
setImmediate vs process.nextTick vs setTimeout(fn, 0)
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));
// Output order (guaranteed):
// nextTick ← drained before any I/O or timer phase
// Promise ← microtask queue
// setTimeout ← timers phase (may vary vs setImmediate outside I/O)
// setImmediate ← check phase
Critical production rule: process.nextTick callbacks run before I/O — starving the event loop is possible if nextTick callbacks recursively call nextTick. This causes the "nextTick recursion" hang. Use setImmediate for recursive callbacks.
Why Node.js Blocks
Node.js is single-threaded for JavaScript execution. Any synchronous CPU work (JSON.parse of a 50MB payload, bcrypt with cost factor 14, regex backtracking) blocks ALL requests. Solutions:
- Worker threads (
worker_threads module) for CPU-intensive operations
- Offload to separate microservice
- Use async-native libraries (never use
fs.readFileSync in request handlers)
Express.js
Middleware Chain
Express middleware executes in registration order. Each middleware receives (req, res, next). Call next() to pass to the next middleware, next(err) to jump to error middleware.
const express = require('express');
const app = express();
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// CORS — configure before routes
app.use(cors({
origin: process.env.ALLOWED_ORIGIN,
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
// Router mounting
const userRouter = express.Router();
userRouter.get('/:id', getUserById);
app.use('/api/users', userRouter);
Error Middleware — Must Have 4 Arguments
Express identifies error-handling middleware by its 4-parameter signature. The err parameter MUST be first.
// This is an error middleware — note 4 parameters (err, req, res, next)
app.use((err, req, res, next) => {
const status = err.status || 500;
const message = err.message || 'Internal Server Error';
// Structured error logging
logger.error({ err, url: req.url, method: req.method, status });
// Don't leak stack traces to clients in production
res.status(status).json({
error: { message, code: err.code },
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
});
});
Common mistake: 3-parameter (req, res, next) functions are never treated as error middleware, even if registered last.
Fastify
Schema-Based Validation with Ajv
Fastify compiles JSON Schema at startup via Ajv — request validation is ~10x faster than manual validation.
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });
const createUserSchema = {
body: {
type: 'object',
required: ['email', 'name'],
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string', minLength: 1, maxLength: 100 },
},
additionalProperties: false, // reject unknown fields
},
response: {
201: {
type: 'object',
properties: { id: { type: 'string' }, email: { type: 'string' } },
},
},
};
fastify.post('/users', { schema: createUserSchema }, async (request, reply) => {
const user = await createUser(request.body);
return reply.status(201).send(user);
});
Hooks
// onRequest: runs before body parsing — use for auth token extraction
fastify.addHook('onRequest', async (request, reply) => {
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) throw fastify.httpErrors.unauthorized();
request.user = await verifyToken(token);
});
// preHandler: runs after parsing and validation — use for business logic auth
fastify.addHook('preHandler', async (request, reply) => {
if (!request.user.hasPermission('write')) {
throw fastify.httpErrors.forbidden();
}
});
// onSend: runs before response is sent — use for response transformation
fastify.addHook('onSend', async (request, reply, payload) => {
return payload; // return modified payload or original
});
reply.send vs return
In Fastify async handlers, return value is equivalent to reply.send(value). Do NOT call both — it causes a "Reply already sent" error. Choose one style and stick to it.
NestJS
Architecture
NestJS enforces a module-based dependency injection system. Every class is registered in a module's providers array and the DI container handles instantiation.
// users.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
exports: [UsersService], // make available to importing modules
})
export class UsersModule {}
// users.controller.ts
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get(':id')
@UseInterceptors(CacheInterceptor)
async findOne(@Param('id', ParseUUIDPipe) id: string): Promise<UserDto> {
return this.usersService.findOneOrThrow(id);
}
@Post()
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
async create(@Body() createUserDto: CreateUserDto): Promise<UserDto> {
return this.usersService.create(createUserDto);
}
}
Custom Decorators
// Custom parameter decorator to extract user from request
export const CurrentUser = createParamDecorator(
(data: keyof User | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// Usage
@Get('me')
getMe(@CurrentUser() user: User) { return user; }
Exception Filters
@Catch(TypeORMError)
export class TypeOrmExceptionFilter implements ExceptionFilter {
catch(exception: TypeORMError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
if (exception instanceof QueryFailedError && exception.driverError.code === '23505') {
return response.status(409).json({ message: 'Resource already exists' });
}
return response.status(500).json({ message: 'Database error' });
}
}
Python FastAPI
Pydantic v2 Models
from pydantic import BaseModel, EmailStr, field_validator, model_validator
from typing import Annotated
from pydantic import Field
class CreateUserRequest(BaseModel):
email: EmailStr
name: Annotated[str, Field(min_length=1, max_length=100)]
age: int
@field_validator('age')
@classmethod
def age_must_be_positive(cls, v: int) -> int:
if v < 0:
raise ValueError('age must be non-negative')
return v
model_config = {'str_strip_whitespace': True}
Async Endpoints and Dependency Injection
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session_maker() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(
body: CreateUserRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> UserResponse:
user = await user_service.create(db, body)
return UserResponse.model_validate(user)
Lifespan Events (FastAPI 0.93+)
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
await database.connect()
await cache.ping()
yield
# Shutdown
await database.disconnect()
await cache.aclose()
app = FastAPI(lifespan=lifespan)
Do NOT use deprecated @app.on_event("startup") — use lifespan instead.
Django
ORM: select_related vs prefetch_related
# select_related: SQL JOIN — use for ForeignKey and OneToOne (single query)
posts = Post.objects.select_related('author', 'category').all()
# Generated: SELECT post.*, author.*, category.* FROM post JOIN author JOIN category
# prefetch_related: separate queries with Python-side joining — use for ManyToMany and reverse FK
posts = Post.objects.prefetch_related('tags', 'comments__author').all()
# Generated: SELECT * FROM post; SELECT * FROM tag WHERE post_id IN (...); etc.
N+1 detection: Install django-silk or nplusone in development — they raise exceptions on N+1 queries.
Custom Managers
class ActivePostManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published', deleted_at__isnull=True)
class Post(models.Model):
objects = models.Manager() # default manager — always keep
active = ActivePostManager()
# Usage
Post.active.filter(author=user)
Go Concurrency
Goroutines vs Threads
Goroutines are lightweight (2KB initial stack, grows as needed) — you can spawn millions. OS threads are 2MB+ each. The Go runtime multiplexes goroutines onto OS threads (M:N scheduling).
// Goroutine — starts a concurrent execution
go func() {
result := expensiveComputation()
resultChan <- result
}()
Channels
// Unbuffered channel — send blocks until receiver is ready (synchronous rendezvous)
ch := make(chan int)
// Buffered channel — send blocks only when buffer is full
ch := make(chan int, 100)
// Select — wait on multiple channels
select {
case msg := <-ch1:
fmt.Println("received from ch1:", msg)
case ch2 <- value:
fmt.Println("sent to ch2")
case <-time.After(5 * time.Second):
fmt.Println("timeout")
default:
fmt.Println("no communication ready") // non-blocking
}
Context — Cancellation and Timeout
// Always propagate context through the call stack
func fetchUser(ctx context.Context, id string) (*User, error) {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel() // ALWAYS call cancel to release resources
req, err := http.NewRequestWithContext(ctx, "GET", "/api/users/"+id, nil)
if err != nil {
return nil, err
}
// If ctx is cancelled, the HTTP request is cancelled too
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// ...
}
sync Primitives
var mu sync.RWMutex
var cache = make(map[string]string)
func get(key string) (string, bool) {
mu.RLock()
defer mu.RUnlock()
v, ok := cache[key]
return v, ok
}
func set(key, value string) {
mu.Lock()
defer mu.Unlock()
cache[key] = value
}
HTTP Server Graceful Shutdown
srv := &http.Server{Addr: ":8080", Handler: router}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("server forced to shutdown:", err)
}
log.Println("server exiting")
Java Spring Boot 3
Bean Lifecycle and DI
// Constructor injection — preferred over field injection (testable, immutable)
@Service
@RequiredArgsConstructor // Lombok
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User createUser(CreateUserDto dto) {
var user = new User(dto.email(), passwordEncoder.encode(dto.password()));
return userRepository.save(user);
}
}
Spring Security Filter Chain (Spring Boot 3 / Spring Security 6)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable) // for stateless JWT APIs
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
N+1 Problem Solutions with JPA
// BAD: N+1 — loads orders lazily, triggers N queries for N orders
List<Order> orders = orderRepository.findAll();
orders.forEach(o -> o.getItems().size()); // LAZY load for each
// GOOD: JOIN FETCH in JPQL
@Query("SELECT o FROM Order o LEFT JOIN FETCH o.items WHERE o.userId = :userId")
List<Order> findByUserIdWithItems(@Param("userId") Long userId);
// GOOD: @EntityGraph (avoids JPQL)
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByUserId(Long userId);
@Transactional
@Service
public class TransferService {
@Transactional(rollbackFor = Exception.class)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepo.findById(fromId).orElseThrow();
Account to = accountRepo.findById(toId).orElseThrow();
from.debit(amount);
to.credit(amount);
accountRepo.saveAll(List.of(from, to));
// If any exception is thrown (including checked), transaction rolls back
}
}
Critical: @Transactional on private methods is silently ignored by Spring AOP proxy. Must be on public methods. Self-invocation (calling a @Transactional method from the same class) also bypasses the proxy — refactor into a separate bean.
Caching with Redis
Redis Data Structures
# Strings — simple key-value, counters
SET user:1:name "Alice" EX 3600 # set with 3600s TTL
INCR user:1:login_count # atomic increment
# Hashes — object representation
HSET user:1 name "Alice" email "alice@example.com"
HGET user:1 name
HGETALL user:1
# Lists — queues, activity feeds
LPUSH notifications:user:1 "New message" # prepend
LRANGE notifications:user:1 0 9 # get first 10
# Sorted Sets — leaderboards, rate limiting
ZADD leaderboard 1500 "alice"
ZREVRANGE leaderboard 0 9 WITHSCORES # top 10
# Streams — event log (like a lightweight Kafka topic)
XADD events * type "user.created" userId "123"
XREAD COUNT 10 STREAMS events 0
Cache-Aside Pattern
async function getUserById(id: string): Promise<User> {
const cacheKey = `user:${id}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
const user = await db.user.findUnique({ where: { id } });
if (!user) throw new NotFoundError(`User ${id} not found`);
await redis.setex(cacheKey, 3600, JSON.stringify(user)); // TTL: 1 hour
return user;
}
Cache Stampede Prevention
When a cached item expires and 1000 requests hit the DB simultaneously — this is a cache stampede (thundering herd).
Mutex approach (Redis SET NX):
async function getUserWithLock(id: string): Promise<User> {
const cacheKey = `user:${id}`;
const lockKey = `lock:user:${id}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Try to acquire lock — only one request proceeds
const acquired = await redis.set(lockKey, '1', 'NX', 'EX', 5);
if (!acquired) {
// Wait and retry — let lock holder populate cache
await sleep(100);
return getUserWithLock(id);
}
try {
const user = await db.user.findUnique({ where: { id } });
await redis.setex(cacheKey, 3600, JSON.stringify(user));
return user!;
} finally {
await redis.del(lockKey);
}
}
Probabilistic Early Expiry (XFetch algorithm): Re-cache before expiry with probability proportional to recomputation cost and time remaining — avoids synchronized stampedes without locking.
Background Jobs
BullMQ
import { Queue, Worker, QueueEvents } from 'bullmq';
import { connection } from './redis'; // ioredis connection
// Producer
const emailQueue = new Queue('emails', { connection });
await emailQueue.add('welcome-email', { userId, email }, {
attempts: 3,
backoff: { type: 'exponential', delay: 2000 },
removeOnComplete: 100, // keep last 100 completed jobs
removeOnFail: 500,
});
// Consumer
const worker = new Worker('emails', async (job) => {
const { userId, email } = job.data;
await sendEmail(email, 'Welcome!', renderTemplate(userId));
}, { connection, concurrency: 5 });
worker.on('failed', (job, err) => {
logger.error({ jobId: job?.id, err }, 'Email job failed');
});
Temporal Workflows
Temporal provides durable execution — if the worker crashes mid-workflow, Temporal replays history to resume from exactly where it left off.
// workflow.ts — pure, deterministic function
import { proxyActivities, sleep } from '@temporalio/workflow';
import type * as activities from './activities';
const { chargeCard, sendReceipt } = proxyActivities<typeof activities>({
startToCloseTimeout: '30 seconds',
retry: { maximumAttempts: 3 },
});
export async function orderWorkflow(orderId: string): Promise<void> {
await chargeCard(orderId);
await sleep('1 minute'); // durable sleep — no timer in memory
await sendReceipt(orderId);
}
Critical: Workflow code must be deterministic — no Math.random(), Date.now(), direct HTTP calls. Use activities for all side effects and non-deterministic operations.
Rate Limiting
Token Bucket (conceptual)
Each "bucket" refills at a constant rate (e.g., 100 tokens/minute). Each request consumes tokens. When the bucket is empty, requests are rejected. Allows bursting up to bucket capacity.
Redis Sliding Window with rate-limiter-flexible
import { RateLimiterRedis } from 'rate-limiter-flexible';
const rateLimiter = new RateLimiterRedis({
storeClient: redisClient,
keyPrefix: 'rl:api',
points: 100, // 100 requests
duration: 60, // per 60 seconds
blockDuration: 60, // block for 60s after limit exceeded
});
// Express middleware
app.use(async (req, res, next) => {
try {
const key = req.ip ?? 'anonymous';
await rateLimiter.consume(key);
next();
} catch (rejRes) {
res.set('Retry-After', String(Math.round(rejRes.msBeforeNext / 1000)));
res.status(429).json({ error: 'Too Many Requests' });
}
});
Circuit Breaker
The circuit breaker prevents cascading failures by stopping calls to a failing dependency.
States:
- Closed: requests flow normally; failures counted
- Open: requests fail immediately (fast-fail) without calling the dependency
- Half-Open: after timeout, limited requests allowed through to test recovery
import CircuitBreaker from 'opossum'; // npm package
const breaker = new CircuitBreaker(callExternalService, {
timeout: 3000, // 3s timeout = failure
errorThresholdPercentage: 50, // open if 50% of requests fail
resetTimeout: 30000, // try half-open after 30s
volumeThreshold: 10, // minimum calls before circuit can open
});
breaker.on('open', () => logger.warn('Circuit OPEN — calls failing fast'));
breaker.on('halfOpen', () => logger.info('Circuit HALF-OPEN — testing'));
breaker.on('close', () => logger.info('Circuit CLOSED — recovered'));
// In handler
const result = await breaker.fire(payload);
Connection Pooling
node-postgres (pg)
import { Pool } from 'pg';
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // maximum pool size — tune based on DB max_connections
idleTimeoutMillis: 30000, // close idle connections after 30s
connectionTimeoutMillis: 5000, // throw if can't connect in 5s
});
// ALWAYS use pool.query or pool.connect + client.release()
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
// For transactions
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query('UPDATE ...', [...]);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release(); // CRITICAL — failure to release leaks connections
}
Pool sizing formula: pool_size = (core_count * 2) + effective_spindle_count — from PgBouncer documentation. Start conservative and measure.
File Handling: Streaming Upload to S3
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import Busboy from 'busboy'; // multipart parser
// Stream multipart upload directly to S3 (never buffer to disk)
app.post('/upload', (req, res) => {
const busboy = Busboy({ headers: req.headers });
busboy.on('file', (fieldname, fileStream, info) => {
const { filename, mimeType } = info;
const key = `uploads/${Date.now()}-${filename}`;
// Pipe directly to S3 using PassThrough stream
const { PassThrough } = require('stream');
const passThrough = new PassThrough();
const upload = s3Client.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET!,
Key: key,
Body: passThrough,
ContentType: mimeType,
}));
fileStream.pipe(passThrough);
upload.then(() => res.json({ key })).catch((err) => next(err));
});
req.pipe(busboy);
});
// Presigned URL — for direct browser-to-S3 uploads (preferred for large files)
async function getPresignedUploadUrl(key: string): Promise<string> {
return getSignedUrl(s3Client, new PutObjectCommand({
Bucket: process.env.S3_BUCKET!,
Key: key,
}), { expiresIn: 300 }); // 5 minutes
}
Graceful Shutdown
// Node.js graceful shutdown pattern
const server = app.listen(PORT, () => logger.info(`Listening on ${PORT}`));
async function shutdown(signal: string) {
logger.info(`Received ${signal}, starting graceful shutdown`);
// Stop accepting new connections
server.close(async () => {
logger.info('HTTP server closed');
// Drain background jobs
await worker.close();
// Close DB connections
await pool.end();
// Close Redis
await redis.quit();
logger.info('Graceful shutdown complete');
process.exit(0);
});
// Force exit after timeout
setTimeout(() => {
logger.error('Forced shutdown after timeout');
process.exit(1);
}, 30000);
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
SQL Query Optimization
EXPLAIN ANALYZE
-- PostgreSQL: always run EXPLAIN (ANALYZE, BUFFERS) not just EXPLAIN
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id;
Look for:
- Seq Scan on large tables → needs index
- Nested Loop with large row estimates → index missing on join column
- Sort without index → add index on ORDER BY column
- Buffers: hit vs read → high read count means disk I/O; increase
shared_buffers
Index Strategies
-- Covering index: includes all columns needed by the query (avoids table heap access)
CREATE INDEX idx_orders_user_status ON orders (user_id, status) INCLUDE (total, created_at);
-- Partial index: indexes only a subset of rows (smaller, faster)
CREATE INDEX idx_users_active_email ON users (email) WHERE deleted_at IS NULL;
-- Expression index: for queries on computed values
CREATE INDEX idx_users_lower_email ON users (LOWER(email));
-- Now: WHERE LOWER(email) = 'alice@example.com' uses the index
VACUUM and ANALYZE
PostgreSQL uses MVCC — deleted/updated rows leave dead tuples. VACUUM reclaims space; ANALYZE updates planner statistics. Autovacuum handles this automatically but may fall behind on high-write tables.
-- Manual for tables with sudden large deletes/updates
VACUUM ANALYZE orders;
-- Check table bloat
SELECT relname, n_dead_tup, n_live_tup,
ROUND(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 2) AS dead_pct
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;
Authentication: JWT + Refresh Token Rotation
// Access token: short-lived (15 min), stateless
const accessToken = jwt.sign(
{ sub: userId, role: user.role },
process.env.JWT_ACCESS_SECRET!,
{ expiresIn: '15m', algorithm: 'RS256' }
);
// Refresh token: long-lived (7 days), stored in DB
const refreshToken = crypto.randomBytes(32).toString('hex');
await db.refreshToken.create({
data: {
token: hashToken(refreshToken), // store hashed, return plaintext
userId,
expiresAt: addDays(new Date(), 7),
},
});
// Rotation: invalidate old refresh token, issue new one (prevents reuse)
async function rotateRefreshToken(oldToken: string) {
const hashed = hashToken(oldToken);
const stored = await db.refreshToken.findUnique({ where: { token: hashed } });
if (!stored || stored.expiresAt < new Date()) {
throw new UnauthorizedError('Invalid or expired refresh token');
}
// Detect reuse: if token already used (not in DB), invalidate ALL tokens for user
await db.refreshToken.delete({ where: { token: hashed } });
return issueTokenPair(stored.userId);
}
httpOnly cookies vs Authorization header: Use httpOnly; Secure; SameSite=Strict cookies for browser clients — JavaScript cannot read them (XSS-safe). Use Authorization: Bearer for mobile/API clients where cookie handling is inconvenient.
Anti-Hallucination Protocol
Never assert without verification:
- Node.js event loop phase order is documented in the official Node.js docs at
nodejs.org/en/docs/guides/event-loop-timers-and-nexttick — verify there
- Fastify hook names are exactly
onRequest, preParsing, preValidation, preHandler, preSerialization, onSend, onResponse, onError, onClose — not "beforeHandler" or "afterResponse"
- Spring Boot 3 requires Spring Security 6 — the API for
HttpSecurity changed significantly from version 5 (no more and(), authorizeRequests() deprecated)
@Transactional on private methods: silently no-ops with Spring AOP proxy — this is a real, documented behavior, not speculation
- Redis commands: always verify with
redis.io/commands — SETEX exists, SETPX exists; EXPIRE accepts seconds not milliseconds
- Pydantic v2 broke v1 API significantly —
validator decorator replaced with field_validator, __root__ model removed, .dict() replaced with .model_dump()
- BullMQ is NOT Bull — it's a rewrite. Do not conflate their APIs. Bull uses
Queue.process(); BullMQ uses new Worker()
pool.end() in node-postgres gracefully drains connections; pool.destroy() does NOT exist in pg
- Go
context.WithTimeout returns BOTH the context AND a cancel function — always call defer cancel() regardless of timeout
EXPLAIN without ANALYZE does not execute the query — it only shows the planner's estimate. EXPLAIN ANALYZE actually runs it
Self-Review Checklist
Before delivering any backend solution, verify:
Source: ArdurAI/god-skill-suite — distributed by TomeVault.