Node.js Backend Mülakat Soruları: Kapsamlı Rehber 2026

Node.js backend mülakatlarında en sık sorulan 25 soru. Event loop, async/await, stream, kümeleme ve performans konuları detaylı cevaplarla açıklanmaktadır.

Node.js Backend Mülakat Soruları - Kapsamlı Rehber

Node.js backend mülakatları, çalışma zamanının iç mekanizmalarına hakimiyet, asenkron programlama kalıplarında ustalık ve performanslı uygulamalar tasarlayabilme becerisini değerlendirir. Bu rehber, temel kavramlardan ileri düzey üretim konseptlerine kadar en sık sorulan soruları kapsamaktadır.

Mülakat İpucu

İşe alım uzmanları, teoriyi pratik örneklerle birleştiren cevapları takdir eder. Her soruda kod veya somut bir kullanım senaryosuyla cevabı desteklemek, gerçek dünya deneyimini ortaya koyar.

Node.js Temelleri

Soru 1: Event Loop nedir ve nasıl çalışır?

Event Loop, Node.js'nin tek iş parçacığı üzerinde çalışmasına rağmen asenkron işlemleri engellemesiz (non-blocking) şekilde yönetmesini sağlayan temel mekanizmadır. JavaScript kodunun, callback'lerin ve olayların yürütülmesini koordine eder.

event-loop-demo.jsjavascript
// Demonstration of Event Loop execution order

console.log('1. Script start (synchronous)');

// setTimeout goes to the Timer Queue
setTimeout(() => {
  console.log('5. setTimeout callback (Timer Queue)');
}, 0);

// setImmediate goes to the Check Queue
setImmediate(() => {
  console.log('6. setImmediate callback (Check Queue)');
});

// Promise goes to the Microtask Queue (priority)
Promise.resolve().then(() => {
  console.log('3. Promise.then (Microtask Queue)');
});

// process.nextTick has the highest priority
process.nextTick(() => {
  console.log('2. process.nextTick (nextTick Queue)');
});

console.log('4. Script end (synchronous)');

// Output order: 1, 4, 2, 3, 5, 6

Event Loop, kuyrukları belirli bir sırayla işler: önce senkron kod, ardından nextTick, mikro görevler (Promise), zamanlayıcılar, I/O callback'leri, setImmediate ve son olarak kapatma callback'leri.

Soru 2: process.nextTick() ile setImmediate() arasındaki fark nedir?

Bu soru, Event Loop'taki yürütme önceliklerinin ayrıntılı kavranışını değerlendirir.

nextTick-vs-immediate.jsjavascript
// Behavior comparison

// process.nextTick executes BEFORE the next Event Loop phase
process.nextTick(() => {
  console.log('nextTick 1');
  process.nextTick(() => {
    console.log('nextTick 2 (nested)');
  });
});

// setImmediate executes in the Check phase of the Event Loop
setImmediate(() => {
  console.log('setImmediate 1');
  setImmediate(() => {
    console.log('setImmediate 2 (nested)');
  });
});

// Output: nextTick 1, nextTick 2, setImmediate 1, setImmediate 2

process.nextTick(), mevcut işlemden hemen sonra, Event Loop devam etmeden önce işlenir. Aşırı kullanım Event Loop'u bloke edebilir. setImmediate() daha öngörülebilirdir ve yürütmeyi ertelemek için önerilir.

Açlık Tehlikesi

process.nextTick() fonksiyonuna yapılan özyinelemeli çağrılar Event Loop'u aç bırakabilir ve I/O işlemlerinin gerçekleştirilmesini engelleyebilir. Kritik olmayan işlemler için setImmediate() tercih edilmelidir.

Soru 3: Node.js asenkron kodda hataları nasıl yönetir?

Asenkron hata yönetimi, senkron koddan temelden farklıdır. Uygun bir hata yönetimi olmadan, bir hata tüm uygulamayı çökertebilir.

error-handling.jsjavascript
// Asynchronous error handling patterns

