Database: Fondasi Sistem yang Sering Diabaikan
Saya sudah "menyelamatkan" beberapa sistem dari database yang poorly designed. Dari query yang lambat sampai deadlocks yang terjadi di production — semua masalah ini biasanya berakar dari design choices yang dibuat terlalu cepat tanpa pertimbangan jangka panjang.
Sebagai engineer, saya percaya database design itu seni sekaligus sains. Dan berikut adalah pattern yang sudah saya buktikan di production.
1. Naming Convention yang Konsisten
Kedengarannya sepele, tapi ini menyelamatkan banyak waktu. Di setiap proyek, saya terapkan:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
full_name VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE user_roles (
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
PRIMARY KEY (user_id, role_id)
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_created_at ON users(created_at);
2. Indexing Strategy yang Efektif
Dari pengalaman saya, 90% performance issue database itu bisa diselesaikan dengan indexing yang tepat. Tapi bukan berarti harus banyak index — terlalu banyak index juga berbahaya (write performance menurun).
Rule of thumb yang saya gunakan:
- Always index foreign keys — ini harusnya default, tapi banyak developer lupa
- Index columns yang sering di-WHERE dan ORDER BY — check dengan EXPLAIN ANALYZE
- Composite index untuk query yang sering — urutan kolom di composite index itu penting!
- Partial index untuk data yang tidak seimbang — misal index hanya untuk status='active'
3. Query Optimization dengan EXPLAIN
Saya selalu bilang ke tim: jangan pernah menulis query tanpa cek execution plan.
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT o.id, o.total, u.email
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'pending'
AND o.created_at > NOW() - INTERVAL '7 days'
ORDER BY o.created_at DESC
LIMIT 20;
4. Soft Delete Pattern
Di production, hard delete itu berbahaya. Saya selalu pakai soft delete:
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMPTZ;
CREATE VIEW active_users AS
SELECT * FROM users WHERE deleted_at IS NULL;
CREATE OR REPLACE FUNCTION cleanup_deleted()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM users
WHERE deleted_at IS NOT NULL
AND deleted_at < NOW() - INTERVAL '90 days';
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Kesimpulan
Database design yang baik itu investasi. Ambil waktu untuk merencanakan, dan sistem kamu akan terima kasih di masa depan.
Database Migration Strategy
Satu hal yang sering dilupakan adalah migration strategy. Di production, kamu tidak bisa asal ALTER TABLE. Saya selalu menggunakan migration tools seperti golang-migrate atau Atlas untuk mengelola perubahan schema secara terstruktur.
Rules yang saya terapkan: setiap migration harus reversible (bisa rollback), backward compatible (tidak break application yang sedang jalan), dan tested di staging sebelum production. Saya pernah melihat production down selama 30 menit karena migration yang tidak di-test dengan benar — pengalaman itu mengajarkan saya untuk selalu treat migration dengan serius.