Daripada membaca kode lebih banyak, kita perlu memahami konsep yang lebih besar.

111.222.***.***
28

우선 제목이 반말(!)이라 죄송합니다. 블로그 포스트 톤으로 쓰다보니 굳이 높임말로 바꾸기 보다는 그대로 두는게 나은 선택인 듯하여 그대로 두었습니다. 요즘 먹고사니즘으로 바쁘다 보니 잡생각 할 시간도 잘 없어서 뜸했는데 오랜만에 긱뉴스에서 좋은 글을 봐서 생각을 조금 정리해 보았습니다. 😁


오늘 GeekNews에서 「이해 가능한 소프트웨어를 향하여」라는 글을 봤습니다. 원문은 Anna Liberty의 Towards Understandable Software입니다.

사실 GeekNews에서의 요약본도 충분하다고 생각하지만 그래도 원문을 보고 왔습니다. 원문의 출발점은 단순합니다. 프로그래밍은 괴롭고, 코드는 읽기 어렵고, 유지보수하기 어렵고, 어떤 프로젝트든 그것을 제대로 이해하는 사람은 소수라는 것. 그리고 사람들이 LLM에 몰리는 이유도 LLM이 훌륭해서가 아니라 프로그래밍이 그만큼 괴롭기 때문이라는 진단입니다. 라이브러리를 풀로 붙이고, 끝없는 보일러플레이트를 쓰고, 충돌하는 스택을 맞추는 일. 그 고통이 사람들을 LLM으로 밀어냈다는 겁니다.

여기서 원문이 내리는 결론과, 제가 인디개발자로써 이른바 에이전틱 코딩을 꽤 오랫동안 해보면서 생각한 것이 조금 달라서 정리해 봅니다.

LLM은 추상화가 아니라 자동화에 가깝다

원문에서 가장 또렷한 통찰은 LLM을 추상화 계층으로 보지 않는다는 점입니다.

LLM은 사람이 하던 코드 작성을 대신해줍니다. 자연어로 말하면 코드가 나오고, 에러를 붙여 넣으면 수정안이 나오고, 요구사항을 적으면 테스트까지 만들어줍니다. 그래서 높은 수준의 추상화처럼 보입니다. 하지만 엄밀히 말하면 이건 추상화라기보다 자동화에 가깝습니다.

추상화는 낮은 수준의 복잡성을 감추고, 예측 가능한 방식으로 더 높은 수준의 조작을 가능하게 해야 합니다. SQL을 쓰면 파일 오프셋을 직접 계산하지 않아도 되고, React를 쓰면 DOM 조작을 매번 직접 하지 않아도 됩니다. 추상화도 새 복잡성을 만들지만, 적어도 같은 입력과 같은 규칙에 대해 예측 가능한 층위를 제공합니다.

LLM은 다릅니다. LLM은 확률적으로 의도를 해석하고 그럴듯한 결과를 생성합니다. 같은 지시를 해도 세부 구현은 흔들리고, 문맥에 따라 결과가 달라지고, 때로는 없는 사실을 만들어냅니다. 원문의 표현을 빌리면, LLM이 추상화 계층이라면 손실이 매우 심한 계층입니다.

그래서 LLM은 결정론적 추상화 계층이라기보다, 코드 작성이라는 노동을 자동화하는 계층에 가깝습니다. 이 구분은 정확하고, 저도 동의합니다. 자동화는 같은 일을 더 빨리 하게 만들고, 추상화는 그 일을 덜 하게 만듭니다.

여기서 원문과 제 생각이 갈립니다.

Anna Liberty는 LLM을 더 잘 쓰자고 말하지 않습니다. 오히려 LLM을 걷어내자고 말합니다. 원문은 LLM이 환경적으로 파괴적이고, 학습 데이터의 출처가 떳떳하지 않으며, 결과가 일관되지 않고, 사용자에게 의존성을 심는다고 봅니다. 그래서 자동화의 이득을 포기하지 않으면서도 LLM은 버릴 수 있다고 주장합니다. 자동화를 포기하려고 LLM을 버리는 게 아니라, LLM 없이도 자동화와 높은 추상화를 가질 수 있다는 쪽입니다.(많은 오래된 개발자들이나 코더들이 비관적으로 혹은 비판적으로 내어 놓는 목소리와 비슷합니다)