// Pattern 1: Callbacks with error-first convention
function readFileCallback(path, callback) {
  const fs = require('fs');
  fs.readFile(path, 'utf8', (err, data) => {
    if (err) {
      // Error is ALWAYS the first argument
      return callback(err, null);
    }
    callback(null, data);
  });
}

// Pattern 2: Promises with catch
async function readFilePromise(path) {
  const fs = require('fs').promises;
  try {
    const data = await fs.readFile(path, 'utf8');
    return data;
  } catch (err) {
    // Centralized error handling
    console.error(`File read error: ${err.message}`);
    throw err; // Re-throw for propagation
  }
}

// Pattern 3: Global handling of unhandled rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection:', reason);
  // In production: log and graceful shutdown
});

// Pattern 4: Handling uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err);
  // CRITICAL: always terminate the process after
  process.exit(1);
});

Üretim ortamında her Promise'in bir .catch() bloğuna sahip olması veya try/catch içinde bulunması gerekir. Global handler'lar bir güvenlik ağı işlevi görür, birincil çözüm değildir.

Asenkron Programlama ve Eşzamanlılık

Soru 4: Node.js'de paralellik ile eşzamanlılık arasındaki farkı açıklayınız

Node.js varsayılan olarak eşzamanlıdır (concurrent) ancak paralel değildir. Bu ayrım, performansın anlaşılması için temel bir kavramdır.

concurrency-vs-parallelism.jsjavascript
// CONCURRENCY: multiple tasks progress by alternating (single-thread)
async function concurrentTasks() {
  console.time('concurrent');

  // These calls are concurrent, not parallel
  const results = await Promise.all([
    fetch('https://api.example.com/users'),    // Non-blocking I/O
    fetch('https://api.example.com/products'), // Non-blocking I/O
    fetch('https://api.example.com/orders'),   // Non-blocking I/O
  ]);

  console.timeEnd('concurrent'); // ~time of the longest request
  return results;
}

// PARALLELISM: with Worker Threads for CPU-bound tasks
const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
  // Main thread delegates CPU-intensive work
  async function parallelComputation() {
    console.time('parallel');

    const workers = [
      createWorker({ start: 0, end: 1000000 }),
      createWorker({ start: 1000000, end: 2000000 }),
      createWorker({ start: 2000000, end: 3000000 }),
    ];

    const results = await Promise.all(workers);
    console.timeEnd('parallel');
    return results.reduce((a, b) => a + b, 0);
  }

  function createWorker(data) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, { workerData: data });
      worker.on('message', resolve);
      worker.on('error', reject);
    });
  }
} else {
  // Code executed in the Worker Thread
  const { workerData } = require('worker_threads');
  let sum = 0;
  for (let i = workerData.start; i < workerData.end; i++) {
    sum += Math.sqrt(i); // CPU-intensive calculation
  }
  parentPort.postMessage(sum);
}

I/O'ya bağlı işlemler (ağ, dosya) için doğal eşzamanlılık yeterlidir. İşlemciye bağlı görevler (yoğun hesaplamalar, kriptografi) için Worker Thread'ler gerçek paralellik sağlar.

Soru 5: Cluster modülü nasıl çalışır?

Cluster modülü, aynı portu paylaşan birden fazla Node.js sürecinin oluşturulmasına olanak tanır ve böylece mevcut tüm CPU çekirdeklerinin kullanılmasını sağlar.

cluster-example.jsjavascript
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  // Fork one worker per CPU core
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // Handle crashing workers
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died (${signal || code})`);
    console.log('Starting a new worker...');
    cluster.fork(); // Automatic restart
  });

  // Inter-process communication
  cluster.on('message', (worker, message) => {
    console.log(`Message from worker ${worker.id}:`, message);
  });

} else {
  // Workers share the TCP port
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Handled by worker ${process.pid}\n`);

    // Send stats to primary
    process.send({ type: 'request', pid: process.pid });
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

