a.
mcp.altay.socialMCP & Prompt Katalogu
Rules
Öne çıkan·v1.0.0·2026-04-22

Veritabanı Kuralları

İlişkisel veritabanı (PostgreSQL öncelikli) tasarım ve kullanım kuralları — schema, naming, indexing, migration, query pattern, transaction, performance, backup. CLAUDE.md / .cursorrules için.

databasepostgressqlmigrationrulesbackendclaude-md

İçerik

Bu içeriği projenin CLAUDE.md / AGENTS.md / .cursorrules dosyasına yapıştır.


Veritabanı Kuralları (PostgreSQL odaklı)

Naming

  • Tablolar: snake_case, tekil (user, order, invoice) — veya takım standardı çoğul. Proje içinde tutarlı.
  • Kolonlar: snake_casecreated_at, email_verified_at, is_active.
  • Primary key: id (her tablo için).
  • Foreign key: <table>_iduser_id, order_id.
  • Timestamp: created_at, updated_at, deleted_at (soft delete varsa). Her zaman TIMESTAMPTZ (timezone-aware).
  • Boolean: is_*, has_*, can_* prefix.
  • Index: <table>_<cols>_<type>_idxorders_user_id_status_idx.
  • Constraint: <table>_<rule>_checkorders_total_positive_check.

Schema

  • Normalize et, sonra gerekliyse denormalize (performans gerekçesiyle).
  • NOT NULL default: kolonu eklerken "gerçekten nullable mı" sor. Nullable kolonlar bug mıknatısıdır.
  • Default value'lar explicit — fallback davranışını schema söyler.
  • Enum: PostgreSQL CREATE TYPE AS ENUM veya CHECK constraint'li text. Evolve etmek istiyorsan text + CHECK tercih.
  • JSON/JSONB: structured data için değil, semi-structured için. JSONB her zaman (binary, indexable).
  • UUID vs bigint PK: çoğu projede UUID (v7 time-sortable tercih). Bigint her zaman büyük yazma yüklü, merkezi sistemler için.

Timestamp

created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
  • Her tabloda created_at, updated_at.
  • Trigger ile updated_at auto-update veya app seviyesinde.

Soft delete

deleted_at TIMESTAMPTZ
  • Hard delete = gerçek DELETE. Soft delete = deleted_at set. Projede biri seçilir, karıştırma.
  • Query'lerde WHERE deleted_at IS NULL — view ile sarmak tercih.

Index'leme

  • Query pattern'inden önce index yazma — over-indexing yazma performansını vurur.
  • WHERE / JOIN / ORDER BY kolonları aday.
  • Composite index sırası: equality önce, range sonra. (status, created_at)(created_at, status).
  • Partial index selective condition'lar için:
    CREATE INDEX orders_pending_idx ON orders(created_at) WHERE status = 'pending';
    
  • Unique: business rule'u (email unique, slug unique) DB ile enforce et, app'te değil.
  • Foreign key'e index zorunlu (Postgres otomatik değil) — JOIN performansı.
  • BRIN büyük time-series için kompakt alternative.
  • Full-text: tsvector + GIN index.

Review: pg_stat_user_indexes ile kullanılmayan index'leri tespit et, kaldır (yazma pahası).

Foreign key

  • Her ilişki FK ile enforce edilir:
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE
    
  • ON DELETE davranışı bilinçli: CASCADE, SET NULL, RESTRICT, NO ACTION. Default NO ACTION (implicit reject).
  • Cycle dikkat — A references B, B references A = migration acısı.

Transactions

  • DEFAULT: her write operation transaction içinde. ORM genelde halleder.
  • Transaction süresi kısa — uzun transaction = lock uzun = outage riski.
  • Dış HTTP çağrısı transaction içinde yasak. Lock süresi blast radius'u büyütür.
  • Isolation level:
    • READ COMMITTED (Postgres default) çoğu projeye yeter.
    • REPEATABLE READ / SERIALIZABLE spesifik ihtiyaçlarda — retry logic'i gerektirir.
  • SELECT ... FOR UPDATE kilit gerekiyorsa. Kullandıktan sonra deadlock potansiyelini düşün.

Query pattern

  • SELECT * yasak prod kodda — gereksiz kolon transfer, schema değiştiğinde kırılma.
  • Explicit column list:
    SELECT id, email, created_at FROM users WHERE ...
    
  • Parametreli query her zaman:
    SELECT * FROM users WHERE id = $1
    
    String concat yasak.
  • LIMIT her list query'de — unbounded fetch yasak.
  • EXPLAIN ANALYZE yeni query eklenirken koş. Sequential scan büyük tabloya = iyice bak.

