package.json Dosyası: Kapsamlı Bağımlılık Yönetimi Rehberi

Node.js projelerinde çalışırken en sık karşılaştığın dosyalardan biri olan package.json, aslında projenin kimlik kartı ve bağımlılık haritası gibi çalışır. Yeni bir projeye dahil olduğunda, bir sunucuya uygulama deploy ettiğinde ya da ekip arkadaşının kodunu klonladığında ilk baktığın yer burasıdır. Ama çoğu geliştirici ve sysadmin bu dosyayı yüzeysel okur, gerçek gücünü fark etmez. Bu yazıda package.json dosyasını derinlemesine inceleyeceğiz, production ortamlarında nasıl yönetilmesi gerektiğini ele alacağız ve sık yapılan hataların üstünden geçeceğiz.

package.json Nedir ve Neden Bu Kadar Önemlidir

package.json, Node.js ekosisteminde projenin merkez dosyasıdır. İçinde projenin adı, versiyonu, bağımlılıkları, çalıştırılabilir scriptler ve onlarca metadata bilgisi bulunur. npm ya da yarn gibi paket yöneticileri bu dosyayı okuyarak hangi kütüphanelerin kurulacağına, hangi Node.js versiyonunun gerektiğine ve projenin nasıl çalıştırılacağına karar verir.

Sysadmin perspektifinden bakıldığında bu dosya birkaç kritik rolü üstlenir. Birincisi, tekrar üretilebilir ortamlar yaratmak için temel referans noktasıdır. İkincisi, CI/CD pipeline’larında hangi adımların çalıştırılacağını belirler. Üçüncüsü, production ve development ortamları arasındaki bağımlılık farkını yönetir. Bu farkı yanlış anlamak, production sunucusuna gereksiz yüzlerce MB paket kurulmasına ya da tam tersine eksik bağımlılık yüzünden uygulamanın çökmesine neden olabilir.

Sıfırdan package.json Oluşturmak

Yeni bir Node.js projesi başlatırken iki yol var. Birincisi interaktif mod:

mkdir my-app && cd my-app
npm init

Bu komut sana sırasıyla proje adı, versiyon, açıklama, entry point, test komutu, git repository ve lisans bilgilerini sorar. Her soruyu yanıtlayarak dosyayı oluşturabilirsin. Ama sunucularda script çalıştırırken ya da otomasyonda bunu kullanmak pratik değil. Bunun yerine:

npm init -y

Bu komut tüm sorulara varsayılan cevap vererek anında package.json dosyasını oluşturur. CI/CD scriptlerinde, Docker build süreçlerinde ve otomatik proje iskelet oluşturucularında hep bu yöntemi kullanırsın.

Oluşan temel dosya şuna benzer görünür:

cat package.json

Dosyanın içeriğinde name, version, description, main, scripts, keywords, author, license alanlarını göreceksin. Bunların her biri birer anlam taşır ama şimdi en kritik olanlara odaklanalım.

Bağımlılık Türleri: dependencies vs devDependencies

Bu ayrım production ortamlarında hayat kurtarır. Pek çok geliştirici her şeyi aynı kategoriye koyar ve sonuçta production sunucusunda Jest, ESLint, Webpack gibi sadece geliştirme sürecinde kullanılan araçlar da kurulu olur. Bu hem disk alanı israfıdır hem de potansiyel güvenlik açığıdır.

dependencies: Uygulamanın çalışması için production ortamında da gerekli olan paketler buraya girer. Express, Axios, Mongoose, dotenv gibi şeyler düşün.

devDependencies: Sadece geliştirme sürecinde kullanılan araçlar. Test frameworkleri, linter’lar, transpiler’lar, bundler’lar bu kategoriye girer.

Paket kurarken doğru kategoriye koymak için:

# Production bağımlılığı eklemek
npm install express
npm install express --save  # Eski npm versiyonlarında gerekiyordu, artık varsayılan

# Development bağımlılığı eklemek
npm install jest --save-dev
npm install eslint --save-dev
npm install nodemon --save-dev

Production sunucusuna deploy ederken sadece production bağımlılıklarını kurmak için:

npm install --production
# veya
NODE_ENV=production npm install