원문이 내놓는 대안은 코드 자체를 걷어내는 방향입니다. 문서를 먼저 쓰고 코드를 거기에 매다는 문해적 프로그래밍(literate programming), 코드 대신 시각적으로 조작하는 GUI 프로그래밍, 그리고 트랜스포머의 생성 능력 없이 문법에서 의미만 파싱하는 결정론적 자연어 처리. 요지는 LLM을 잘 길들이자는 게 아니라, LLM을 대체할 진짜 추상화 계층을 새로 만들자는 것입니다. 원문 제목의 "이해 가능한"은 산출물이 다른 사람에게 읽히기 쉬워야 한다는 뜻에 가깝습니다만

저는 거기까지는 가지 않겠습니다.

LLM을 루프에서 빼는 대신, 루프 안에 두되 길들이는 쪽을 택했습니다. 이유는 단순합니다. 문해적 프로그래밍도, 결정론적 자연어 컴파일러도 아직 대부분의 실무 환경에 없습니다. 원문이 그리는 세계는 매력적이지만 아직 도착하지 않았고, 그 사이에도 코드는 짜여야 합니다. 그래서 저는 지금 손에 있는 도구(!)로 무엇을 할지를 더 본격적으로 묻습니다.

이 차이를 굳이 적어두는 이유는, 원문을 제 결론의 근거처럼 쓰고 싶지 않아서 입니다. 제가 원문에서 빌려온 건 "LLM은 추상화가 아니라 자동화"라는 진단 한 줄이고, 처방은 서로 다릅니다. 원문은 LLM을 대체하자고 하고, 저는 LLM을 좋은 도구로서의 구조를 만들자고 합니다. 그리고 이 글에서 말하는 "이해"는 원문과 달리, 산출물의 성질이 아니라 개발자가 갖춰야 할 능력 쪽을 제 정의 해보는 것입니다.

코드를 모르는 개발자의 시대는 아니다

AI 코딩 도구가 좋아지면서 자주 나오는 말이 있습니다. 이제 개발자는 코드를 몰라도 되는 것 아니냐는 말입니다. 반은 맞고 반은 틀립니다.

코드를 전혀 몰라도 된다는 뜻은 아닙니다. 그런 시대가 오려면 아직 멀었고, 어쩌면 오지 않을 수도 있습니다. 결국 실행되는 건 코드이고, 장애가 나면 코드를 봐야 하고, 보안 문제가 생기면 코드를 봐야 합니다. LLM이 만든 코드도 책임까지 대신 져주지는 않습니다.

다만 모든 개발자가 모든 코드를 직접 읽고, 모든 구현을 손으로 따라가며, 모든 세부를 머릿속에 넣어야 하는 시대는 조금씩 저물고 있습니다. 중심이 바뀌고 있습니다.

Dahulu, memahami kode pada tingkat baris lebih banyak diminta. Anda harus langsung mengikuti fungsi apa yang menerima argumen, objek apa yang dimodifikasi, dan perpustakaan apa yang dipanggil. Kemampuan ini masih diperlukan. Namun, ke depannya yang lebih penting adalah struktur di mana kode itu berada daripada kode itu sendiri.

Apa tanggung jawab modul ini? Dari mana data ini berasal dan kemana mengalirnya? Arti fungsi ini dalam domain apa. Apa yang tersisa jika gagal? Apa yang dijamin oleh pengujian dan apa yang tidak dijamin? Kriteria apa yang akan digunakan untuk mengadopsi atau membuang hasil yang dihasilkan LLM.

Jika Anda tidak memahami hal ini, Anda mungkin dapat membaca kode, tetapi Anda belum memahami perangkat lunak. Sebaliknya, jika Anda memahami struktur ini, Anda dapat memimpin pekerjaan tanpa harus membaca semua kode. Anda dapat mendelegasikan tugas ke LLM, memvalidasi hasilnya, dan membalikkan arah yang salah. Pengembang mungkin akan membaca kode lebih sedikit. Namun, mereka harus memahami hal-hal yang lebih besar.

Jika instruksi jelas, arahnya akan berkumpul

Bukan berarti LLM hanya bisa dianggap sebagai alat yang tidak dapat dipercaya. LLM tidak deterministik, tetapi juga bukan alat yang sepenuhnya acak. Jika instruksi jelas, hasilnya akan cukup berkumpul dalam rentang yang sempit.

Misalnya, jika Anda mengatakannya seperti ini, hasilnya akan berbeda setiap kali. Mungkin juga tidak akan berhasil dengan baik.

Buat fitur autentikasi.

Permintaan ini terlalu luas. Seperti meminta untuk membuat halaman web. Anda dapat menggunakan sesi, JWT, OAuth, mengubah skema database, atau menambahkan perpustakaan apa pun.