Yük dengeleme, işletim sistemi tarafından otomatik olarak yönetilir (Linux/macOS'ta round-robin). Üretimde PM2, yerleşik küme moduyla bu yönetimi basitleştirir.

Node.js / NestJS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Stream'ler ve Buffer'lar

Soru 6: Klasik yöntemler yerine Stream ne zaman kullanılmalıdır?

Stream'ler, verilerin tamamını belleğe yüklemek yerine parçalar halinde işlenmesine olanak tanır. Büyük dosyalar ve akış senaryoları için vazgeçilmezdir.

streams-comparison.jsjavascript
const fs = require('fs');

// ❌ BAD: loads entire file into memory
async function readEntireFile(path) {
  const data = await fs.promises.readFile(path); // Blocks if file > RAM
  return processData(data);
}

// ✅ GOOD: chunk-based processing with Stream
function readWithStream(path) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    const readStream = fs.createReadStream(path, {
      highWaterMark: 64 * 1024, // 64KB per chunk
    });

    readStream.on('data', (chunk) => {
      // Progressive processing, constant memory
      chunks.push(processChunk(chunk));
    });

    readStream.on('end', () => resolve(chunks));
    readStream.on('error', reject);
  });
}

// ✅ BEST: pipeline for chaining transformations
const { pipeline } = require('stream/promises');
const zlib = require('zlib');

async function compressFile(input, output) {
  await pipeline(
    fs.createReadStream(input),   // Source
    zlib.createGzip(),            // Transform
    fs.createWriteStream(output)  // Destination
  );
  // Automatic error handling and backpressure management
}

Veri boyutu birkaç MB'ı aşabilecekse veya gerçek zamanlı işleme gerektiğinde (yüklemeler, loglar, ağ verileri) Stream'ler tercih edilmelidir.

Soru 7: Backpressure kavramını açıklayınız

Backpressure, veri üreticisinin tüketiciden daha hızlı olduğu durumlarda ortaya çıkar. Yönetilmezse bellek tükenir.

backpressure-demo.jsjavascript
const fs = require('fs');

// ❌ Problem: no backpressure handling
function badCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  readable.on('data', (chunk) => {
    // If write() returns false, the internal buffer is full
    // But here reading continues anyway → memory leak
    writable.write(chunk);
  });
}

// ✅ Solution: respect the writable signal
function goodCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  readable.on('data', (chunk) => {
    const canContinue = writable.write(chunk);

    if (!canContinue) {
      // Pause reading until buffer drains
      readable.pause();
    }
  });

  writable.on('drain', () => {
    // Buffer drained, resume reading
    readable.resume();
  });

  readable.on('end', () => writable.end());
}

// ✅ BEST: pipe() handles everything automatically
function bestCopy(src, dest) {
  const readable = fs.createReadStream(src);
  const writable = fs.createWriteStream(dest);

  // pipe() handles backpressure natively
  readable.pipe(writable);
}

pipe() veya pipeline() metodu backpressure'ı otomatik olarak yönetir. Karmaşık senaryolarda pause/resume mantığının manuel olarak uygulanması gerekir.

Performans ve Optimizasyon

Soru 8: Bellek sızıntıları nasıl tespit edilir ve giderilir?

Bellek sızıntıları Node.js'de sık karşılaşılan bir sorundur. Bunları tespit etme ve düzeltme becerisi üretim ortamlarında kritik öneme sahiptir.

memory-leak-patterns.jsjavascript
// ❌ Leak 1: closures that retain references
function createLeakyHandler() {
  const hugeData = Buffer.alloc(100 * 1024 * 1024); // 100MB

  return function handler(req, res) {
    // hugeData remains in memory as long as handler exists
    res.end('Hello');
  };
}

// ✅ Fix: limit the scope
function createSafeHandler() {
  return function handler(req, res) {
    // Data created and released on each request
    const data = fetchData();
    res.end(data);
  };
}

// ❌ Leak 2: event listeners not cleaned up
class LeakyClass {
  constructor() {
    // Added on each instantiation, never removed
    process.on('message', this.handleMessage);
  }
  handleMessage(msg) { /* ... */ }
}