Bu sayede node_modules klasörü çok daha küçük olur, kurulum hızlanır ve gereksiz araçlar sisteme girmemiş olur. Bir e-ticaret projesinde bu yöntemi uyguladığımızda node_modules boyutu 850 MB’dan 120 MB’a düşmüştü. Docker image boyutu da buna paralel küçülünce deploy süreleri yarı yarıya indi.

peerDependencies ve optionalDependencies

Bu iki kategori daha az bilinir ama özellikle kütüphane geliştiriyorsan veya karmaşık bir ekosistemde çalışıyorsan önemlidir.

peerDependencies: Kütüphanenin çalışması için kullanıcının projesinde belirli bir paketin belirli bir versiyonunun kurulu olmasını zorunlu kılar. Örneğin bir React component kütüphanesi yazıyorsun ve bu kütüphanenin React 18 ile çalışması gerekiyor. Kütüphanenin kendi node_modules‘ına React eklemek yerine, kullanıcının projesinde React olmasını beklersin.

optionalDependencies: Kurulumu başarısız olsa bile npm install işleminin devam etmesini sağlar. Platform’a özel native modüller için kullanışlıdır. Örneğin bir paket hem Linux hem Windows’ta çalışıyor ama Linux’ta daha hızlı bir native modül kullanıyorsa bunu optional olarak işaretler.

Versiyon Numaraları ve Semver

Bağımlılık yönetiminin en kritik parçası versiyon kontrolüdür. npm Semantic Versioning (semver) kullanır: MAJOR.MINOR.PATCH formatında.

Patch (1.0.X): Geriye dönük uyumlu hata düzeltmeleri. Minor (1.X.0): Geriye dönük uyumlu yeni özellikler. Major (X.0.0): Geriye dönük uyumsuz değişiklikler, breaking changes.

package.json içindeki versiyon ifadelerindeki sembolleri anlamak çok önemli:

^1.2.3 (caret): Minor ve patch güncellemelerine izin verir. 1.x.x aralığındaki en son versiyonu alır ama 2.0.0’a geçmez. npm’in varsayılan davranışı budur.

~1.2.3 (tilde): Sadece patch güncellemelerine izin verir. 1.2.x aralığındaki en son versiyonu alır.

1.2.3 (sabit): Tam olarak bu versiyon kurulur. En güvenli ama en az esnek seçenek.

* veya latest: Her zaman en son versiyon. Production’da kullanma, ciddi sorunlara yol açar.

Production ortamları için önerim: Kritik altyapı paketlerini sabit versiyonla kilitle, geri kalanlar için ~ kullan. ^ biraz riskli çünkü minor güncellemeler zaman zaman beklenmedik davranış değişikliklerine neden olabiliyor.

package-lock.json: Neden Önemlidir ve Commit’e Eklemeli misin

package-lock.json, npm’in npm install çalıştırdığında oluşturduğu ve tüm bağımlılık ağacını tam versiyon numaralarıyla kilitleyen dosyadır. Bu dosya sayesinde sen hangi makineye gidersen git, aynı paket versiyonları kurulur.

Sık sorulan soru şu: Bu dosyayı Git’e eklemeli miyiz? Evet, kesinlikle eklemeli. Özellikle uygulama projeleri için bu dosyayı .gitignore‘a koymak büyük hata. Sebebi basit: Ekipten biri npm install çalıştırdığında farklı patch versiyonları gelebilir ve “bende çalışıyor, sende neden çalışmıyor” soruları başlar.

Kütüphane geliştiriyorsan durum biraz farklı. Kütüphane paketlenip npm’e yüklendiğinde package-lock.json dahil edilmez, bu yüzden kütüphane geliştirme projelerinde bu dosyayı commit’e eklememek de makul bir yaklaşım.

package-lock.json‘ı atlayarak kesinlikle tutarlı kurulum yapmak istiyorsan:

npm ci

npm ci komutu npm install‘dan farklı çalışır. node_modules‘ı tamamen siler, package-lock.json‘ı referans alır ve içindeki versiyonları harfi harfine kurar. package.json ile package-lock.json arasında uyumsuzluk varsa hata verir. CI/CD pipeline’larında ve production deploy scriptlerinde her zaman npm ci kullan.

# CI/CD pipeline örneği
#!/bin/bash
set -e