Namun, jika Anda mengatakannya seperti ini, hasilnya akan berbeda.

Buat middleware verifikasi JWT access token di FastAPI. Input adalah header Authorization: Bearer, dan kembalikan JSON 401 jika gagal. Gunakan UserRepository.find_by_id() yang ada, jangan ubah skema database. Tambahkan tiga tes: token kedaluwarsa, tanda tangan salah, dan token valid.

Dalam hal ini, LLM tidak akan bekerja seperti kompiler. Namun, hasilnya akan menjadi lebih sempit. Yang penting adalah bukan mengubah bahasa alami menjadi bahasa pemrograman yang sepenuhnya deterministik, tetapi membatasi LLM agar hasilnya tetap berada dalam satu arah meskipun bergoyang. Hal ini dilakukan dengan mendefinisikan kondisi input dengan jelas, membatasi ruang lingkup modifikasi, mendefinisikan kondisi kegagalan, menambahkan tes, dan menjaga loop validasi hasil. Ini adalah pekerjaan yang harus dilakukan pengembang di era LLM.

Jangan biarkan mereka menggunakan boilerplate, buatlah alat pembuatannya

Pengembang yang menggunakan LLM sering kali meminta LLM untuk melakukan tugas berulang. Buat rute API, buat DTO, buat tes, perbarui dokumentasi, buat komponen serupa lainnya.

Awalnya mudah. Namun, Anda akan segera mencapai batasnya. Setiap kali Anda meminta LLM untuk menggunakan boilerplate yang sama, outputnya akan sedikit berbeda. Gaya kode akan berubah, penanganan pengecualian akan berbeda, struktur file akan tidak selaras, biaya peninjauan akan berulang, dan konteks terus terbuang. Menurut saya, ini adalah otomatisasi tingkat rendah.

Arah yang lebih baik adalah daripada meminta LLM untuk terus menggunakan boilerplate, minta mereka untuk membuat skrip yang menghasilkan boilerplate. Jika CRUD diulang, buatlah generator yang sesuai dengan aturan proyek daripada menulis kode CRUD setiap kali. Jika konversi DTO diulang, buatlah generator mapper berdasarkan skema. Jika fixture tes diulang, buatlah perintah scaffold. Jika pembaruan dokumentasi diulang, buatlah skrip yang mengekstrak dan mensinkronkan dokumentasi dari kode atau skema.

Gunakan LLM bukan sebagai pekerja penulisan kode, tetapi sebagai asisten pembuatan alat yang menghilangkan pengulangan. Yang menarik adalah arah ini mirip dengan "jika Anda ingin mengotomatiskan satu tingkat, abstrakkan tingkat itu" dalam teks asli. Hanya saja saya membuat abstraksi itu dengan bantuan LLM, sedangkan teks aslinya tidak menggunakan LLM. Ini adalah fakta penting, meskipun tujuannya sama, tetapi jalannya berbeda. Dalam penggunaan LLM yang non-deterministik, hasil dan proses keduanya penting.

Tugas berulang harus dikeluarkan dari LLM

LLM sangat kuat, tetapi mahal. Biaya di sini bukan hanya uang. Termasuk biaya konteks, biaya verifikasi, biaya reproduksibilitas, dan biaya peninjauan.

Jika Anda meminta LLM untuk melakukan hal yang sama berulang kali, Anda harus menjelaskan setiap kali, hasilnya akan sedikit berbeda setiap kali, Anda harus memverifikasinya setiap kali, dan kesalahan juga akan diulang. Sebaliknya, jika Anda membuat generator sekali dan memverifikasinya, maka generator itu sendiri akan menjadi alat deterministik. Kode dengan struktur yang sama akan dihasilkan dari input yang sama, konvensi tim akan tetap dalam templat, dan Anda tidak perlu mengulangi penjelasan panjang.

Inilah mengapa harness diperlukan. Harness bukan hanya alat untuk membuat LLM terlihat lebih bagus. Ini adalah alat untuk membentuk struktur dari luar agar LLM tidak bergoyang setiap saat. Ini adalah struktur eksternal yang menentukan apa yang akan dibaca, apa yang akan dimodifikasi, tes apa yang akan dijalankan, hasil apa yang akan diterima, dan ke mana harus kembali jika gagal.

Oleh karena itu, harness yang banyak tidak selalu baik. Banyak keterampilan, agen, hook, dan dokumentasi juga tidak selalu baik. Harness yang baik adalah harness yang mengurangi keputusan dan tugas berulang, sedangkan harness yang buruk menambahkan prosedur dan penjelasan baru.