// ✅ Fix: explicit cleanup
class SafeClass {
  constructor() {
    this.boundHandler = this.handleMessage.bind(this);
    process.on('message', this.boundHandler);
  }
  handleMessage(msg) { /* ... */ }

  destroy() {
    // Mandatory cleanup
    process.removeListener('message', this.boundHandler);
  }
}

// Diagnostics with native tools
function diagnoseMemory() {
  const used = process.memoryUsage();
  console.log({
    heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
    heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
    external: `${Math.round(used.external / 1024 / 1024)}MB`,
    rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
  });
}

// Enable manual garbage collector for testing
// node --expose-gc app.js
if (global.gc) {
  global.gc();
  diagnoseMemory();
}

Üretimde clinic.js, Chrome DevTools'un heap snapshot'ları veya DataDog, New Relic gibi APM (Application Performance Monitoring) çözümleri kullanılması önerilir.

Soru 9: Node.js API performansı nasıl optimize edilir?

Bu soru, birden fazla düzeyde optimizasyon tekniklerine hakimiyeti değerlendirir.

performance-optimization.jsjavascript
// 1. CACHING: reduce expensive calls
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5-minute TTL

async function getCachedUser(id) {
  const cacheKey = `user:${id}`;
  let user = cache.get(cacheKey);

  if (!user) {
    user = await db.users.findById(id);
    cache.set(cacheKey, user);
  }

  return user;
}

// 2. CONNECTION POOLING: reuse DB connections
const { Pool } = require('pg');
const pool = new Pool({
  max: 20,                // Max simultaneous connections
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// 3. COMPRESSION: reduce response size
const compression = require('compression');
app.use(compression({
  filter: (req, res) => {
    // Only compress if > 1KB
    return compression.filter(req, res);
  },
  threshold: 1024,
}));

// 4. BATCHING: group operations
async function batchInsert(items) {
  const BATCH_SIZE = 1000;

  for (let i = 0; i < items.length; i += BATCH_SIZE) {
    const batch = items.slice(i, i + BATCH_SIZE);
    await db.items.insertMany(batch);
  }
}

// 5. LAZY LOADING: load on demand
async function getUserWithPosts(userId, includePosts = false) {
  const user = await db.users.findById(userId);

  if (includePosts) {
    user.posts = await db.posts.findByUserId(userId);
  }

  return user;
}

Optimizasyonlar profilleme ile yönlendirilmelidir. Gerçek darboğazları tespit etmek için optimize etmeden önce ölçüm yapılması esastır.

80/20 Kuralı

Performans sorunlarının %80'i kodun %20'sinden kaynaklanır. Körü körüne optimize etmeye başlamadan önce bu kritik alanları profilleme ile tespit etmek gerekir.

Güvenlik

Soru 10: Bir Node.js API'si yaygın saldırılara karşı nasıl korunur?

Güvenlik, mülakatlarda sürekli gündeme gelen bir konudur. OWASP güvenlik açıklarına hakimiyet gösterilmesi beklenir.

security-best-practices.jsjavascript
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

const app = express();

// 1. SECURITY HEADERS with Helmet
app.use(helmet());

// 2. RATE LIMITING against brute-force attacks
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,                  // 100 requests per IP
  message: 'Too many requests, please try again later',
  standardHeaders: true,
  legacyHeaders: false,
});
app.use('/api/', limiter);

// 3. SANITIZATION against NoSQL injections
app.use(mongoSanitize());

// 4. XSS PROTECTION
app.use(xss());

// 5. STRICT INPUT VALIDATION
const { body, validationResult } = require('express-validator');

app.post('/api/users',
  [
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 8 }).escape(),
    body('name').trim().escape(),
  ],
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // Continue processing
  }
);

// 6. SQL INJECTION PROTECTION (with parameters)
async function safeQuery(userId) {
  // ✅ Parameterized query
  const result = await pool.query(
    'SELECT * FROM users WHERE id = $1',
    [userId]
  );
  return result.rows;
}

// ❌ NEVER string concatenation
async function unsafeQuery(userId) {
  // Vulnerable to SQL injection
  const result = await pool.query(
    `SELECT * FROM users WHERE id = ${userId}`
  );
}