echo "Bağımlılıklar kuruluyor..."
npm ci --production

echo "Uygulama build ediliyor..."
npm run build

echo "Servis yeniden başlatılıyor..."
pm2 restart app

Scripts Alanı: Otomasyon Merkezi

scripts alanı package.json‘ın en güçlü özelliklerinden biridir. Tekrarlayan komutları tanımlayıp npm run ile çalıştırabilirsin.

# Tanımlı bir script çalıştırmak
npm run build
npm run test
npm run lint

# Özel lifecycle scriptleri (run gerekmez)
npm start
npm test

Gerçek bir production projesinde scripts alanı şöyle görünebilir:

# package.json scripts alanındaki komutları görmek için
npm run

# Bu komut mevcut tüm scriptleri listeler

Scripts içinde birden fazla komutu zincirleme:

  • && kullanırsan: İlk komut başarılı olursa ikincisi çalışır.
  • || kullanırsan: İlk komut başarısız olursa ikincisi çalışır.
  • ; kullanırsan: Her koşulda ikinci komut da çalışır.

Pre ve post hook’lar da çok işe yarar. prebuild adında bir script tanımlarsan npm run build çalıştırıldığında önce prebuild otomatik tetiklenir. Aynı şekilde postbuild de build tamamlandıktan sonra çalışır. Bu özelliği veritabanı migration’larından önce validation çalıştırmak, build sonrası dosyaları belirli bir konuma kopyalamak gibi işler için kullanabilirsin.

Bağımlılıkları Güncelleme ve Denetleme

Zamanla bağımlılıklar eskir, güvenlik açıkları çıkar. Bu süreci yönetmek için birkaç temel komut bilmek gerek:

# Hangi paketlerin güncel olmadığını görmek
npm outdated

# Belirli bir paketi güncellemek
npm update express

# Tüm bağımlılıkları minor/patch bazında güncellemek
npm update

# Major versiyon atlamaları dahil güncellemek için
npx npm-check-updates -u
npm install

npm outdated komutu sana mevcut versiyon, istenen versiyon ve en son mevcut versiyonu gösterir. Sarı renkte gösterilenler package.json kısıtlamaları içinde güncellenebilir, kırmızılar ise major versiyon atlaması gerektirir.

Güvenlik açıklarını kontrol etmek ve yamak için:

# Güvenlik açığı taraması
npm audit

# Otomatik düzeltme (dikkatli kullan, bazen breaking change getirebilir)
npm audit fix

# Force fix (major versiyon atlamalarına da izin verir, test et)
npm audit fix --force

npm audit‘i CI/CD pipeline’ına eklemek iyi bir pratik. Yüksek ya da kritik seviyeli açık bulunan bir build’in production’a geçmesini engelleyebilirsin:

#!/bin/bash
# Sadece yüksek ve kritik açıkları kontrol et
npm audit --audit-level=high
if [ $? -ne 0 ]; then
    echo "Kritik güvenlik açığı bulundu, deploy iptal edildi!"
    exit 1
fi

engines Alanı: Node.js Versiyon Kısıtlaması

Ekibinde farklı insanlar farklı Node.js versiyonları kullanıyor olabilir. Ya da deploy ettiğin sunucuda eski bir Node.js versiyonu var. Bu uyumsuzlukları yakalamak için engines alanını kullan:

# package.json engines alanını kontrol etmek
node --version
npm --version

# .nvmrc dosyası varsa doğru versiyona geçmek
nvm use

engines alanında belirttiğin versiyonu zorlamak için .npmrc dosyasına engine-strict=true ekleyebilirsin. Bu sayede yanlış Node.js versiyonuyla npm install çalıştırmaya çalışan biri hata alır.

Birden fazla sunucu ya da geliştirici makinesi yönetiyorsan nvm (Node Version Manager) kullanmanı şiddetle tavsiye ederim. Projenin kökünde .nvmrc dosyası bırakırsın, herkes nvm use diyerek doğru versiyona geçer.

private Alanı ve Güvenlik

"private": true ayarı küçük ama kritik bir güvenlik önlemidir. Bunu eklemek, yanlışlıkla npm publish çalıştırdığında projenin npm registry’ye yüklenmesini engeller. Internal araçlar, uygulama projeleri, monorepo’lar gibi npm’e yayınlamak istemediğin her şeyde bu alanı true yap.

