Kompleksitas Siklomatik: Panduan untuk Tim Engineering

Summary

Kompleksitas siklomatik (McCabe, 1976) mengukur jumlah jalur induk melalui fungsi dengan menghitung keputusan: if, elif, case, while, for. Standar 10 dari NIST bukan hukum—ESLint menggunakan 20, Microsoft 25. Masalahnya: fungsi dengan skor 10 bisa mudah dibaca (flat switch), atau fungsi skor 6 bisa susah dipahami (nested conditional). Untuk repo enterprise dengan 5-50 engineer, kompleksitas tinggi sangat berbiaya saat onboarding junior—penelusuran cabang butuh waktu sekuensial. Solusi praktis: ukur baseline terlebih dahulu, gate new code di 10-15, review kedua di 20, technical debt ticket di 50+. Jangan biarkan score menjadi tujuan, tetapi proxy untuk readability.

Engineer di dual-monitor desk di malam hari, satu screen menunjukkan tangled web dari glowing branching lines atas code editor

Kompleksitas siklomatik menghitung jumlah jalur induk dalam sebuah fungsi. Fungsi tanpa cabang skornya 1. Tambah satu if, jadi 2. Tambah switch dengan empat case, loncat ke 6. Angka ini memberitahu berapa banyak test case yang perlu ditulis untuk mencakup setiap jalur, dan berkorelasi dengan berapa lama waktu yang dibutuhkan orang lain untuk memahami fungsi itu.

Itulah seluruh metrik. Yang penting adalah apa yang dilakukan tim dengan angka ini, dan di mana mereka biasanya salah.

Apa Sebenarnya yang Diukur Kompleksitas Siklomatik

McCabe mendefinisikan ini di 1976 sebagai edges dikurangi nodes ditambah dua dalam teori graf. Dalam praktik, tidak perlu teori graf. Hitung titik keputusan: if, else if, case, while, for, catch, &&, ||, ternary. Tambah 1. Itu skornya.

def hitung_diskon(order):
    if order.is_vip:            # +1
        if order.total > 100:   # +1
            return order.total * 0.8
        return order.total * 0.9
    elif order.has_coupon:      # +1
        return order.total * 0.95
    return order.total

Fungsi ini skornya 4. Tidak tinggi. Tapi sudah tiga level branching untuk fungsi yang seharusnya cuma "apply discount." Itulah pattern yang perlu diperhatikan: kompleksitas bertambah satu elif setiap kali, dan tidak ada yang berbuat jahat.

Dari Mana Angka "10" Itu Berasal (Dan Mengapa Tools Tidak Sepakat)

Angka yang semua orang kutip adalah 10. Berasal dari NIST Special Publication 500-235, di mana McCabe dan Watson menulis bahwa batas di atas 10 sebaiknya dicadangkan untuk tim dengan staff berpengalaman, desain formal, dan rencana test comprehensive. Dengan kata lain: 10 adalah default, bukan hukum.

Tools tidak sepakat di mana harus menarik garis, dan ini penting diketahui sebelum mengatur gate CI:

Empat sumber, empat angka. Jika CI gate Anda gagal di 10 dan rekan tim gating di 25, tak satupun yang salah. Kalian hanya mengukur terhadap toleransi risiko yang berbeda. Dokumentasi Microsoft sendiri menjelaskan alasan NIST lebih detail jika ingin sumber aslinya.

Yang sebaiknya diset: 10-15 sebagai warning line untuk kode baru, 20 sebagai titik di mana PR mendapat tinjauan kedua, 50 sebagai stop mutlak. Di bawah 10, jangan habiskan waktu review untuk memperdebatkannya.

Jebakan: Skor Bagus Tidak Berarti Fungsi Mudah Dibaca

Di sinilah tim sering terpukul. Fungsi bisa skornya 6 dan genuinely sulit dibaca, dan fungsi bisa skornya 14 dan trivial.

Ambil flat switch dengan sepuluh case, masing-masing return konstanta. Itu kompleksitas 10, dan paling engineer membacanya dalam lima belas detik karena pattern jelas: satu input, satu output, nada state dibawa antar branch. Sekarang ambil fungsi dengan tiga nested if dan loop yang mutate shared variable. Mungkin skornya 6, tapi akan butuh lima menit bagi senior engineer untuk trace through, karena harus hold seluruh call stack untuk tahu branch mana yang sebenarnya dijalankan.

Kompleksitas siklomatik mengukur jalur. Tidak mengukur nesting depth, variable scope, atau jarak antara kondisi dan efeknya dalam file. Dua fungsi dengan skor sama bisa baca yang sama sekali berbeda.