Üretimde ayrıca kısıtlayıcı CORS, zorunlu HTTPS, güvenlik loglama, gizli anahtar rotasyonu ve düzenli bağımlılık denetimleri (npm audit) eklenmelidir.

Mimari ve Tasarım Kalıpları

Soru 11: Node.js'de Repository kalıbını açıklayınız

Repository kalıbı, veri erişimini soyutlar ve test edilebilirliği ile bakım kolaylığını artırır.

repository-pattern.jsjavascript
// Abstract interface (for TypeScript, or documentation)
class UserRepository {
  async findById(id) { throw new Error('Not implemented'); }
  async findByEmail(email) { throw new Error('Not implemented'); }
  async create(userData) { throw new Error('Not implemented'); }
  async update(id, userData) { throw new Error('Not implemented'); }
  async delete(id) { throw new Error('Not implemented'); }
}

// Concrete implementation with Prisma
class PrismaUserRepository extends UserRepository {
  constructor(prisma) {
    super();
    this.prisma = prisma;
  }

  async findById(id) {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async findByEmail(email) {
    return this.prisma.user.findUnique({ where: { email } });
  }

  async create(userData) {
    return this.prisma.user.create({ data: userData });
  }

  async update(id, userData) {
    return this.prisma.user.update({
      where: { id },
      data: userData,
    });
  }

  async delete(id) {
    return this.prisma.user.delete({ where: { id } });
  }
}

// Implementation for testing
class InMemoryUserRepository extends UserRepository {
  constructor() {
    super();
    this.users = new Map();
    this.idCounter = 1;
  }

  async findById(id) {
    return this.users.get(id) || null;
  }

  async create(userData) {
    const user = { id: this.idCounter++, ...userData };
    this.users.set(user.id, user);
    return user;
  }
  // ... other methods
}

// Service using the repository (dependency injection)
class UserService {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async getUser(id) {
    const user = await this.userRepository.findById(id);
    if (!user) throw new Error('User not found');
    return user;
  }
}

Bu kalıp, iş mantığını değiştirmeden kalıcılık katmanının uygulamasını değiştirmeye olanak tanır.

Soru 12: Bir iş kuyruğu sistemi nasıl uygulanır?

Kuyruklar, ağır görevlerin ertelenmesine ve güvenilir bir şekilde yürütülmesine olanak sağlar.

job-queue.jsjavascript
const Queue = require('bull');

// Create queue with Redis as backend
const emailQueue = new Queue('email', {
  redis: {
    host: 'localhost',
    port: 6379,
  },
  defaultJobOptions: {
    attempts: 3,          // Number of attempts
    backoff: {
      type: 'exponential',
      delay: 2000,        // Initial delay between attempts
    },
    removeOnComplete: 100, // Keep last 100 completed jobs
  },
});

// Producer: add jobs to the queue
async function sendWelcomeEmail(userId, email) {
  await emailQueue.add('welcome', {
    userId,
    email,
    template: 'welcome',
  }, {
    priority: 1,          // High priority
    delay: 5000,          // 5-second delay
  });
}

// Consumer: process jobs
emailQueue.process('welcome', async (job) => {
  const { userId, email, template } = job.data;

  // Update progress
  job.progress(10);

  const html = await renderTemplate(template, { userId });
  job.progress(50);

  await sendEmail(email, 'Welcome!', html);
  job.progress(100);

  return { sent: true, email };
});

// Event handling
emailQueue.on('completed', (job, result) => {
  console.log(`Job ${job.id} completed:`, result);
});

emailQueue.on('failed', (job, err) => {
  console.error(`Job ${job.id} failed:`, err.message);
});

// Recurring jobs (cron)
emailQueue.add('newsletter', { type: 'weekly' }, {
  repeat: {
    cron: '0 9 * * MON', // Every Monday at 9am
  },
});

Redis ile Bull en popüler çözümdür. Daha basit ihtiyaçlar için agenda veya bee-queue hafif alternatifler sunar.

İleri Düzey Sorular

Soru 13: Yerel N-API modülü nasıl çalışır?

N-API, Node.js sürümleri arasında kararlı bir API ile C/C++ dilinde yerel modüller oluşturmaya olanak tanır.

native-module.cppcpp
// Native module for CPU-intensive calculations

#include <napi.h>

// Synchronous function exposed to JavaScript
Napi::Number Fibonacci(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  // Argument validation
  if (info.Length() < 1 || !info[0].IsNumber()) {
    Napi::TypeError::New(env, "Number expected")
      .ThrowAsJavaScriptException();
    return Napi::Number::New(env, 0);
  }

  int n = info[0].As<Napi::Number>().Int32Value();

  // Iterative Fibonacci calculation
  long long a = 0, b = 1;
  for (int i = 0; i < n; i++) {
    long long temp = a + b;
    a = b;
    b = temp;
  }

  return Napi::Number::New(env, static_cast<double>(a));
}

// Module initialization
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(
    Napi::String::New(env, "fibonacci"),
    Napi::Function::New(env, Fibonacci)
  );
  return exports;
}

NODE_API_MODULE(native_module, Init)
javascript
// Usage from JavaScript
const native = require('./build/Release/native_module');

// 10x faster than JavaScript equivalent
const result = native.fibonacci(50);

Yerel modüller yoğun hesaplamalar, mevcut C/C++ kütüphanelerinin entegrasyonu veya sistem API'lerine erişim için kullanışlıdır.

Soru 14: V8 Garbage Collector'ı açıklayınız

GC'nin anlaşılması, duraklamaları ve bellek tüketimini minimize eden kod yazmaya yardımcı olur.

gc-optimization.jsjavascript
// V8 GC uses two spaces: Young and Old Generation

// 1. Young Generation: short-lived objects
function shortLivedObjects() {
  for (let i = 0; i < 1000; i++) {
    const temp = { data: i }; // Allocated then collected quickly
  }
  // Minor GC (Scavenge) very fast
}

// 2. Old Generation: objects that survive multiple GCs
const cache = new Map(); // Survives, promoted to Old Generation

// ❌ Problematic pattern: many promoted objects
function createManyLongLived() {
  const objects = [];
  for (let i = 0; i < 100000; i++) {
    objects.push({ id: i, data: new Array(100).fill(0) });
  }
  return objects; // All promoted to Old Gen = slow major GC
}

// ✅ Optimized pattern: object reuse
class ObjectPool {
  constructor(factory, size = 100) {
    this.pool = Array.from({ length: size }, factory);
    this.available = [...this.pool];
  }

  acquire() {
    return this.available.pop() || this.pool[0];
  }

  release(obj) {
    // Reset and return to pool
    Object.keys(obj).forEach(k => obj[k] = null);
    this.available.push(obj);
  }
}

// GC monitoring
const v8 = require('v8');

function getHeapStats() {
  const stats = v8.getHeapStatistics();
  return {
    totalHeap: `${Math.round(stats.total_heap_size / 1024 / 1024)}MB`,
    usedHeap: `${Math.round(stats.used_heap_size / 1024 / 1024)}MB`,
    heapLimit: `${Math.round(stats.heap_size_limit / 1024 / 1024)}MB`,
  };
}

--max-old-space-size bayrağı, yoğun bellek kullanan uygulamalar için Old Generation limitini artırmaya olanak tanır.

Soru 15: Graceful shutdown nasıl uygulanır?

Graceful shutdown, sunucu durdurulmadan önce devam eden isteklerin tamamlanmasına ve bağlantıların düzgün şekilde kapatılmasına olanak tanır.

graceful-shutdown.jsjavascript
const http = require('http');

const server = http.createServer((req, res) => {
  // Simulate a long request
  setTimeout(() => {
    res.writeHead(200);
    res.end('Done');
  }, 2000);
});

// Tracking active connections
let connections = new Set();

server.on('connection', (conn) => {
  connections.add(conn);
  conn.on('close', () => connections.delete(conn));
});