N+1 ve join

  • N+1 probleminin farkında ol:

    for user in users:
      orders = Order.find(user_id=user.id)  # ❌ N+1
    
  • Eager load:

    • SQL: JOIN + tek query
    • ORM: includes, selectinload, populate
  • Join büyük tabloya dikkat — query plan'ı gör. Subquery / CTE bazen daha okunaklı.

Migration

  • Reversible — her up için down yaz.
  • Zero-downtime:
    • Add column → nullable önce, backfill, sonra NOT NULL
    • Rename column → expand (yeni kolon + dual write) + backfill + contract (eski kolon drop)
    • Add index → CONCURRENTLY
  • Büyük tabloya ALTER dikkat — LOCK süresi ölçülür.
  • Tek migration tek iş — 10 DDL + 3 DML tek dosyada olmaz.
  • Staging'de prod-size ile test.

Detaylı: Migration Yazma skill'i.

Connection pool

  • App seviyesinde pool zorunlu — her istekte yeni connection yasak.
  • Max connections DB tarafında (Postgres default 100) aşılmasın. Pool toplamı < max.
  • Pool sınırları:
    • Per-process pool 5-20
    • PgBouncer / pgcat gibi proxy büyük projelerde

Backup

  • Automated daily backup minimum. Point-in-time recovery (PITR) kritik DB için.
  • Test restore — backup var = restore edildi mi? Quarterly test.
  • Backup şifrelemesi at-rest.
  • Retention policy — ne kadar süre saklanır, compliance ne diyor.

Monitoring

  • Query performance:
    • pg_stat_statements — yavaş query tespiti
    • Slow query log (> 500ms)
  • Connection: aktif vs idle connection count
  • Replication lag: read replica'lar geride mi?
  • Disk space: %80 alert
  • Bloat: autovacuum çalışıyor mu, table/index bloat metrikleri

Data types

  • Money: NUMERIC(12, 2)float / double asla (kayan noktalı hata).
  • Boolean: BOOLEANSMALLINT 0/1 kullanma.
  • Enum: CHECK + text veya Postgres ENUM.
  • JSON: JSONB (binary, indexable). JSON kullanma.
  • Array: PostgreSQL array okey küçük veri için. Büyük set = ayrı tablo.
  • UUID: UUIDCHAR(36) kullanma.
  • Text: TEXT (PostgreSQL'de VARCHAR(n) ile performans farkı yok; VARCHAR(n) sadece business limit gerekiyorsa).

Query güvenliği

  • Prepared statements zorunlu.
  • Least privilege: uygulama user'ı sadece gerekli permission'lara sahip:
    • Read-only user: SELECT sadece
    • Write user: SELECT, INSERT, UPDATE, DELETEDROP, TRUNCATE yok
    • Migration user: ayrı, daha geniş yetki
  • Row-level security (RLS) multi-tenant için — güçlü ama karmaşık; doğru uygularsan IDOR'u DB seviyesinde çözer.

ORM vs raw SQL

  • ORM çoğu CRUD için yeter (SQLAlchemy, Prisma, Drizzle).
  • Karmaşık analytical query için raw SQL tercih — ORM abstraction'ı zayıf.
  • Migration: ORM migrate (auto-generate) + manuel review. Critical migration'ı elle yaz.

Veri kalitesi

  • Constraint'leri DB'de:
    • UNIQUE (email, slug)
    • NOT NULL (gerekenlerde)
    • CHECK (age > 0, status IN (...))
    • FOREIGN KEY (ilişkiler)
  • App-level validation DB constraint'in tekrarı değil — her iki katman da katkı sağlar.
  • "Garbage in" önleme: input validation + DB constraint + zaman zaman data audit.

Locking

  • Optimistic locking (version kolonu) çoğu çakışmasız durumda yeter.
  • Pessimistic locking (SELECT FOR UPDATE) gerçekten race varsa.
  • Advisory lock (pg_advisory_lock) uygulama-seviyesinde kritik section için.

Replication & read replicas

  • Read replica analitik / heavy read için.
  • Read-after-write consistency kullanıcı için → primary'e yönlendir (session affinity).
  • Replication lag ölçülür; alerts'i var.

Yasak listesi (özet)

  • SELECT * prod kodda
  • String concat SQL
  • VARCHAR para için (NUMERIC kullan)
  • float/double para için
  • Foreign key'siz ilişki
  • NOT NULL'suz kolon "emin değilim" diye
  • Index'siz FK kolonu (JOIN yavaş)
  • Uzun transaction + dış API call
  • Migration up var, down yok
  • ALTER prod'da concurrently'siz
  • Pool'suz connection management
  • Production DB credential paylaşımı
  • Backup test edilmeden
  • TRUNCATE / DROP app user permission'da
  • Default enum çevirmesi migration yok

Commit

Conventional Commits. Migration için feat(db): ..., data fix için chore(db): backfill .... Destructive migration için kurul tartışma.