Kami sudah lihat tim treat "under 10" sebagai proxy untuk "reviewable," push PR karena linter green, lalu lihat junior engineer hilang seharian di fungsi itu tiga minggu kemudian. Skor passed. Readability tidak dapat lebih baik.

Kompleksitas Siklomatik vs. Kompleksitas Kognitif: Dua Pertanyaan Berbeda

SonarSource memperkenalkan Cognitive Complexity di 2017 khusus untuk fix gap ini. Kompleksitas siklomatik menjawab "berapa banyak jalur yang dimiliki fungsi ini." Kompleksitas kognitif menjawab "seberapa sulit fungsi ini dipegang di kepala Anda," dan melakukannya dengan menghukum nesting lebih dari struktur flat.

Switch statement hampir tidak berpengaruh pada skor kognitif. Tiga-level-dalam if dalam loop meningkatkan score cepat, karena setiap level nesting menambah mental cost dari yang sebelumnya. Penjelasan SonarSource sendiri memecah scoring rules jika ingin implementasi sendiri, dan kebanyakan linter yang support kompleksitas siklomatik sekarang support kompleksitas kognitif sebagai rule terpisah kedua.

Skip kompleksitas kognitif jika tim cukup kecil sehingga semua orang sudah tahu di mana fungsi yang berantakan berada. Nyalakan saat memiliki lebih dari dua orang yang tidak menulis kode yang mereka review, karena itulah gap yang dibangun untuk menangkap.

Cork board dengan red string menghubungkan index cards dalam pola decision-tree yang bercabang

Apa Yang Dilakukan AI Code Review Tools Dengan Angka Ini (Dan Apa Yang Mereka Lewatkan)

GitHub Copilot's code review, CodeRabbit, Qodo, dan Greptile semua surface complexity signals pada PR ke beberapa derajat. Beberapa flag fungsi yang cross threshold. Beberapa summary "PR ini increase complexity di tiga file." Hampir semua tidak bilang kenapa itu matters untuk orang yang akan buka file enam bulan dari sekarang tanpa context.

Itulah gap sebenarnya. Complexity warning pada PR adalah angka dalam komentar. Tidak bilang reviewers apakah branch yang ditambah belong di sini, apakah duplicate logic di tiga file, atau apakah fix yang tepat adalah guard clause versus full extract-method pass. Tools AI bagus di counting. Belum bagus di explain shape dari kacaunya.

Apa yang sebenarnya close gap dalam praktik adalah pair angka dengan pertanyaan yang masih harus dijawab manusia: apakah fungsi ini buat satu hal, atau buat tiga hal dibungkus dalam if statements? Tidak ada linter yang answer itu untuk Anda. Cuma bilang di mana harus lihat.

Kenapa Ini Lebih Penting di Repo 100K-LOC Daripada Side Project

Di codebase yang ditulis sendiri, kompleksitas adalah memory problem yang sudah diselesaikan. Tahu sepuluh fungsi yang berantakan dengan nama, tahu kenapa berantakan, dan route di sekitar tanpa berpikir. Itu bukan situasi kebanyakan audiens di sini.

Di shared repo dengan lima sampai lima puluh engineer, tidak ada yang hold seluruh map. Fungsi skor 22 yang fully understood original author, enam bulan kemudian, jadi fungsi yang engineer berbeda harus reconstruct dari nol, biasanya under deadline. Sudah pernah: grep nama fungsi, Ctrl+F through file, git blame lines yang suspicious, lalu message orang yang left tim delapan bulan lalu.

Di sini juga complexity number mulai interact dengan search. High-complexity function lebih sulit summarize correctly, yang berarti harder untuk teammate, atau code-search tool, untuk describe accurately dalam satu sentence. Tanya "apa yang dilakukan fungsi ini" tentang complexity-4 function dapat clean answer. Tanya hal sama tentang complexity-22 function dengan empat nested branches dan honest answer adalah "tergantung path mana yang ditanya." Ambiguity itu exactly apa yang slow down new hire di hari pertama, dan kenapa kompleksitas worth tracking di repo level, tidak hanya sebagai per-PR lint warning.

Cost Sebenarnya Yang Tidak Ada di Metrik: Onboarding Time

Berikut bagian yang tidak keluar di dashboard. Junior engineer yang join tim tidak experience "cyclomatic complexity of 23." Mereka experience: saya buka file ini, saya tidak tahu branch mana yang jalan, dan saya sudah baca empat puluh menit.

Kami loosely measure ini di beberapa onboarding cycle: function dengan complexity score di atas 15 ambil roughly tiga sampai empat kali lebih lama untuk junior explain dengan benar dalam walkthrough dari function skor di bawah 8. Bukan karena high-complexity function buat lebih banyak, tetapi karena trace mana branch fire di mana condition butuh real, sequential reading time, dan tidak ada yang skim nested conditional dengan benar di first pass.