Ayrıca package.json içinde hassas bilgi bırakmamaya dikkat et. API anahtarları, database bağlantı stringleri gibi şeyler buraya asla girmemeli. Bunlar environment variable’lara ya da ayrı config dosyalarına gitmeli. scripts alanındaki komutlara da dikkat et; zaman zaman geliştiriciler test sırasında kullandıkları credential’ları script’e gömmüş ve bunu commit’lemiş oluyor.

workspaces: Monorepo Yönetimi

Birden fazla ilişkili paketi tek bir repository’de yönetmek istiyorsan workspaces özelliği hayatını kolaylaştırır. Bu özellik npm 7 ile geldi ve yarn’ın uzun süredir sunduğu bir yeteneği artık native npm’e taşıdı.

# Monorepo kökünde tüm workspace'lerin bağımlılıklarını kurmak
npm install

# Belirli bir workspace'te komut çalıştırmak
npm run build --workspace=packages/api
npm run test --workspace=packages/ui

# Tüm workspace'lerde aynı komutu çalıştırmak
npm run build --workspaces

Büyük ekiplerde mikroservis mimarisi ya da shared component kütüphanesi olan projelerde monorepo yaklaşımı çok işe yarar. Her servis kendi package.json‘ına sahip olur ama tek bir node_modules havuzunu paylaşırlar.

Gerçek Dünya Senaryosu: Bozuk Bağımlılık Durumu

Production’da karşılaşılan en sinir bozucu durumlardan biri “bende çalışıyor ama sunucuda çalışmıyor” sendromudur. Çoğunlukla bunun sebebi bağımlılık tutarsızlığıdır.

Senaryo: Bir Node.js API’ı local’de mükemmel çalışıyor, sunucuya push edince 502 hatası veriyor. Log’lara bakıyorsun, “Cannot find module ‘some-package'” hatası var.

Neden olur? Geliştirici paketi kurmuş ama package.json‘a kaydetmemiş. Local node_modules‘da var çünkü kurulmuş, ama package.json‘da yok. Sunucuda npm ci çalışınca o paket kurulmamış.

Çözüm ve önlem:

# Mevcut node_modules ile package.json arasındaki farkı kontrol et
npm ls

# Eksik ya da fazla bağımlılıkları bul
npm prune  # package.json'da olmayan paketleri kaldırır

# Temiz kurulum
rm -rf node_modules package-lock.json
npm install

# Sonra kontrol et, her şey düzgünse commit'le
git add package.json package-lock.json
git commit -m "fix: eksik bağımlılıklar package.json'a eklendi"

Bunu önlemek için ekibine şunu alışkanlık haline getirdir: Paket kurarken asla npm install paket-adi yazıp bırakma, hep --save ya da --save-dev ekle (npm 5+ için otomatik ama eski projelerde hala gerekebilir). Ve her yeni paket kurulumundan sonra package.json ve package-lock.json birlikte commit’e gitmeli.

Sonuç

package.json basit bir config dosyası gibi görünse de arkasında proje yönetiminin, bağımlılık güvenliğinin ve deployment otomasyonunun büyük bir kısmı yatıyor. Sysadmin olarak bu dosyayı iyi anlamak sana şu avantajları sağlar: Production sunucularına gereksiz paket kurmaktan kaçınırsın, güvenlik açıklarını CI/CD aşamasında yakalar ve durdurursun, “bende çalışıyor” sendromunun önüne geçersin.

Pratik olarak yapabileceğin birkaç şey şu: Mevcut projelerde npm audit çalıştır ve sonuçlara bak. package-lock.json‘ın git’te takip edilip edilmediğini kontrol et. CI/CD pipeline’larında npm install yerine npm ci kullanıyor musunuz kontrol et. Ve production deploy’larında --production flag’ini eklediğinizden emin ol.

Bağımlılık yönetimi es geçilebilecek bir detay gibi görünüyor ama ihmal edildiğinde production’da ciddi sorunlara kapı aralıyor. Biraz dikkat ve iyi pratiklerle bu risklerin büyük çoğunluğunu baştan önleyebilirsin.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir