filefunc — satu file, satu konsep

Masalah

AI code agent (seperti Claude Code) menelusuri kode dengan grep untuk menemukan file, lalu membukanya dengan read. Satuan read adalah file.

Lalu apa yang terjadi jika satu file berisi 20 fungsi?

Butuh satu type CrossError, lalu read
→ 19 fungsi yang tidak perlu ikut terbawa
→ Konteks tercemar

Penelitian “Lost in the Middle” (Stanford, 2024) melaporkan bahwa performa LLM turun lebih dari 30% ketika informasi yang relevan terkubur di tengah-tengah konteks. Penelitian “Context Length Alone Hurts LLM Performance” (Amazon, 2025) mengungkap bahwa token yang tidak perlu — bahkan berupa spasi kosong — menurunkan performa sebesar 13,9 hingga 85%.

Riset telah membuktikan bahwa “konteks semakin pendek semakin baik.” Namun belum ada alat yang memecah kode secara struktural agar hanya hal yang diperlukan saja yang masuk ke konteks.

filefunc mengisi celah itu. Ini adalah konvensi struktur kode sekaligus CLI tool untuk pengembangan aplikasi Go — backend service, CLI tool, code generator, SSOT validator.


Prinsip Inti

Satu file, satu konsep. Nama file = nama konsep.

Berlaku untuk func, type, interface, maupun kumpulan const. Semua aturan diturunkan dari satu prinsip ini.

# Tanpa filefunc
read utils.go → 20 func, 19 tidak perlu. Konteks tercemar.

# Dengan filefunc
read check_one_file_one_func.go → 1 func. Tepat apa yang dibutuhkan.

Lebih penting untuk tidak membuka 290 file yang tidak perlu, daripada memilih 5–10 yang relevan.


First-Class Citizen adalah AI Agent

Struktur kode filefunc dirancang bukan untuk manusia, melainkan untuk AI agent.

AI agent menelusuri dengan grep, bukan ls. Tidak peduli ada 500 atau 1000 file, satu perintah rg '//ff:func feature=validate' sudah cukup. Semakin banyak file, semakin kecil ukuran masing-masing, dan semakin sedikit noise yang terbawa saat satu kali read — justru lebih menguntungkan.

Mungkin muncul pertanyaan: “Bukankah file jadi terlalu banyak?” Bagi manusia, memang iya. Tapi ketidaknyamanan manusia diselesaikan di layer view (seperti ekstensi VSCode). Struktur filefunc tidak dikompromikan demi kenyamanan manusia.


Pola Penelusuran yang Berubah

Sebelumnya

Permintaan pengguna
→ Tidak tahu apa yang ada, pakai ls, find
→ Buka file, pahami struktur
→ grep lagi untuk cari file terkait
→ Sudah dibuka, ternyata ada 20 func, kebanyakan tidak perlu
→ Biaya penelusuran > waktu kerja aktual

Dengan filefunc

Permintaan pengguna + codebook tersedia
→ Lihat codebook, langsung susun query grep
→ Read 20–30 file (masing-masing 1 konsep, semua konteks valid)
→ Kerjakan

Read 30 file tidak masalah jika semuanya konteks yang valid. Yang bermasalah adalah read 1 file tapi isi 30 file ikut terbawa.


Codebook

Codebook menempati posisi paling penting dalam desain filefunc. Codebook lebih didahulukan daripada aturan anotasi. Codebook yang dirancang baik menghasilkan query grep yang presisi; grep yang presisi menghasilkan daftar read yang bersih.

# codebook.yaml
required:
  feature: [validate, annotate, chain, parse, codebook, report, cli]
  type: [command, rule, parser, walker, model, formatter, loader, util]

optional:
  pattern: [error-collection, file-visitor, rule-registry]
  level: [error, warning, info]

Key required wajib ada di setiap anotasi //ff:func dan //ff:type. Ini untuk menjamin keandalan grep — tidak ada celah di key required. Key optional hanya dipakai saat relevan.

Codebook adalah peta proyek bagi AI agent. Tanpa codebook, penelusuran dimulai tanpa kosakata. Dengan codebook, query seperti feature=validate, type=rule bisa langsung dilontarkan tanpa perlu eksplorasi.

Menulis nilai yang tidak ada di codebook ke dalam anotasi adalah ERROR. Dengan menormalisasi kosakata lewat codebook, feature yang terlewat, type yang duplikat, dan klasifikasi yang ambigu menjadi terlihat. Kalau celahnya kelihatan, baru bisa dikelola. Codebook sendiri juga divalidasi — minimal satu key di required, tidak boleh ada nilai duplikat, hanya huruf kecil dan tanda hubung yang diizinkan.


Anotasi Metadata

Anotasi ditempatkan di bagian paling atas setiap file. Tujuannya agar metadata bisa dipahami hanya dari beberapa baris atas, tanpa harus read seluruh body.

//ff:func feature=validate type=rule control=sequence
//ff:what F1: validates one func per file
//ff:why Primary citizen is AI agent. 1 file 1 concept prevents context pollution.
//ff:checked llm=gpt-oss:20b hash=a3f8c1d2
func CheckOneFileOneFunc(gf *model.GoFile) []model.Violation {
AnotasiIsiWajib
//ff:funcMetadata func: feature, type, control, dll.Wajib untuk file func
//ff:typeMetadata type: feature, type, dll.Wajib untuk file type
//ff:whatDeskripsi satu baris — fungsi/type ini melakukan apaWajib
//ff:whyMengapa dibuat seperti ini — alasan di balik keputusanOpsional
//ff:checkedTanda tangan verifikasi LLM (dibuat otomatis oleh llmc)Otomatis

Formatnya adalah //ff:key key1=value1 key2=value2. Langsung bisa dicari dengan grep/ripgrep, dan terstruktur dalam key-value sehingga bisa di-parse oleh tool. Polanya sama dengan konvensi //go:generate dan //go:embed di Go.

control — 1 func 1 control

control= wajib ada di setiap file func. Nilainya salah satu dari tiga:

controlArtiBatas depth
sequenceEksekusi sekuensial2
selectionPercabangan (switch)2
iterationPerulangan (loop)dimension + 1

Berdasarkan teorema Böhm-Jacopini (1966): setiap program adalah kombinasi dari tiga struktur kontrol — sequence, selection, iteration. filefunc memberlakukan ini di tingkat fungsi — satu fungsi hanya boleh memiliki satu aliran kontrol.

Fungsi dengan control=iteration juga wajib memiliki dimension=. Ini menyatakan dimensi data yang di-iterasi. dimension=1 berarti list datar (depth ≤ 2), dimension ≥ 2 mengharuskan type bertingkat bernama (struct/interface).

filefunc juga memvalidasi kesesuaian antara control dan kode aktual. Jika control=selection tetapi tidak ada switch, atau control=sequence tetapi ada switch atau loop, hasilnya adalah ERROR.


LLM Search Pipeline

Anotasi berperan seperti search index. Beroperasi tanpa infrastruktur berat seperti vector embedding.

1. Penyempitan struktural (tanpa LLM, pakai grep)
   Susun query grep berdasarkan codebook
   → Ekstrak 20–30 file kandidat

2. Seleksi metadata (tanpa LLM atau LLM sangat kecil)
   Read hanya anotasi bagian atas tiap file
   → Persempit ke 5–10 file berdasarkan name/input/output/what

3. Pekerjaan presisi (LLM besar, konteks minimal)
   Full read hanya 5–10 file
   → Modifikasi/pembuatan kode

Konteks semakin mengecil seiring progres tiap tahap. Saat LLM besar dilibatkan, hanya file yang benar-benar diperlukan yang tersisa.


CLI

validate — Validasi aturan struktur kode

filefunc validate                    # direktori saat ini
filefunc validate /path/to/project   # root proyek eksplisit
filefunc validate --format json

Membutuhkan go.mod dan codebook.yaml di root proyek. Read-only. Exit code 1 jika ada pelanggaran.

chain — Pelacakan relasi pemanggilan

filefunc chain func RunAll              # 1 tingkat (default)
filefunc chain func RunAll --chon 2     # 2 tingkat (termasuk fungsi yang dipanggil bersama)
filefunc chain func RunAll --chon 3     # 3 tingkat (maksimum)
filefunc chain func RunAll --child-depth 3   # hanya pemanggilan ke bawah
filefunc chain func RunAll --parent-depth 3  # hanya pemanggil di atas
filefunc chain feature validate         # seluruh feature

Analisis AST real-time. --chon adalah jarak relasi. Tingkat 1 adalah pemanggil/yang dipanggil langsung, tingkat 2 mencakup fungsi yang dipanggil bersama.

go callgraph yang ada menganalisis semua pemanggilan secara statis dan menghasilkan ribuan node. chain hanya melacak dalam feature yang sama. Feature di codebook adalah zoom level-nya.

llmc — Verifikasi LLM

filefunc llmc                           # direktori saat ini
filefunc llmc --model qwen3:8b
filefunc llmc --threshold 0.9

Memverifikasi apakah //ff:what sesuai dengan func body menggunakan LLM lokal (ollama). Skor 0,0–1,0, threshold default 0,8. Jika lolos, //ff:checked llm=nama-model hash=hash otomatis dicatat. Jika body berubah, hash ikut berubah sehingga verifikasi ulang diperlukan.

Ini adalah solusi untuk masalah inti annotation drift — //ff:what yang berupa teks alami tidak bisa diverifikasi secara mekanis — yang diselesaikan dengan LLM kecil. Pendekatan ini dimungkinkan karena 1 file 1 func menjamin korespondensi 1:1 di tingkat file.


Aturan

Aturan Struktur File

AturanJika dilanggar
Satu file satu func (nama file = nama fungsi)ERROR
Satu file satu type (nama file = nama type)ERROR
Method: 1 file 1 methodERROR
init() tidak boleh berdiri sendiri (harus bersama var atau func)ERROR
_test.go boleh berisi banyak funcPengecualian
const yang secara semantik merupakan satu kelompok boleh dalam satu filePengecualian

Aturan Kualitas Kode

AturanJika dilanggar
Nesting depth: sequence=2, selection=2, iteration=dimension+1ERROR
Maksimum 1000 baris per funcERROR
Rekomendasi: sequence/iteration 100 baris, selection 300 barisWARNING

Nesting depth berbeda tergantung jenis control. sequence dan selection: depth 2. iteration: dimension + 1 — jika dimension=1 (list datar) maka depth 2, jika dimension=2 (struktur bertingkat) maka depth 3. Dikombinasikan dengan pola early return di Go, sebagian besar kode akan masuk dalam batasan ini.

selection (switch) cenderung membuat case yang panjang, sehingga rekomendasi jumlah baris diperlonggar menjadi 300.


.ffignore

Letakkan .ffignore di root proyek untuk mengecualikan path tertentu dari semua perintah filefunc. Sintaksnya sama dengan .gitignore.

vendor/
*.pb.go
*_gen.go
internal/legacy/

Ini untuk mengecualikan kode yang tidak bisa dipaksakan mengikuti aturan filefunc, seperti kode yang digenerate (output protobuf, codegen) atau kode vendor eksternal.


Integrasi dengan whyso

Karena func = file, riwayat perubahan per fungsi jatuh tepat pada tingkat file.

whyso history check_ssac_openapi.go   # riwayat perubahan fungsi CheckSSaCOpenAPI

Jika satu file berisi banyak fungsi, kita harus menggali diff untuk tahu fungsi mana yang berubah. Dengan filefunc, perubahan file = perubahan fungsi. Biaya pelacakan nol.

Deteksi Coupling Implisit

whyso coupling check_ssac_openapi.go

Fungsi yang dimodifikasi bersama dalam permintaan yang sama:
  check_response_fields.go  8 kali
  check_err_status.go       5 kali
  types.go                  4 kali

Jika tidak ada relasi eksplisit namun terus muncul dalam statistik coupling, itu adalah sinyal dependensi tersembunyi: fungsi yang mengimplementasikan aturan bisnis yang sama dari sudut berbeda, format yang diselaraskan secara implisit tanpa interface, atau bug yang selalu muncul bersamaan.


Mengapa Khusus Go

Tanpa Go, strukturisasi filefunc tidak mudah dilakukan. gofmt memaksa format kode, early return adalah konvensi, tidak ada exception, dan package = direktori. Untuk memperluas ke bahasa lain, diperlukan strategi pemaksaan struktur setara gofmt. Ini di luar cakupan filefunc.

Target penerapannya juga jelas: backend service, CLI tool, code generator, SSOT validator. Algoritma library, pemrograman sistem low-level, dan hot path yang kritis terhadap performa bukan termasuk target.


Penutup

Struktur kode di era LLM harus disesuaikan dengan efisiensi penelusuran AI, bukan kemudahan penelusuran manusia. filefunc adalah langkah pertama dari pergeseran itu.

Satu file, satu konsep. Normalisasi kosakata lewat codebook, lampirkan metadata lewat anotasi, temukan file yang tepat dengan satu grep. Tidak ada kode yang tidak perlu ikut terbawa saat satu kali read. Kontaminasi konteks dicegah langsung oleh struktur file itu sendiri.

Kode: github.com/park-jun-woo/filefunc