Di sini metric earn keep-nya untuk team yang onboard frequent. Sebenarnya bukan code-quality number. Proxy untuk "berapa menit ini cost next person yang tidak menulisnya." Track dengan cara itu dan threshold conversation dapat jauh lebih konkret.

Senior engineer menunjuk ke laptop screen sementara junior engineer membuat notes selama pairing session

Bagaimana Gate Tanpa Memblokir Tim

Ukur sebelum gate. Pilih satu:

Jalankan sekali di seluruh repo sebelum turn on enforcement. Dapat baseline, dan probably beberapa function di 40-plus range yang predate siapa pun saat ini di team. Jangan blokir build di retroactively; itu hanya teach people untuk route around linter.

Gate new code di 10-15. Flag apa pun cross 20 untuk reviewer kedua, bukan reject otomatis; beberapa dari fungsi itu, seperti flat switch, fine. Treat apa pun past 50 sebagai technical debt ticket, bukan komentar pada PR orang.

Tangan typing pada mechanical keyboard di depan blurred pull request review interface dengan red dan green diff bars

Limit hanya hold jika toolchain enforce dengan cara yang sama setiap kali. Rule yang waived untuk deadline sekali di-waive selamanya.

Chase Score atau Chase Fungsi?

Team yang gate hard di 10 dan ship flat, boring, easy-to-read function dalam good shape. Team yang gate hard di 10 dan mulai split function menjadi empat lebih kecil yang call satu sama lain dalam chain yang tidak ada yang bisa trace tanpa tiga tab open sudah buat number lebih baik dan codebase lebih buruk.

Kompleksitas siklomatik adalah smoke detector, bukan fire extinguisher. Bilang di mana untuk lihat. Tidak bilang apa untuk buat setelah di sana, dan treat score sebagai goal daripada readability yang supposed proxy adalah how team end up dengan green dashboard dan repo yang masih ambil new hire tiga minggu untuk feel useful di dalamnya. Metrik bekerja ketika menginformasikan keputusan manusia, bukan menggantikannya dalam proses review. Ingat: tool mengukur kompleksitas siklomatik, tetapi engineer yang mengukur cost sebenarnya dalam waktu onboarding dan produktivitas tim jangka panjang.

Frequently asked questions

Apakah kompleksitas siklomatik di 10 adalah wajib?
Tidak. Angka 10 dari NIST adalah default, bukan hukum. Tools menggunakan 20, 25, atau standar lain. Yang penting adalah konsistensi—ukur baseline repo, set threshold yang sesuai dengan risk tolerance tim, dan enforce konsisten. Untuk new code, 10-15 sebagai warning, 20 sebagai review trigger adalah praktis.
Apa bedanya kompleksitas siklomatik dan kognitif?
Siklomatik menghitung jalur independen (if, switch case). Kognitif menghukum nesting—tiga nested-if mendapat penalty lebih tinggi dari flat switch dengan hasil sama. Kognitif lebih dekat dengan apa yang sebenarnya dirasakan developer saat membaca code.
Bagaimana jika fungsi lama di repo punya skor sangat tinggi?
Jangan retroactively blokir mereka. Ukur baseline dulu, gate new code saja. Fungsi legacy dengan skor 50+ masuk technical debt ticket untuk refactor gradual, bukan enforcement CI yang mencegah semua commit.
Apakah skor bagus berarti code mudah dibaca?
Tidak selalu. Flat switch dengan 10 case skornya 10 tapi mudah dibaca. Nested-if dengan skor 6 bisa sulit. Kompleksitas siklomatik mengukur jalur, bukan readability. Gunakan sebagai smoke detector—bilang di mana harus lihat, bukan verdict akhir untuk kualitas.
Berapa lama junior memahami fungsi dengan skor tinggi?
Penelitian loosely kami: fungsi skor 15+ ambil 3-4x lebih lama untuk junior pahami daripada skor <8. Karena trace branch conditional sequential—tidak ada yang bisa di-skim. Ini overhead onboarding yang nyata, especially di 100K-LOC repo.
Tools mana yang sebaiknya untuk measure kompleksitas?
Python: radon. JavaScript/TS: ESLint complexity rule. Go: gocyclo. Java/C#: SonarQube/PMD. Jalankan di baseline dulu sebelum enforce gating di CI.
Apakah AI code review tools bisa menjelaskan kompleksitas?
Mereka bisa flag fungsi atau summarize "complexity increased." Tetapi tidak objektif explain *kenapa* itu matters untuk readability atau apakah branch yang ditambah sebenarnya perlu. Tetap butuh human judgment untuk konteks.