- 1. Apa Itu Clean Code?
- 2. Mengapa Clean Code Penting dalam Rekayasa Perangkat Lunak?
- 3. Prinsip Utama Clean Code
- 4. Contoh Praktis: Refactoring Kode Buruk ke Clean Code
- 5. Tools dan Linter untuk Menjaga Kualitas Kode
- 6. Code Review: Kebiasaan Tim yang Sehat
- 7. Kesalahan Umum yang Wajib Dihindari
- 8. Hasil Output Dan File HTML
- 9. Kesimpulan dan Langkah Selanjutnya
1. Apa Itu Clean Code?
Clean Code adalah filosofi penulisan kode program yang menekankan keterbacaan, kemudahan pemeliharaan, dan kesederhanaan. Istilah ini dipopulerkan oleh Robert C. Martin (Uncle Bob) melalui bukunya Clean Code: A Handbook of Agile Software Craftsmanship (2008), yang hingga kini menjadi referensi wajib bagi para software engineer di seluruh dunia.
Secara sederhana, clean code adalah kode yang mudah dibaca oleh manusia, bukan hanya oleh mesin. Kode yang bersih adalah kode yang:
- Bisa dipahami oleh developer lain dalam hitungan menit
- Mudah diubah atau diperluas tanpa menimbulkan bug baru
- Minim duplikasi logika
- Memiliki nama variabel, fungsi, dan kelas yang deskriptif
2. Mengapa Clean Code Penting dalam Rekayasa Perangkat Lunak?
Dalam dunia rekayasa perangkat lunak profesional, kode dibaca jauh lebih sering daripada ditulis. Sebuah studi oleh Stripe (2018) memperkirakan bahwa developer menghabiskan sekitar 42% waktu mereka hanya untuk membaca dan memahami kode yang sudah ada — bukan menulis kode baru.
Dampak Kode yang Buruk (Technical Debt)
Kode yang tidak bersih menciptakan technical debt — utang teknis yang semakin membesar seiring waktu. Konsekuensinya meliputi:
- Waktu onboarding lebih lama bagi developer baru
- Bug yang sulit dilacak karena logika yang tersembunyi
- Fitur baru membutuhkan waktu lebih lama untuk dikembangkan
- Risiko refactoring besar-besaran yang mahal di masa depan
ROI (Return on Investment) Clean Code
| Aspek | Kode Buruk | Clean Code |
|---|---|---|
| Waktu debugging | Tinggi (60-80% waktu) | Rendah (20-30% waktu) |
| Onboarding developer baru | 2–4 minggu | 3–5 hari |
| Penambahan fitur baru | Lambat, berisiko | Cepat, aman |
| Pemeliharaan jangka panjang | Semakin mahal | Stabil atau menurun |
3. Prinsip Utama Clean Code
3.1 Penamaan yang Bermakna
Nama adalah salah satu hal terpenting dalam kode. Nama yang baik menjelaskan apa yang dilakukan sesuatu, mengapa ia ada, dan bagaimana ia digunakan — tanpa perlu membaca implementasinya.
Aturan Emas Penamaan:
- Gunakan nama yang mengungkapkan maksud (intention-revealing)
- Hindari singkatan yang ambigu (
d,tmp,val) - Gunakan kata benda untuk variabel/kelas, kata kerja untuk fungsi
- Konsisten dalam konvensi (
camelCase,snake_case,PascalCase)
python
# ❌ BURUK — Nama tidak bermakna
d = 86400
def calc(x, y):
return x * y * d
# ✅ BAIK — Nama yang deskriptif
SECONDS_PER_DAY = 86400
def calculate_total_seconds(days: int, multiplier: float) -> float:
return days * multiplier * SECONDS_PER_DAY
3.2 Fungsi yang Kecil dan Terfokus
Sebuah fungsi yang baik hanya melakukan satu hal, dan melakukannya dengan baik. Ini dikenal sebagai Single Responsibility Principle pada level fungsi.
Panduan ukuran fungsi:
- Idealnya 5–20 baris kode
- Maksimal 1 level abstraksi per fungsi
- Jika perlu menambahkan komentar “// bagian ini untuk…”, itu tanda fungsi perlu dipecah
python
# ❌ BURUK — Satu fungsi melakukan terlalu banyak hal
def process_order(order_data):
# Validasi
if not order_data.get('user_id'):
raise ValueError("User ID tidak ada")
if not order_data.get('items'):
raise ValueError("Pesanan kosong")
# Hitung total
total = 0
for item in order_data['items']:
total += item['price'] * item['quantity']
if item.get('discount'):
total -= item['price'] * item['discount']
# Kirim email
send_email(order_data['user_email'], f"Total pesanan Anda: {total}")
# Simpan ke database
db.save({'order': order_data, 'total': total})
return total
# ✅ BAIK — Setiap fungsi memiliki satu tanggung jawab
def validate_order(order_data: dict) -> None:
if not order_data.get('user_id'):
raise ValueError("User ID tidak ada")
if not order_data.get('items'):
raise ValueError("Pesanan kosong")
def calculate_order_total(items: list) -> float:
return sum(
item['price'] * item['quantity'] * (1 - item.get('discount', 0))
for item in items
)
def notify_customer(email: str, total: float) -> None:
send_email(email, f"Total pesanan Anda: {total}")
def process_order(order_data: dict) -> float:
validate_order(order_data)
total = calculate_order_total(order_data['items'])
notify_customer(order_data['user_email'], total)
db.save({'order': order_data, 'total': total})
return total
3.3 Komentar yang Tepat
Komentar yang baik menjelaskan mengapa, bukan apa. Kode yang baik seharusnya sudah cukup jelas menjelaskan apa yang dilakukannya.
python
# ❌ BURUK — Komentar hanya mengulang kode
# Menambah 1 ke counter
counter = counter + 1
# ❌ BURUK — Komentar yang sudah kadaluarsa / menyesatkan
# Fungsi ini menghitung pajak 10%
def calculate_tax(amount):
return amount * 0.12 # Ternyata sudah berubah jadi 12%!
# ✅ BAIK — Komentar menjelaskan alasan keputusan bisnis
# PPN diatur dalam PMK No. 63/2021 yang berlaku mulai April 2022
VAT_RATE = 0.11
# ✅ BAIK — Komentar untuk algoritma kompleks
def find_optimal_route(nodes, edges):
# Menggunakan algoritma Dijkstra karena graph bersifat weighted
# dan tidak memiliki bobot negatif (jarak selalu positif)
...
3.4 Penanganan Error yang Baik
Error handling yang baik membuat sistem lebih tangguh dan mudah di-debug.
python
# ❌ BURUK — Menelan exception tanpa informasi
def get_user(user_id):
try:
return db.find(user_id)
except:
return None # Error hilang tanpa jejak!
# ✅ BAIK — Error handling yang informatif dan spesifik
class UserNotFoundError(Exception):
"""Raised ketika user tidak ditemukan di database."""
pass
def get_user(user_id: int) -> dict:
if not isinstance(user_id, int) or user_id <= 0:
raise ValueError(f"user_id harus berupa integer positif, diterima: {user_id}")
try:
user = db.find(user_id)
if user is None:
raise UserNotFoundError(f"User dengan ID {user_id} tidak ditemukan")
return user
except DatabaseConnectionError as e:
logger.error(f"Gagal terhubung ke database saat mencari user {user_id}: {e}")
raise
3.5 Prinsip DRY
Don’t Repeat Yourself — setiap logika harus memiliki satu representasi tunggal dalam sistem. Duplikasi kode adalah sumber utama bug yang konsisten.
javascript
// ❌ BURUK — Logika validasi email diulang di berbagai tempat
function registerUser(email, password) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) throw new Error("Email tidak valid");
// ...
}
function updateUserEmail(userId, newEmail) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(newEmail)) throw new Error("Email tidak valid");
// ...
}
// ✅ BAIK — Logika validasi disentralisasi
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function isValidEmail(email) {
return EMAIL_REGEX.test(email);
}
function validateEmail(email) {
if (!isValidEmail(email)) {
throw new Error(`Format email tidak valid: ${email}`);
}
}
function registerUser(email, password) {
validateEmail(email);
// ...
}
function updateUserEmail(userId, newEmail) {
validateEmail(newEmail);
// ...
}
3.6 Prinsip SOLID
SOLID adalah akronim dari 5 prinsip desain object-oriented yang fundamental:
| Huruf | Prinsip | Singkat |
|---|---|---|
| S | Single Responsibility | Satu kelas = satu tanggung jawab |
| O | Open/Closed | Terbuka untuk ekstensi, tertutup untuk modifikasi |
| L | Liskov Substitution | Subclass harus bisa menggantikan parent class |
| I | Interface Segregation | Interface kecil lebih baik dari satu interface besar |
| D | Dependency Inversion | Bergantung pada abstraksi, bukan implementasi |
Contoh Open/Closed Principle:
python
# ❌ BURUK — Harus memodifikasi kelas setiap kali ada metode pembayaran baru
class PaymentProcessor:
def process(self, payment_type, amount):
if payment_type == "credit_card":
# proses kartu kredit
pass
elif payment_type == "bank_transfer":
# proses transfer bank
pass
elif payment_type == "gopay": # Setiap tambah metode, kelas ini diubah!
pass
# ✅ BAIK — Gunakan abstraksi, tambah metode tanpa ubah kelas inti
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def process(self, amount: float) -> bool:
pass
class CreditCardPayment(PaymentMethod):
def process(self, amount: float) -> bool:
print(f"Memproses kartu kredit sebesar Rp{amount:,.0f}")
return True
class BankTransferPayment(PaymentMethod):
def process(self, amount: float) -> bool:
print(f"Memproses transfer bank sebesar Rp{amount:,.0f}")
return True
class GopayPayment(PaymentMethod): # Tambah tanpa ubah kode lama!
def process(self, amount: float) -> bool:
print(f"Memproses GoPay sebesar Rp{amount:,.0f}")
return True
class PaymentProcessor:
def __init__(self, method: PaymentMethod):
self.method = method
def execute(self, amount: float) -> bool:
return self.method.process(amount)
4. Contoh Praktis: Refactoring Kode Buruk ke Clean Code
4.1 Studi Kasus 1 – Sistem Autentikasi Pengguna
Skenario: Anda mewarisi kode autentikasi yang berantakan dari developer sebelumnya. Tugas Anda adalah melakukan refactoring tanpa mengubah fungsionalitas.
python
# ========================================
# SEBELUM REFACTORING (Kode Buruk)
# ========================================
import hashlib, datetime
from db import connection
def login(u, p):
c = connection.cursor()
h = hashlib.md5(p.encode()).hexdigest() # MD5 tidak aman!
c.execute(f"SELECT * FROM users WHERE username='{u}' AND password='{h}'") # SQL Injection!
r = c.fetchone()
if r != None:
s = str(datetime.datetime.now().timestamp())
c.execute(f"UPDATE users SET last_login='{s}', token='{u+h}' WHERE id={r[0]}")
connection.commit()
return {'success': True, 'token': u+h, 'user': r}
return False
# ========================================
# SESUDAH REFACTORING (Clean Code)
# ========================================
import bcrypt
import secrets
from datetime import datetime, timezone
from typing import Optional
from dataclasses import dataclass
@dataclass
class AuthResult:
success: bool
token: Optional[str] = None
user_id: Optional[int] = None
error_message: Optional[str] = None
class AuthenticationError(Exception):
"""Raised ketika proses autentikasi gagal."""
pass
class UserRepository:
def __init__(self, db_connection):
self._conn = db_connection
def find_by_username(self, username: str) -> Optional[dict]:
cursor = self._conn.cursor()
# Parameterized query — aman dari SQL Injection
cursor.execute(
"SELECT id, username, password_hash FROM users WHERE username = ?",
(username,)
)
row = cursor.fetchone()
if row is None:
return None
return {"id": row[0], "username": row[1], "password_hash": row[2]}
def update_login_metadata(self, user_id: int, token: str) -> None:
cursor = self._conn.cursor()
cursor.execute(
"UPDATE users SET last_login = ?, session_token = ? WHERE id = ?",
(datetime.now(timezone.utc).isoformat(), token, user_id)
)
self._conn.commit()
class PasswordService:
@staticmethod
def verify(plain_password: str, hashed_password: str) -> bool:
return bcrypt.checkpw(
plain_password.encode('utf-8'),
hashed_password.encode('utf-8')
)
@staticmethod
def generate_session_token() -> str:
return secrets.token_urlsafe(32) # Kriptografis aman
class AuthenticationService:
def __init__(self, user_repo: UserRepository, password_service: PasswordService):
self._user_repo = user_repo
self._password_service = password_service
def login(self, username: str, plain_password: str) -> AuthResult:
user = self._user_repo.find_by_username(username)
if user is None:
# Tetap verifikasi password palsu untuk mencegah timing attack
bcrypt.checkpw(b"dummy", b"$2b$12$dummy_hash_to_prevent_timing_attack")
return AuthResult(success=False, error_message="Username atau password salah")
if not self._password_service.verify(plain_password, user['password_hash']):
return AuthResult(success=False, error_message="Username atau password salah")
token = self._password_service.generate_session_token()
self._user_repo.update_login_metadata(user['id'], token)
return AuthResult(success=True, token=token, user_id=user['id'])
Apa yang diperbaiki:
- MD5 diganti dengan bcrypt (aman untuk password)
- SQL Injection dihilangkan dengan parameterized query
- Nama variabel yang deskriptif (
u,p,h,r→ nama yang bermakna) - Separation of concerns: Repository, PasswordService, AuthService terpisah
- Return type yang konsisten (
AuthResultdataclass) - Timing attack dicegah
4.2 Studi Kasus 2 – Kalkulasi Diskon E-Commerce
Skenario: Sebuah platform e-commerce memiliki aturan diskon yang kompleks: diskon member, diskon kuantitas, dan diskon spesial hari tertentu.
javascript
// ========================================
// SEBELUM REFACTORING
// ========================================
function getPrice(p, qty, isMember, day) {
let x = p * qty;
if (isMember == true) {
x = x - (x * 0.1);
}
if (qty > 10) {
x = x - (x * 0.05);
}
if (qty > 50) {
x = x - (x * 0.1);
}
if (day == 'friday' || day == 'saturday') {
x = x - (x * 0.15);
}
if (x < 0) x = 0;
return x;
}
// ========================================
// SESUDAH REFACTORING
// ========================================
const DISCOUNT_RULES = {
MEMBER: 0.10, // 10% untuk member
BULK_MEDIUM: 0.05, // 5% untuk pembelian 11–50 item
BULK_LARGE: 0.10, // 10% untuk pembelian >50 item
WEEKEND: 0.15, // 15% di hari Jumat dan Sabtu
};
const WEEKEND_DAYS = new Set(['friday', 'saturday']);
/**
* Menghitung semua diskon yang berlaku dan mengembalikan daftar diskon aktif.
* @param {Object} params - Parameter kalkulasi
* @param {boolean} params.isMember - Apakah pembeli adalah member
* @param {number} params.quantity - Jumlah item yang dibeli
* @param {string} params.dayOfWeek - Hari pembelian (lowercase, e.g. 'friday')
* @returns {Array<{name: string, rate: number}>} Daftar diskon yang berlaku
*/
function getApplicableDiscounts({ isMember, quantity, dayOfWeek }) {
const discounts = [];
if (isMember) {
discounts.push({ name: 'Diskon Member', rate: DISCOUNT_RULES.MEMBER });
}
if (quantity > 50) {
discounts.push({ name: 'Diskon Pembelian Massal (>50)', rate: DISCOUNT_RULES.BULK_LARGE });
} else if (quantity > 10) {
discounts.push({ name: 'Diskon Pembelian Grosir (>10)', rate: DISCOUNT_RULES.BULK_MEDIUM });
}
if (WEEKEND_DAYS.has(dayOfWeek)) {
discounts.push({ name: 'Diskon Akhir Pekan', rate: DISCOUNT_RULES.WEEKEND });
}
return discounts;
}
/**
* Menghitung harga akhir setelah semua diskon diterapkan.
* Diskon diterapkan secara berurutan (bukan dijumlahkan).
*/
function calculateFinalPrice(unitPrice, quantity, discounts) {
const subtotal = unitPrice * quantity;
const finalPrice = discounts.reduce(
(price, discount) => price * (1 - discount.rate),
subtotal
);
return Math.max(0, finalPrice); // Harga tidak pernah negatif
}
/**
* Entry point utama — menggabungkan kalkulasi diskon dan harga.
*/
function getPriceWithDiscounts({ unitPrice, quantity, isMember, dayOfWeek }) {
const applicableDiscounts = getApplicableDiscounts({ isMember, quantity, dayOfWeek });
const finalPrice = calculateFinalPrice(unitPrice, quantity, applicableDiscounts);
return {
subtotal: unitPrice * quantity,
discountsApplied: applicableDiscounts,
finalPrice,
totalSavings: (unitPrice * quantity) - finalPrice,
};
}
// Contoh penggunaan:
const result = getPriceWithDiscounts({
unitPrice: 50000,
quantity: 15,
isMember: true,
dayOfWeek: 'friday',
});
console.log(result);
// Output:
// {
// subtotal: 750000,
// discountsApplied: [
// { name: 'Diskon Member', rate: 0.1 },
// { name: 'Diskon Pembelian Grosir (>10)', rate: 0.05 },
// { name: 'Diskon Akhir Pekan', rate: 0.15 }
// ],
// finalPrice: 542812.5,
// totalSavings: 207187.5
// }
4.3 Studi Kasus 3 – Pengelolaan Data dari API
Skenario: Aplikasi perlu mengambil data dari API eksternal dan menampilkannya ke pengguna dengan penanganan error yang baik.
typescript
// ========================================
// SEBELUM REFACTORING
// ========================================
async function getData(id) {
try {
const res = await fetch(`https://api.example.com/products/${id}`);
const data = await res.json();
return data;
} catch(e) {
console.log(e);
return null;
}
}
// ========================================
// SESUDAH REFACTORING (TypeScript)
// ========================================
// Types yang jelas
interface Product {
id: number;
name: string;
price: number;
stock: number;
category: string;
}
type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: string; statusCode?: number };
// Custom error classes
class ApiError extends Error {
constructor(
message: string,
public readonly statusCode: number,
public readonly endpoint: string
) {
super(message);
this.name = 'ApiError';
}
}
class NetworkError extends Error {
constructor(message: string, public readonly cause: unknown) {
super(message);
this.name = 'NetworkError';
}
}
// Konfigurasi terpusat
const API_CONFIG = {
BASE_URL: 'https://api.example.com',
TIMEOUT_MS: 5000,
HEADERS: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
} as const;
// Fungsi helper untuk request dengan timeout
async function fetchWithTimeout(url: string, timeoutMs: number): Promise<Response> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
headers: API_CONFIG.HEADERS,
signal: controller.signal,
});
return response;
} finally {
clearTimeout(timeoutId);
}
}
// Service layer yang bersih
async function getProductById(productId: number): Promise<ApiResult<Product>> {
if (!Number.isInteger(productId) || productId <= 0) {
return { success: false, error: 'Product ID harus berupa integer positif' };
}
const endpoint = `${API_CONFIG.BASE_URL}/products/${productId}`;
try {
const response = await fetchWithTimeout(endpoint, API_CONFIG.TIMEOUT_MS);
if (response.status === 404) {
return { success: false, error: `Produk dengan ID ${productId} tidak ditemukan`, statusCode: 404 };
}
if (!response.ok) {
throw new ApiError(
`Server merespons dengan status ${response.status}`,
response.status,
endpoint
);
}
const product: Product = await response.json();
return { success: true, data: product };
} catch (error) {
if (error instanceof ApiError) {
console.error(`[API Error] ${error.endpoint}: ${error.message}`);
return { success: false, error: error.message, statusCode: error.statusCode };
}
if (error instanceof DOMException && error.name === 'AbortError') {
return { success: false, error: 'Request timeout — server tidak merespons dalam 5 detik' };
}
console.error('[Network Error]', error);
return { success: false, error: 'Gagal terhubung ke server. Periksa koneksi internet Anda.' };
}
}
// Contoh penggunaan di komponen React
async function displayProduct(productId: number) {
const result = await getProductById(productId);
if (!result.success) {
console.error(`Gagal memuat produk: ${result.error}`);
// Tampilkan pesan error ke user
return;
}
const { data: product } = result;
console.log(`Produk: ${product.name} — Rp${product.price.toLocaleString('id-ID')}`);
}
5. Tools dan Linter untuk Menjaga Kualitas Kode
Menulis clean code secara manual bisa melelahkan. Gunakan tools berikut untuk mengotomatiskan pemeriksaan kualitas:
Untuk JavaScript/TypeScript
| Tool | Fungsi | Perintah Instalasi |
|---|---|---|
| ESLint | Analisis kode statis, deteksi bug | npm install -D eslint |
| Prettier | Format kode otomatis | npm install -D prettier |
| TypeScript | Type checking | npm install -D typescript |
| Husky | Git hooks (lint sebelum commit) | npm install -D husky |
Untuk Python
| Tool | Fungsi | Perintah Instalasi |
|---|---|---|
| Ruff | Linter ultra-cepat (ganti Flake8) | pip install ruff |
| Black | Format kode otomatis | pip install black |
| mypy | Static type checking | pip install mypy |
| pytest | Unit testing | pip install pytest |
Konfigurasi ESLint untuk Proyek Modern (.eslintrc.json):
json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"no-var": "error",
"prefer-const": "error",
"no-unused-vars": "error",
"eqeqeq": ["error", "always"],
"no-console": "warn",
"max-lines-per-function": ["warn", { "max": 30 }]
}
}
6. Code Review: Kebiasaan Tim yang Sehat
Code review adalah garis pertahanan terakhir sebelum kode masuk ke production. Berikut panduan code review yang konstruktif:
Sebagai Reviewer:
- Tanyakan, jangan perintah: “Apakah ini bisa dibuat lebih sederhana dengan
map()?” bukan “Ganti ini pakaimap()“ - Fokus pada kode, bukan orangnya: “Fungsi ini melakukan terlalu banyak hal” bukan “Kamu menulis fungsi yang buruk”
- Berikan apresiasi: Jika ada kode yang elegan, katakan! Positif reinforcement membangun budaya tim yang baik
- Prioritaskan feedback: Bedakan antara blocker (wajib diperbaiki) dan nitpick (opsional)
Checklist Review Cepat:
- Apakah nama variabel/fungsi/kelas sudah deskriptif?
- Apakah ada duplikasi logika yang bisa di-refactor?
- Apakah edge case dan error sudah ditangani?
- Apakah ada unit test untuk logika baru?
- Apakah perubahan ini merusak kontrak/API yang ada?
7. Kesalahan Umum yang Wajib Dihindari
1. Magic Numbers
python
# ❌ BURUK
if user.age >= 17:
allow_access()
# ✅ BAIK
MINIMUM_AGE_FOR_ACCESS = 17
if user.age >= MINIMUM_AGE_FOR_ACCESS:
allow_access()
2. Negasi Berlebihan
python
# ❌ BURUK — Sulit dibaca
if not (not user.is_active and not user.is_banned):
show_dashboard()
# ✅ BAIK
if user.is_active and not user.is_banned:
show_dashboard()
3. Callback Hell (JavaScript)
javascript
// ❌ BURUK
getUser(userId, function(user) {
getOrders(user.id, function(orders) {
getProducts(orders[0].id, function(products) {
calculateTotal(products, function(total) {
sendInvoice(user.email, total, function(result) {
// Indentasi ke mana-mana...
});
});
});
});
});
// ✅ BAIK — Gunakan async/await
async function processUserInvoice(userId) {
const user = await getUser(userId);
const orders = await getOrders(user.id);
const products = await getProducts(orders[0].id);
const total = await calculateTotal(products);
return await sendInvoice(user.email, total);
}
4. God Class (Kelas yang Tahu Segalanya)
python
# ❌ BURUK — Satu kelas yang menangani semua hal
class Application:
def login_user(self): ...
def register_user(self): ...
def send_email(self): ...
def generate_report(self): ...
def process_payment(self): ...
def calculate_tax(self): ...
# ...dan 50 method lainnya
# ✅ BAIK — Pisahkan berdasarkan domain
class AuthService: ...
class EmailService: ...
class ReportService: ...
class PaymentService: ...
class TaxCalculator: ...
8. Hasil Output Dan File HTML

9. Kesimpulan dan Langkah Selanjutnya
Clean code bukan tentang menulis kode yang “sempurna” — melainkan tentang menghormati developer lain (termasuk diri Anda sendiri di masa depan) yang akan membaca dan memodifikasi kode tersebut. Kode adalah komunikasi.
Ringkasan Poin Kunci:
- Penamaan bermakna adalah investasi terbaik dalam keterbacaan kode
- Fungsi kecil dan terfokus membuat kode mudah diuji dan dimodifikasi
- DRY principle mencegah bug yang konsisten dari duplikasi
- SOLID principles membuat sistem fleksibel dan scalable
- Tools otomatisasi (linter, formatter) mengurangi beban mental developer
- Code review adalah proses pembelajaran kolektif, bukan inspeksi