// Graceful shutdown function
async function shutdown(signal) {
  console.log(`${signal} received, starting graceful shutdown...`);

  // 1. Stop accepting new connections
  server.close(() => {
    console.log('HTTP server closed');
  });

  // 2. Close idle connections
  for (const conn of connections) {
    conn.end();
  }

  // 3. Close DB connections, queues, etc.
  await Promise.all([
    database.disconnect(),
    redisClient.quit(),
    messageQueue.close(),
  ]);

  // 4. Safety timeout
  setTimeout(() => {
    console.error('Forced shutdown after timeout');
    process.exit(1);
  }, 30000);

  console.log('Graceful shutdown completed');
  process.exit(0);
}

// Listen for termination signals
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

// Start server
server.listen(3000, () => {
  console.log('Server running on port 3000');
});

Konteyner ortamlarında (Docker, Kubernetes) graceful shutdown, kesintisiz dağıtımlar için kritik öneme sahiptir.

Node.js / NestJS mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Davranışsal Sorular

Soru 16: Çözdüğünüz bir performans problemini anlatınız

Bu soru pratik deneyimi değerlendirir. Cevap, STAR formatında (Durum, Görev, Eylem, Sonuç) yapılandırılmalıdır.

Yapılandırılmış cevap örneği:

text
Situation: A reporting API was timing out on requests
            exceeding 100,000 records.

Task:       Reduce response time from 45s to under 5s.

Action:
1. Profiling with clinic.js → identified JSON serialization as bottleneck
2. Implemented streaming with Transform streams
3. Database-side pagination
4. Added Redis caching for frequent queries

Result:     Response time reduced to 2s, memory usage decreased by 10x.

Soru 17: Bağımlılıklar ve güncellemeleri nasıl yönetilir?

package.json - Versioning best practicesjavascript
{
  "dependencies": {
    // ✅ Exact versions for production
    "express": "4.18.2",

    // ✅ Caret for compatible minor updates
    "lodash": "^4.17.21",

    // ❌ Avoid latest or *
    // "some-lib": "*"
  },
  "devDependencies": {
    // Quality tools
    "npm-check-updates": "^16.0.0"
  },
  "scripts": {
    // Vulnerability check
    "audit": "npm audit --audit-level=moderate",

    // Interactive update
    "update:check": "ncu",
    "update:apply": "ncu -u && npm install"
  },
  "engines": {
    // Specify required Node.js version
    "node": ">=20.0.0"
  }
}

package-lock.json kullanımı, Dependabot veya Renovate ile otomasyon ve her büyük güncelleme öncesinde regresyon testlerinin uygulanması da belirtilmelidir.

Sonuç

Node.js backend mülakatları hem iç mekanizmaların teorik kavranışını hem de pratik üretim sorunlarını çözme becerisini değerlendirir. Event Loop, asenkron kalıplar ve optimizasyon tekniklerinde ustalık, kıdemli backend geliştirici pozisyonları için beklenen temel yetkinlikleri oluşturur.

Hazırlık Kontrol Listesi

  • ✅ Event Loop işleyişi ve fazlarının anlaşılması
  • ✅ Callback, Promise ve async/await arasındaki farkların kavranması
  • ✅ Asenkron hata yönetimi kalıplarının bilinmesi
  • ✅ Stream ve klasik yöntemler arasında doğru seçimin yapılması
  • ✅ Bellek sızıntılarının tespiti ve giderilmesi
  • ✅ OWASP güvenlik en iyi uygulamalarının uygulanması
  • ✅ Kümeleme ve graceful shutdown uygulaması
  • ✅ Profilleme araçlarının kullanımı (clinic.js, Chrome DevTools)

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Teknik hazırlık, pratik projelerle desteklenmelidir. Üretim düzeyinde bir API geliştirmek, Node.js açık kaynak projelerine katkıda bulunmak veya LeetCode gibi platformlardaki zorlukları çözmek, bu bilgilerin pekiştirilmesine yardımcı olur.

Etiketler

#nodejs
#interview
#backend
#javascript
#technical interview

Paylaş

İlgili makaleler