Software 2.0 dari Andrej Karpathy dan vibe coding

Software 2.0 oleh Andrej Karpathy pada tahun 2017 menjelaskan bagaimana jaringan saraf mengubah cara perangkat lunak dibuat. Jika kode yang ditulis manusia adalah Software 1.0, maka bobot jaringan saraf yang dipelajari adalah Software 2.0.

Setelah alat kode LLM muncul, dia menulis istilah "vibe coding" pada tahun 2025. Namun, penting untuk melihat definisi aslinya dengan tepat. Vibe coding Kapasi adalah cara mengabaikan fakta bahwa kode ada dan menyerahkan diri sepenuhnya pada alur. Dia tidak membaca diff lagi, hanya menyalin dan menempel pesan kesalahan, dan menggunakannya untuk proyek akhir pekan yang bisa dibuang. Intinya, vibe coding asli bukan tentang meningkatkan pemahaman, melainkan melepaskan pemahaman. Meskipun sekarang digunakan sebagai meme, jika dibaca sebagai berarti bahwa Anda dapat mengkode tanpa tanggung jawab, itu lebih dekat dengan definisi aslinya.

Yang ingin saya bicarakan bukanlah definisi Kapasi itu sendiri, tetapi apa yang tersisa ketika kita membaliknya. Semakin banyak AI menggunakan kode, semakin peran manusia mendekati selera, penilaian, dan pengawasan daripada mengetik. Ini bukan klaim Kapasi tentang vibe coding, melainkan interpretasi saya sendiri melalui praktik.

Di Mana Pengembang Harus Berada

Pengembang di era LLM mungkin akan mengetik kode lebih sedikit. Namun, pekerjaan tidak berkurang, hanya posisinya yang berubah.

Pengembang harus memahami domain. Mereka perlu mengetahui apa yang sebenarnya ingin dilakukan pengguna, batasan apa yang ada, kondisi apa yang berbahaya, dan pengecualian apa yang penting.

Pengembang harus memahami struktur. Mereka perlu mengetahui di mana batasnya, mengapa modul dipisahkan, bagaimana data mengalir, dan bagian mana yang mudah berubah.

Pengembang harus memahami verifikasi. Mereka perlu mengetahui apa yang dijamin oleh pengujian, apa yang dikatakan log, dan kondisi apa yang ditinggalkan kegagalan.

Dan pengembang harus membedakan antara otomatisasi dan abstraksi. Lebih baik terus menyerahkan tugas berulang ke LLM, atau membuat alat untuk menghilangkan pengulangan?

Peran ini berada di atas level koder. Arsitek mungkin terdengar berlebihan, tetapi lebih dekat dengan operator. Mereka adalah orang yang mengoperasikan ruang kerja tempat LLM, kode, alat, dokumen, dan pengujian bekerja sama.

Pemahaman Tidak Dapat Di-Outsourcing

Ini kata-kata Kapasi. "Anda dapat melakukan outsourcing sebagian pemikiran, tetapi tidak pemahaman."

Anda dapat meminta LLM untuk menulis draft, membuat kode, mengusulkan pengujian, menemukan contoh yang bertentangan, meneliti pustaka yang tidak dikenal, dan melakukan tugas konversi yang membosankan. Ini semua bisa di-outsourcing. Namun, memutuskan apakah jawaban LLM benar adalah sesuatu yang tidak dapat di-outsourcing. Keputusan itu tidak hanya membutuhkan kemampuan membaca satu baris kode, tetapi juga pemahaman tentang domain, struktur, dan batasan.

Nilai pengembang akan ditentukan pada titik ini ke depan (mungkin tidak). Jika ada yang ingin membantah, pendapat mereka adalah yang benar.

Orang yang menulis banyak kode bukan yang paling berharga, tetapi orang yang tahu apa yang harus dibuat dengan kode. Orang yang membuat LLM bekerja keras bukan yang paling berharga, tetapi orang yang merancang struktur agar LLM tidak perlu mengulang. Orang yang membaca kode dengan cepat bukan yang paling berharga, tetapi orang yang memahami sesuatu yang lebih besar daripada kode.

Pengembang di era LLM mungkin tidak perlu memahami kode lebih banyak. Mereka perlu memahami sesuatu yang lebih besar daripada kode.


Referensi

로그인한 회원만 댓글 등록이 가능합니다.

개발한당

KR | ID | EN
  • IDR
  • KOR
8.35 -0.01

2026.07.10 KEB 하나은행 고시회차 1022회

다가오는 한인 행사일정

  • 등록 된 일정이 없어요!